📄 snprintf.cpp
字号:
/* * 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 * * markus@oberhumer.com, August 2002 * large modifications for use in UPX * **************************************************************/#if 1#include "conf.h"#else#include <ctype.h>#include <stdarg.h>#include <stdlib.h>#include <string.h>#undef NDEBUG#include <assert.h>#endif#undef LLONG#undef ULLONG#if 1 && defined(acc_int64l_t)# define LLONG acc_int64l_t# define ULLONG acc_uint64l_t#else# define LLONG long int# define ULLONG unsigned long int#endif#undef NO_FLOAT#undef LDOUBLE#if 1# define NO_FLOAT 1# if (ACC_CC_SUNPROC)# else# define float error no_float# define double error no_float# endif#elif 0 || defined(HAVE_LONG_DOUBLE)# define LDOUBLE long double#else# define LDOUBLE double#endif/* * 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 char_to_int(p) ((p)- '0')#undef MAX#define MAX(p,q) (((p) >= (q)) ? (p) : (q))/************************************************************************* //**************************************************************************/static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, int c){ if (*currlen < maxlen) buffer[*currlen] = (char) c; *currlen += 1;}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 if (value == NULL) value = "<NULL>"; for (strln = 0; value[strln]; ) /* strlen */ ++strln; 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; }}/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */static void fmtint(char *buffer, size_t *currlen, size_t maxlen, LLONG value, unsigned base, int min, int max, int flags){ int signvalue = 0; ULLONG uvalue; char convert[64+1]; int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ const char *digits; if (min < 0) min = 0; if (max < 0) max = 0; uvalue = value; if (!(flags & DP_F_UNSIGNED)) { if( value < 0 ) { signvalue = '-'; uvalue = -value; } else { if (flags & DP_F_PLUS) /* Do a sign (+/i) */ signvalue = '+'; else if (flags & DP_F_SPACE) signvalue = ' '; } } digits = (flags & DP_F_UP) ? "0123456789ABCDEF" : "0123456789abcdef"; do { convert[place] = digits[(unsigned) (uvalue % base)]; uvalue /= base; } while (++place < (int)sizeof(convert) - 1 && uvalue); convert[place] = 0; zpadlen = max - place; spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); if (zpadlen < 0) zpadlen = 0; if (spadlen < 0) spadlen = 0; if (flags & DP_F_ZERO) { zpadlen = MAX(zpadlen, spadlen); spadlen = 0; } if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */#ifdef DEBUG_SNPRINTF printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", zpadlen, spadlen, min, max, place);#endif /* Spaces */ while (spadlen > 0) { dopr_outch (buffer, currlen, maxlen, ' '); --spadlen; } /* Sign */ if (signvalue) dopr_outch (buffer, currlen, maxlen, signvalue); /* Zeros */ while (zpadlen > 0) { dopr_outch (buffer, currlen, maxlen, '0'); --zpadlen; } /* Digits */ while (place > 0) dopr_outch (buffer, currlen, maxlen, convert[--place]); /* Left Justified spaces */ while (spadlen < 0) { dopr_outch (buffer, currlen, maxlen, ' '); ++spadlen; }}/*************************************************************************// floating format support**************************************************************************/#if !defined(NO_FLOAT)static LDOUBLE abs_val(LDOUBLE value){ LDOUBLE result = value; if (value < 0) result = -value; return result;}static LDOUBLE POW10(int exp){ LDOUBLE result = 1; while (exp) { result *= 10; exp--; } return result;}static LLONG ROUND(LDOUBLE value){ LLONG intpart; intpart = (LLONG)value; value = value - intpart; if (value >= 0.5) intpart++; return intpart;}/* a replacement for modf that doesn't need the math library. Should be portable, but slow */static double my_modf(double x0, double *iptr){ int i; long l; double x = x0; double f = 1.0; for (i = 0; i < 100; i++) { l = (long)x; if (l <= (x+1) && l >= (x-1)) break; x *= 0.1; f *= 10.0; } if (i == 100) { /* yikes! the number is beyond what we can handle. What do we do? */ *iptr = 0.0; return 0; } if (i != 0) { double i2, ret; ret = my_modf(x0-l*f, &i2); *iptr = l*f + i2; return ret; } *iptr = l; return x - *iptr;}static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags){ /* avoid warnings with 'gcc -Wshadow' */#undef index#define index iindex int signvalue = 0; double ufvalue; char iconvert[311+1]; char fconvert[311+1]; int iplace = 0; int fplace = 0; int padlen = 0; /* amount to pad */ int zpadlen = 0; const char *digits; int index; double intpart; double fracpart; double temp; /* * AIX manpage says the default is 0, but Solaris says the default * is 6, and sprintf on AIX defaults to 6 */ if (min < 0) min = 0; if (max < 0) max = 6; ufvalue = abs_val (fvalue); if (fvalue < 0) { signvalue = '-'; } else { if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ signvalue = '+'; } else { if (flags & DP_F_SPACE) signvalue = ' '; } } digits = "0123456789ABCDEF";#if 0 digits = (flags & DP_F_UP) ? "0123456789ABCDEF" : "0123456789abcdef";#endif#if 0 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */#endif /* * Sorry, we only support 16 digits past the decimal because of our * conversion method */ if (max > 16) max = 16; /* We "cheat" by converting the fractional part to integer by * multiplying by a factor of 10 */ temp = ufvalue; my_modf(temp, &intpart); fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); if (fracpart >= POW10(max)) { intpart++; fracpart -= POW10(max); } /* Convert integer part */ do { temp = intpart; my_modf(intpart*0.1, &intpart); temp = temp*0.1; index = (int) ((temp - intpart + 0.05) * 10.0); /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ /* printf ("%llf, %f, %x\n", temp, intpart, index); */ iconvert[iplace] = digits[index]; } while (++iplace < (int)sizeof(iconvert) - 1 && intpart); iconvert[iplace] = 0; /* Convert fractional part */ if (fracpart) { do { temp = fracpart; my_modf(fracpart*0.1, &fracpart); temp = temp*0.1; index = (int) ((temp - fracpart + 0.05) * 10.0); /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */ /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */ fconvert[fplace] = digits[index]; } while (++fplace < (int)sizeof(fconvert) - 1 && fracpart); } fconvert[fplace] = 0; /* -1 for decimal point, another -1 if we are printing a sign */ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); zpadlen = max - fplace; if (zpadlen < 0) zpadlen = 0; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justifty */ if ((flags & DP_F_ZERO) && (padlen > 0)) { if (signvalue) { dopr_outch (buffer, currlen, maxlen, signvalue); --padlen; signvalue = 0; } while (padlen > 0) { dopr_outch (buffer, currlen, maxlen, '0'); --padlen; } } while (padlen > 0) { dopr_outch (buffer, currlen, maxlen, ' '); --padlen; } if (signvalue) dopr_outch (buffer, currlen, maxlen, signvalue); while (iplace > 0) dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);#ifdef DEBUG_SNPRINTF printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);#endif /* * Decimal point. This should probably use locale to find the correct * char to print out. */ if (max > 0) { dopr_outch (buffer, currlen, maxlen, '.'); while (fplace > 0) dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); } while (zpadlen > 0) { dopr_outch (buffer, currlen, maxlen, '0'); --zpadlen; } while (padlen < 0) { dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; }#undef index}#endif /* !defined(NO_FLOAT) *//*************************************************************************// dopr()**************************************************************************/static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -