?? snprintf.cc
字號:
/* * Copyright Patrick Powell 1995 * This code is based on code written by Patrick Powell (papowell@astart.com) * It may be used for any purpose as long as this notice remains intact * on all source code distributions *//************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 * A bombproof version of doprnt (dopr) included. * Sigh. This sort of thing is always nasty do deal with. Note that * the version here does not include floating point... * * snprintf() is used instead of sprintf() as it does limit checks * for string length. This covers a nasty loophole. * * The other functions are there to prevent NULL pointers from * causing nast effects. * * More Recently: * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 * This was ugly. It is still ugly. I opted out of floating point * numbers, but the formatter understands just about everything * from the normal C string format, at least as far as I can tell from * the Solaris 2.5 printf(3S) man page. * * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 * Ok, added some minimal floating point support, which means this * probably requires libm on most operating systems. Don't yet * support the exponent (e,E) and sigfig (g,G). Also, fmtint() * was pretty badly broken, it just wasn't being exercised in ways * which showed it, so that's been fixed. Also, formated the code * to mutt conventions, and removed dead code left over from the * original. Also, there is now a builtin-test, just compile with: * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm * and run snprintf for results. * * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i * The PGP code was using unsigned hexadecimal formats. * Unfortunately, unsigned formats simply didn't work. * * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 * The original code assumed that both snprintf() and vsnprintf() were * missing. Some systems only have snprintf() but not vsnprintf(), so * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. * * Andrew Tridgell (tridge@samba.org) Oct 1998 * fixed handling of %.0f * added test for HAVE_LONG_DOUBLE * * tridge@samba.org, idra@samba.org, April 2001 * got rid of fcvt code (twas buggy and made testing harder) * added C99 semantics * * HT authors * * ht_snprintf/ht_vsnprintf return number of characters actually * written instead of the number of characters that would * have been written. * * added '%y' to allow object output using Object's toString() method. * * added '%q[dioux]' for formatting qwords. * * added '%b' for formatting in binary notation. **************************************************************/#include <cctype>#include <cstring>#include <cstdlib>#include <cstdio>#include <cstdarg>#include <sys/types.h>#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "io/types.h"#include "data.h"#ifdef HAVE_CONFIG_H#include "config.h"#endif#ifndef MIN#define MIN(a, b) ((a) < (b) ? (a) : (b))#endif#ifndef MAX#define MAX(a, b) ((a) > (b) ? (a) : (b))#endif#ifdef HAVE_LONG_DOUBLE#define LDOUBLE long double#else#define LDOUBLE double#endif#ifdef HAVE_LONG_LONG#define LLONG long long#else#define LLONG long#endifstatic size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args);static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, const char *value, int flags, int min, int max);static void fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base, int min, int max, int flags);static void fmtqword(char *buffer, size_t *currlen, size_t maxlen, sint64 value, int base, int min, int max, int flags);static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags);static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);/* * dopr(): poor man's version of doprintf *//* format read states */#define DP_S_DEFAULT 0#define DP_S_FLAGS 1#define DP_S_MIN 2#define DP_S_DOT 3#define DP_S_MAX 4#define DP_S_MOD 5#define DP_S_CONV 6#define DP_S_DONE 7/* format flags - Bits */#define DP_F_MINUS (1 << 0)#define DP_F_PLUS (1 << 1)#define DP_F_SPACE (1 << 2)#define DP_F_NUM (1 << 3)#define DP_F_ZERO (1 << 4)#define DP_F_UP (1 << 5)#define DP_F_UNSIGNED (1 << 6)/* Conversion Flags */#define DP_C_SHORT 1#define DP_C_LONG 2#define DP_C_LDOUBLE 3#define DP_C_LLONG 4#define DP_C_QWORD 5#define char_to_int(p) ((p)- '0')#ifndef MAX#define MAX(p,q) (((p) >= (q)) ? (p) : (q))#endifstatic size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args){ char ch; LLONG value; LDOUBLE fvalue; const char *strvalue; int min; int max; int state; int flags; int cflags; size_t currlen; state = DP_S_DEFAULT; currlen = flags = cflags = min = 0; max = -1; ch = *format++; while (state != DP_S_DONE) { if (ch == '\0') state = DP_S_DONE; switch(state) { case DP_S_DEFAULT: if (ch == '%') { state = DP_S_FLAGS; } else { dopr_outch(buffer, &currlen, maxlen, ch); } ch = *format++; break; case DP_S_FLAGS: switch (ch) { case '-': flags |= DP_F_MINUS; ch = *format++; break; case '+': flags |= DP_F_PLUS; ch = *format++; break; case ' ': flags |= DP_F_SPACE; ch = *format++; break; case '#': flags |= DP_F_NUM; ch = *format++; break; case '0': flags |= DP_F_ZERO; ch = *format++; break; default: state = DP_S_MIN; break; } break; case DP_S_MIN: if (isdigit((unsigned char)ch)) { min = 10*min + char_to_int (ch); ch = *format++; } else if (ch == '*') { min = va_arg (args, int); ch = *format++; state = DP_S_DOT; } else { state = DP_S_DOT; } break; case DP_S_DOT: if (ch == '.') { state = DP_S_MAX; ch = *format++; } else { state = DP_S_MOD; } break; case DP_S_MAX: if (isdigit((unsigned char)ch)) { if (max < 0) max = 0; max = 10*max + char_to_int (ch); ch = *format++; } else if (ch == '*') { max = va_arg (args, int); ch = *format++; state = DP_S_MOD; } else { state = DP_S_MOD; } break; case DP_S_MOD: switch (ch) { case 'h': cflags = DP_C_SHORT; ch = *format++; break; case 'l': cflags = DP_C_LONG; ch = *format++; if (ch == 'l') { /* It's a long long */ cflags = DP_C_LLONG; ch = *format++; } break; case 'L': cflags = DP_C_LDOUBLE; ch = *format++; break; case 'q': cflags = DP_C_QWORD; ch = *format++; break; default: break; } state = DP_S_CONV; break; case DP_S_CONV: switch (ch) { case 'b': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) { value = va_arg (args, unsigned int); } else if (cflags == DP_C_LONG) { value = (long)va_arg (args, unsigned long int); } else if (cflags == DP_C_LLONG) { value = (LLONG)va_arg (args, unsigned LLONG); } else if (cflags == DP_C_QWORD) { sint64 q = va_arg (args, sint64); fmtqword(buffer, &currlen, maxlen, q, 2, min, max, flags); break; } else { value = (long)va_arg (args, unsigned int); } fmtint (buffer, &currlen, maxlen, value, 2, min, max, flags); break; case 'd': case 'i': if (cflags == DP_C_SHORT) { value = va_arg (args, int); } else if (cflags == DP_C_LONG) { value = va_arg (args, long int); } else if (cflags == DP_C_LLONG) { value = va_arg (args, LLONG); } else if (cflags == DP_C_QWORD) { sint64 q = va_arg (args, sint64); fmtqword(buffer, &currlen, maxlen, q, 10, min, max, flags); break; } else { value = va_arg (args, int); } fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'o': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) { value = va_arg (args, unsigned int); } else if (cflags == DP_C_LONG) { value = (long)va_arg (args, unsigned long int); } else if (cflags == DP_C_LLONG) { value = (long)va_arg (args, unsigned LLONG); } else if (cflags == DP_C_QWORD) { sint64 q = va_arg (args, sint64); fmtqword(buffer, &currlen, maxlen, q, 8, min, max, flags); break; } else { value = (long)va_arg (args, unsigned int); } fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); break; case 'u': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) { value = va_arg (args, unsigned int); } else if (cflags == DP_C_LONG) { value = (long)va_arg (args, unsigned long int); } else if (cflags == DP_C_LLONG) { value = (LLONG)va_arg (args, unsigned LLONG); } else if (cflags == DP_C_QWORD) { sint64 q = va_arg (args, sint64); fmtqword(buffer, &currlen, maxlen, q, 10, min, max, flags); break; } else { value = (long)va_arg (args, unsigned int); } fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'X': flags |= DP_F_UP; case 'x': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) { value = va_arg (args, unsigned int); } else if (cflags == DP_C_LONG) { value = (long)va_arg (args, unsigned long int); } else if (cflags == DP_C_LLONG) { value = (LLONG)va_arg (args, unsigned LLONG); } else if (cflags == DP_C_QWORD) { sint64 q = va_arg (args, sint64); fmtqword(buffer, &currlen, maxlen, q, 16, min, max, flags); break; } else { value = (long)va_arg (args, unsigned int); } fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); break; case 'f': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); /* um, floating point? */ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'E': flags |= DP_F_UP; case 'e': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); break; case 'G': flags |= DP_F_UP; case 'g': if (cflags == DP_C_LDOUBLE) fvalue = va_arg (args, LDOUBLE); else fvalue = va_arg (args, double); break; case 'c': dopr_outch(buffer, &currlen, maxlen, va_arg (args, int)); break; case 's': strvalue = va_arg (args, char *); if (!strvalue) strvalue = "(null)"; if (max == -1) { max = strlen(strvalue); } if (min > 0 && max >= 0 && min > max) max = min; fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max); break; case 'p': strvalue = va_arg (args, char *); fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); break; case 'n': if (cflags == DP_C_SHORT) { short int *num; num = va_arg (args, short int *); *num = currlen; } else if (cflags == DP_C_LONG) { long int *num; num = va_arg (args, long int *); *num = (long int)currlen; } else if (cflags == DP_C_LLONG) { LLONG *num; num = va_arg (args, LLONG *); *num = (LLONG)currlen; } else { int *num; num = va_arg (args, int *); *num = currlen; } break; case '%': dopr_outch(buffer, &currlen, maxlen, ch); break; case 'w': /* not supported yet, treat as next char */ ch = *format++; break; case 'y': { /* object */ Object *obj = va_arg (args, Object *); if (obj) { currlen += obj->toString(buffer+currlen, maxlen - currlen); } else { strvalue = "(null)"; if (max == -1) { max = strlen(strvalue); } if (min > 0 && max >= 0 && min > max) max = min; fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); } break; } default: /* Unknown, skip */ break; } ch = *format++; state = DP_S_DEFAULT; flags = cflags = min = 0; max = -1; break; case DP_S_DONE: break; default: /* hmm? */ break; /* some picky compilers need this */ } } if (maxlen != 0) { if (currlen < maxlen - 1) buffer[currlen] = '\0'; else if (maxlen > 0) buffer[maxlen - 1] = '\0'; } return currlen;}static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, const char *value, int flags, int min, int max){ int padlen, strln; /* amount to pad */ int cnt = 0;#ifdef DEBUG_SNPRINTF printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);#endif for (strln = 0; value[strln]; ++strln); /* strlen */ padlen = min - strln; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justify */ while ((padlen > 0) && (cnt < max)) { dopr_outch(buffer, currlen, maxlen, ' '); --padlen; ++cnt; } while (*value && (cnt < max)) { dopr_outch(buffer, currlen, maxlen, *value++); ++cnt; } while ((padlen < 0) && (cnt < max)) { dopr_outch(buffer, currlen, maxlen, ' '); ++padlen; ++cnt;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -