snprintf.c

説明を見る。
00001 /*
00002  * NOTE: If you change this file, please merge it into rsync, samba, etc.
00003  */
00004 
00005 /*
00006  * Copyright Patrick Powell 1995
00007  * This code is based on code written by Patrick Powell (papowell@astart.com)
00008  * It may be used for any purpose as long as this notice remains intact
00009  * on all source code distributions
00010  */
00011 
00012 /**************************************************************
00013  * Original:
00014  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
00015  * A bombproof version of doprnt (dopr) included.
00016  * Sigh.  This sort of thing is always nasty do deal with.  Note that
00017  * the version here does not include floating point...
00018  *
00019  * snprintf() is used instead of sprintf() as it does limit checks
00020  * for string length.  This covers a nasty loophole.
00021  *
00022  * The other functions are there to prevent NULL pointers from
00023  * causing nast effects.
00024  *
00025  * More Recently:
00026  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
00027  *  This was ugly.  It is still ugly.  I opted out of floating point
00028  *  numbers, but the formatter understands just about everything
00029  *  from the normal C string format, at least as far as I can tell from
00030  *  the Solaris 2.5 printf(3S) man page.
00031  *
00032  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
00033  *    Ok, added some minimal floating point support, which means this
00034  *    probably requires libm on most operating systems.  Don't yet
00035  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
00036  *    was pretty badly broken, it just wasn't being exercised in ways
00037  *    which showed it, so that's been fixed.  Also, formated the code
00038  *    to mutt conventions, and removed dead code left over from the
00039  *    original.  Also, there is now a builtin-test, just compile with:
00040  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
00041  *    and run snprintf for results.
00042  * 
00043  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
00044  *    The PGP code was using unsigned hexadecimal formats. 
00045  *    Unfortunately, unsigned formats simply didn't work.
00046  *
00047  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
00048  *    The original code assumed that both snprintf() and vsnprintf() were
00049  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
00050  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
00051  *
00052  *  Andrew Tridgell (tridge@samba.org) Oct 1998
00053  *    fixed handling of %.0f
00054  *    added test for HAVE_LONG_DOUBLE
00055  *
00056  * tridge@samba.org, idra@samba.org, April 2001
00057  *    got rid of fcvt code (twas buggy and made testing harder)
00058  *    added C99 semantics
00059  *
00060  * date: 2002/12/19 19:56:31;  author: herb;  state: Exp;  lines: +2 -0
00061  * actually print args for %g and %e
00062  * 
00063  * date: 2002/06/03 13:37:52;  author: jmcd;  state: Exp;  lines: +8 -0
00064  * Since includes.h isn't included here, VA_COPY has to be defined here.  I don't
00065  * see any include file that is guaranteed to be here, so I'm defining it
00066  * locally.  Fixes AIX and Solaris builds.
00067  * 
00068  * date: 2002/06/03 03:07:24;  author: tridge;  state: Exp;  lines: +5 -13
00069  * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
00070  * functions
00071  * 
00072  * date: 2002/05/17 14:51:22;  author: jmcd;  state: Exp;  lines: +21 -4
00073  * Fix usage of va_list passed as an arg.  Use __va_copy before using it
00074  * when it exists.
00075  * 
00076  * date: 2002/04/16 22:38:04;  author: idra;  state: Exp;  lines: +20 -14
00077  * Fix incorrect zpadlen handling in fmtfp.
00078  * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
00079  * few mods to make it easier to compile the tests.
00080  * addedd the "Ollie" test to the floating point ones.
00081  *
00082  * Martin Pool (mbp@samba.org) April 2003
00083  *    Remove NO_CONFIG_H so that the test case can be built within a source
00084  *    tree with less trouble.
00085  *    Remove unnecessary SAFE_FREE() definition.
00086  *
00087  * Martin Pool (mbp@samba.org) May 2003
00088  *    Put in a prototype for dummy_snprintf() to quiet compiler warnings.
00089  *
00090  *    Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
00091  *    if the C library has some snprintf functions already.
00092  **************************************************************/
00093 
00094 #ifndef NO_CONFIG_H
00095 #include "config.h"
00096 #else
00097 #define NULL 0
00098 #endif 
00099 
00100 #ifdef TEST_SNPRINTF /* need math library headers for testing */
00101 
00102 /* In test mode, we pretend that this system doesn't have any snprintf
00103  * functions, regardless of what config.h says. */
00104 #  undef HAVE_SNPRINTF
00105 #  undef HAVE_VSNPRINTF
00106 #  undef HAVE_C99_VSNPRINTF
00107 #  undef HAVE_ASPRINTF
00108 #  undef HAVE_VASPRINTF
00109 #  include <math.h>
00110 #endif /* TEST_SNPRINTF */
00111 
00112 #ifdef HAVE_STRING_H
00113 #include <string.h>
00114 #endif
00115 
00116 #ifdef HAVE_STRINGS_H
00117 #include <strings.h>
00118 #endif
00119 #ifdef HAVE_CTYPE_H
00120 #include <ctype.h>
00121 #endif
00122 #include <sys/types.h>
00123 #include <stdarg.h>
00124 #ifdef HAVE_STDLIB_H
00125 #include <stdlib.h>
00126 #endif
00127 
00128 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
00129 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
00130 #include <stdio.h>
00131  /* make the compiler happy with an empty file */
00132  void dummy_snprintf(void);
00133  void dummy_snprintf(void) {} 
00134 #endif /* HAVE_SNPRINTF, etc */
00135 
00136 #ifdef HAVE_LONG_DOUBLE
00137 #define LDOUBLE long double
00138 #else
00139 #define LDOUBLE double
00140 #endif
00141 
00142 #if SIZEOF_LONG_LONG
00143 #define LLONG long long
00144 #else
00145 #define LLONG long
00146 #endif
00147 
00148 #ifndef VA_COPY
00149 #if defined HAVE_VA_COPY || defined va_copy
00150 #define VA_COPY(dest, src) va_copy(dest, src)
00151 #else
00152 #ifdef HAVE___VA_COPY
00153 #define VA_COPY(dest, src) __va_copy(dest, src)
00154 #else
00155 #define VA_COPY(dest, src) (dest) = (src)
00156 #endif
00157 #endif
00158 
00159 /*
00160  * dopr(): poor man's version of doprintf
00161  */
00162 
00163 /* format read states */
00164 #define DP_S_DEFAULT 0
00165 #define DP_S_FLAGS   1
00166 #define DP_S_MIN     2
00167 #define DP_S_DOT     3
00168 #define DP_S_MAX     4
00169 #define DP_S_MOD     5
00170 #define DP_S_CONV    6
00171 #define DP_S_DONE    7
00172 
00173 /* format flags - Bits */
00174 #define DP_F_MINUS      (1 << 0)
00175 #define DP_F_PLUS       (1 << 1)
00176 #define DP_F_SPACE      (1 << 2)
00177 #define DP_F_NUM        (1 << 3)
00178 #define DP_F_ZERO       (1 << 4)
00179 #define DP_F_UP         (1 << 5)
00180 #define DP_F_UNSIGNED   (1 << 6)
00181 
00182 /* Conversion Flags */
00183 #define DP_C_SHORT   1
00184 #define DP_C_LONG    2
00185 #define DP_C_LDOUBLE 3
00186 #define DP_C_LLONG   4
00187 
00188 #define char_to_int(p) ((p)- '0')
00189 #ifndef MAX
00190 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
00191 #endif
00192 
00193 /* yes this really must be a ||. Don't muck with this (tridge) */
00194 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
00195 
00196 static size_t dopr(char *buffer, size_t maxlen, const char *format, 
00197                    va_list args_in);
00198 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
00199                     char *value, int flags, int min, int max);
00200 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
00201                     long value, int base, int min, int max, int flags);
00202 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
00203                    LDOUBLE fvalue, int min, int max, int flags);
00204 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
00205 
00206 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
00207 {
00208         char ch;
00209         LLONG value;
00210         LDOUBLE fvalue;
00211         char *strvalue;
00212         int min;
00213         int max;
00214         int state;
00215         int flags;
00216         int cflags;
00217         size_t currlen;
00218         va_list args;
00219 
00220         VA_COPY(args, args_in);
00221         
00222         state = DP_S_DEFAULT;
00223         currlen = flags = cflags = min = 0;
00224         max = -1;
00225         ch = *format++;
00226         
00227         while (state != DP_S_DONE) {
00228                 if (ch == '\0') 
00229                         state = DP_S_DONE;
00230 
00231                 switch(state) {
00232                 case DP_S_DEFAULT:
00233                         if (ch == '%') 
00234                                 state = DP_S_FLAGS;
00235                         else 
00236                                 dopr_outch (buffer, &currlen, maxlen, ch);
00237                         ch = *format++;
00238                         break;
00239                 case DP_S_FLAGS:
00240                         switch (ch) {
00241                         case '-':
00242                                 flags |= DP_F_MINUS;
00243                                 ch = *format++;
00244                                 break;
00245                         case '+':
00246                                 flags |= DP_F_PLUS;
00247                                 ch = *format++;
00248                                 break;
00249                         case ' ':
00250                                 flags |= DP_F_SPACE;
00251                                 ch = *format++;
00252                                 break;
00253                         case '#':
00254                                 flags |= DP_F_NUM;
00255                                 ch = *format++;
00256                                 break;
00257                         case '0':
00258                                 flags |= DP_F_ZERO;
00259                                 ch = *format++;
00260                                 break;
00261                         default:
00262                                 state = DP_S_MIN;
00263                                 break;
00264                         }
00265                         break;
00266                 case DP_S_MIN:
00267                         if (isdigit((unsigned char)ch)) {
00268                                 min = 10*min + char_to_int (ch);
00269                                 ch = *format++;
00270                         } else if (ch == '*') {
00271                                 min = va_arg (args, int);
00272                                 ch = *format++;
00273                                 state = DP_S_DOT;
00274                         } else {
00275                                 state = DP_S_DOT;
00276                         }
00277                         break;
00278                 case DP_S_DOT:
00279                         if (ch == '.') {
00280                                 state = DP_S_MAX;
00281                                 ch = *format++;
00282                         } else { 
00283                                 state = DP_S_MOD;
00284                         }
00285                         break;
00286                 case DP_S_MAX:
00287                         if (isdigit((unsigned char)ch)) {
00288                                 if (max < 0)
00289                                         max = 0;
00290                                 max = 10*max + char_to_int (ch);
00291                                 ch = *format++;
00292                         } else if (ch == '*') {
00293                                 max = va_arg (args, int);
00294                                 ch = *format++;
00295                                 state = DP_S_MOD;
00296                         } else {
00297                                 state = DP_S_MOD;
00298                         }
00299                         break;
00300                 case DP_S_MOD:
00301                         switch (ch) {
00302                         case 'h':
00303                                 cflags = DP_C_SHORT;
00304                                 ch = *format++;
00305                                 break;
00306                         case 'l':
00307                                 cflags = DP_C_LONG;
00308                                 ch = *format++;
00309                                 if (ch == 'l') {        /* It's a long long */
00310                                         cflags = DP_C_LLONG;
00311                                         ch = *format++;
00312                                 }
00313                                 break;
00314                         case 'L':
00315                                 cflags = DP_C_LDOUBLE;
00316                                 ch = *format++;
00317                                 break;
00318                         default:
00319                                 break;
00320                         }
00321                         state = DP_S_CONV;
00322                         break;
00323                 case DP_S_CONV:
00324                         switch (ch) {
00325                         case 'd':
00326                         case 'i':
00327                                 if (cflags == DP_C_SHORT) 
00328                                         value = va_arg (args, int);
00329                                 else if (cflags == DP_C_LONG)
00330                                         value = va_arg (args, long int);
00331                                 else if (cflags == DP_C_LLONG)
00332                                         value = va_arg (args, LLONG);
00333                                 else
00334                                         value = va_arg (args, int);
00335                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
00336                                 break;
00337                         case 'o':
00338                                 flags |= DP_F_UNSIGNED;
00339                                 if (cflags == DP_C_SHORT)
00340                                         value = va_arg (args, unsigned int);
00341                                 else if (cflags == DP_C_LONG)
00342                                         value = (long)va_arg (args, unsigned long int);
00343                                 else if (cflags == DP_C_LLONG)
00344                                         value = (long)va_arg (args, unsigned LLONG);
00345                                 else
00346                                         value = (long)va_arg (args, unsigned int);
00347                                 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
00348                                 break;
00349                         case 'u':
00350                                 flags |= DP_F_UNSIGNED;
00351                                 if (cflags == DP_C_SHORT)
00352                                         value = va_arg (args, unsigned int);
00353                                 else if (cflags == DP_C_LONG)
00354                                         value = (long)va_arg (args, unsigned long int);
00355                                 else if (cflags == DP_C_LLONG)
00356                                         value = (LLONG)va_arg (args, unsigned LLONG);
00357                                 else
00358                                         value = (long)va_arg (args, unsigned int);
00359                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
00360                                 break;
00361                         case 'X':
00362                                 flags |= DP_F_UP;
00363                         case 'x':
00364                                 flags |= DP_F_UNSIGNED;
00365                                 if (cflags == DP_C_SHORT)
00366                                         value = va_arg (args, unsigned int);
00367                                 else if (cflags == DP_C_LONG)
00368                                         value = (long)va_arg (args, unsigned long int);
00369                                 else if (cflags == DP_C_LLONG)
00370                                         value = (LLONG)va_arg (args, unsigned LLONG);
00371                                 else
00372                                         value = (long)va_arg (args, unsigned int);
00373                                 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
00374                                 break;
00375                         case 'f':
00376                                 if (cflags == DP_C_LDOUBLE)
00377                                         fvalue = va_arg (args, LDOUBLE);
00378                                 else
00379                                         fvalue = va_arg (args, double);
00380                                 /* um, floating point? */
00381                                 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
00382                                 break;
00383                         case 'E':
00384                                 flags |= DP_F_UP;
00385                         case 'e':
00386                                 if (cflags == DP_C_LDOUBLE)
00387                                         fvalue = va_arg (args, LDOUBLE);
00388                                 else
00389                                         fvalue = va_arg (args, double);
00390                                 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
00391                                 break;
00392                         case 'G':
00393                                 flags |= DP_F_UP;
00394                         case 'g':
00395                                 if (cflags == DP_C_LDOUBLE)
00396                                         fvalue = va_arg (args, LDOUBLE);
00397                                 else
00398                                         fvalue = va_arg (args, double);
00399                                 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
00400                                 break;
00401                         case 'c':
00402                                 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
00403                                 break;
00404                         case 's':
00405                                 strvalue = va_arg (args, char *);
00406                                 if (!strvalue) strvalue = "(NULL)";
00407                                 if (max == -1) {
00408                                         max = strlen(strvalue);
00409                                 }
00410                                 if (min > 0 && max >= 0 && min > max) max = min;
00411                                 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
00412                                 break;
00413                         case 'p':
00414                                 strvalue = va_arg (args, void *);
00415                                 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
00416                                 break;
00417                         case 'n':
00418                                 if (cflags == DP_C_SHORT) {
00419                                         short int *num;
00420                                         num = va_arg (args, short int *);
00421                                         *num = currlen;
00422                                 } else if (cflags == DP_C_LONG) {
00423                                         long int *num;
00424                                         num = va_arg (args, long int *);
00425                                         *num = (long int)currlen;
00426                                 } else if (cflags == DP_C_LLONG) {
00427                                         LLONG *num;
00428                                         num = va_arg (args, LLONG *);
00429                                         *num = (LLONG)currlen;
00430                                 } else {
00431                                         int *num;
00432                                         num = va_arg (args, int *);
00433                                         *num = currlen;
00434                                 }
00435                                 break;
00436                         case '%':
00437                                 dopr_outch (buffer, &currlen, maxlen, ch);
00438                                 break;
00439                         case 'w':
00440                                 /* not supported yet, treat as next char */
00441                                 ch = *format++;
00442                                 break;
00443                         default:
00444                                 /* Unknown, skip */
00445                                 break;
00446                         }
00447                         ch = *format++;
00448                         state = DP_S_DEFAULT;
00449                         flags = cflags = min = 0;
00450                         max = -1;
00451                         break;
00452                 case DP_S_DONE:
00453                         break;
00454                 default:
00455                         /* hmm? */
00456                         break; /* some picky compilers need this */
00457                 }
00458         }
00459         if (maxlen != 0) {
00460                 if (currlen < maxlen - 1) 
00461                         buffer[currlen] = '\0';
00462                 else if (maxlen > 0) 
00463                         buffer[maxlen - 1] = '\0';
00464         }
00465         
00466         return currlen;
00467 }
00468 
00469 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
00470                     char *value, int flags, int min, int max)
00471 {
00472         int padlen, strln;     /* amount to pad */
00473         int cnt = 0;
00474 
00475 #ifdef DEBUG_SNPRINTF
00476         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
00477 #endif
00478         if (value == 0) {
00479                 value = "<NULL>";
00480         }
00481 
00482         for (strln = 0; value[strln]; ++strln); /* strlen */
00483         padlen = min - strln;
00484         if (padlen < 0) 
00485                 padlen = 0;
00486         if (flags & DP_F_MINUS) 
00487                 padlen = -padlen; /* Left Justify */
00488         
00489         while ((padlen > 0) && (cnt < max)) {
00490                 dopr_outch (buffer, currlen, maxlen, ' ');
00491                 --padlen;
00492                 ++cnt;
00493         }
00494         while (*value && (cnt < max)) {
00495                 dopr_outch (buffer, currlen, maxlen, *value++);
00496                 ++cnt;
00497         }
00498         while ((padlen < 0) && (cnt < max)) {
00499                 dopr_outch (buffer, currlen, maxlen, ' ');
00500                 ++padlen;
00501                 ++cnt;
00502         }
00503 }
00504 
00505 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
00506 
00507 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
00508                     long value, int base, int min, int max, int flags)
00509 {
00510         int signvalue = 0;
00511         unsigned long uvalue;
00512         char convert[20];
00513         int place = 0;
00514         int spadlen = 0; /* amount to space pad */
00515         int zpadlen = 0; /* amount to zero pad */
00516         int caps = 0;
00517         
00518         if (max < 0)
00519                 max = 0;
00520         
00521         uvalue = value;
00522         
00523         if(!(flags & DP_F_UNSIGNED)) {
00524                 if( value < 0 ) {
00525                         signvalue = '-';
00526                         uvalue = -value;
00527                 } else {
00528                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
00529                                 signvalue = '+';
00530                         else if (flags & DP_F_SPACE)
00531                                 signvalue = ' ';
00532                 }
00533         }
00534   
00535         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
00536 
00537         do {
00538                 convert[place++] =
00539                         (caps? "0123456789ABCDEF":"0123456789abcdef")
00540                         [uvalue % (unsigned)base  ];
00541                 uvalue = (uvalue / (unsigned)base );
00542         } while(uvalue && (place < 20));
00543         if (place == 20) place--;
00544         convert[place] = 0;
00545 
00546         zpadlen = max - place;
00547         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
00548         if (zpadlen < 0) zpadlen = 0;
00549         if (spadlen < 0) spadlen = 0;
00550         if (flags & DP_F_ZERO) {
00551                 zpadlen = MAX(zpadlen, spadlen);
00552                 spadlen = 0;
00553         }
00554         if (flags & DP_F_MINUS) 
00555                 spadlen = -spadlen; /* Left Justifty */
00556 
00557 #ifdef DEBUG_SNPRINTF
00558         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
00559                zpadlen, spadlen, min, max, place);
00560 #endif
00561 
00562         /* Spaces */
00563         while (spadlen > 0) {
00564                 dopr_outch (buffer, currlen, maxlen, ' ');
00565                 --spadlen;
00566         }
00567 
00568         /* Sign */
00569         if (signvalue) 
00570                 dopr_outch (buffer, currlen, maxlen, signvalue);
00571 
00572         /* Zeros */
00573         if (zpadlen > 0) {
00574                 while (zpadlen > 0) {
00575                         dopr_outch (buffer, currlen, maxlen, '0');
00576                         --zpadlen;
00577                 }
00578         }
00579 
00580         /* Digits */
00581         while (place > 0) 
00582                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
00583   
00584         /* Left Justified spaces */
00585         while (spadlen < 0) {
00586                 dopr_outch (buffer, currlen, maxlen, ' ');
00587                 ++spadlen;
00588         }
00589 }
00590 
00591 static LDOUBLE abs_val(LDOUBLE value)
00592 {
00593         LDOUBLE result = value;
00594 
00595         if (value < 0)
00596                 result = -value;
00597         
00598         return result;
00599 }
00600 
00601 static LDOUBLE POW10(int exp)
00602 {
00603         LDOUBLE result = 1;
00604         
00605         while (exp) {
00606                 result *= 10;
00607                 exp--;
00608         }
00609   
00610         return result;
00611 }
00612 
00613 static LLONG ROUND(LDOUBLE value)
00614 {
00615         LLONG intpart;
00616 
00617         intpart = (LLONG)value;
00618         value = value - intpart;
00619         if (value >= 0.5) intpart++;
00620         
00621         return intpart;
00622 }
00623 
00624 /* a replacement for modf that doesn't need the math library. Should
00625    be portable, but slow */
00626 static double my_modf(double x0, double *iptr)
00627 {
00628         int i;
00629         long l;
00630         double x = x0;
00631         double f = 1.0;
00632 
00633         for (i=0;i<100;i++) {
00634                 l = (long)x;
00635                 if (l <= (x+1) && l >= (x-1)) {
00636                         if (i != 0) {
00637                                 double i2;
00638                                 double ret;
00639 
00640                                 ret = my_modf(x0-l*f, &i2);
00641                                 (*iptr) = l*f + i2;
00642                                 return ret;
00643                         } 
00644 
00645                         (*iptr) = l;
00646                         return x - (*iptr);
00647                 }
00648                 x *= 0.1;
00649                 f *= 10.0;
00650         }
00651 
00652         /* yikes! the number is beyond what we can handle. What do we do? */
00653         (*iptr) = 0;
00654         return 0;
00655 }
00656 
00657 
00658 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
00659                    LDOUBLE fvalue, int min, int max, int flags)
00660 {
00661         int signvalue = 0;
00662         double ufvalue;
00663         char iconvert[311];
00664         char fconvert[311];
00665         int iplace = 0;
00666         int fplace = 0;
00667         int padlen = 0; /* amount to pad */
00668         int zpadlen = 0; 
00669         int caps = 0;
00670         int idx;
00671         double intpart;
00672         double fracpart;
00673         double temp;
00674   
00675         /* 
00676          * AIX manpage says the default is 0, but Solaris says the default
00677          * is 6, and sprintf on AIX defaults to 6
00678          */
00679         if (max < 0)
00680                 max = 6;
00681 
00682         ufvalue = abs_val (fvalue);
00683 
00684         if (fvalue < 0) {
00685                 signvalue = '-';
00686         } else {
00687                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
00688                         signvalue = '+';
00689                 } else {
00690                         if (flags & DP_F_SPACE)
00691                                 signvalue = ' ';
00692                 }
00693         }
00694 
00695 #if 0
00696         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
00697 #endif
00698 
00699 #if 0
00700          if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
00701 #endif
00702 
00703         /* 
00704          * Sorry, we only support 16 digits past the decimal because of our 
00705          * conversion method
00706          */
00707         if (max > 16)
00708                 max = 16;
00709 
00710         /* We "cheat" by converting the fractional part to integer by
00711          * multiplying by a factor of 10
00712          */
00713 
00714         temp = ufvalue;
00715         my_modf(temp, &intpart);
00716 
00717         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
00718         
00719         if (fracpart >= POW10(max)) {
00720                 intpart++;
00721                 fracpart -= POW10(max);
00722         }
00723 
00724 
00725         /* Convert integer part */
00726         do {
00727                 temp = intpart*0.1;
00728                 my_modf(temp, &intpart);
00729                 idx = (int) ((temp -intpart +0.05)* 10.0);
00730                 /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
00731                 /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
00732                 iconvert[iplace++] =
00733                         (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
00734         } while (intpart && (iplace < 311));
00735         if (iplace == 311) iplace--;
00736         iconvert[iplace] = 0;
00737 
00738         /* Convert fractional part */
00739         if (fracpart)
00740         {
00741                 do {
00742                         temp = fracpart*0.1;
00743                         my_modf(temp, &fracpart);
00744                         idx = (int) ((temp -fracpart +0.05)* 10.0);
00745                         /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
00746                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
00747                         fconvert[fplace++] =
00748                         (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
00749                 } while(fracpart && (fplace < 311));
00750                 if (fplace == 311) fplace--;
00751         }
00752         fconvert[fplace] = 0;
00753   
00754         /* -1 for decimal point, another -1 if we are printing a sign */
00755         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
00756         zpadlen = max - fplace;
00757         if (zpadlen < 0) zpadlen = 0;
00758         if (padlen < 0) 
00759                 padlen = 0;
00760         if (flags & DP_F_MINUS) 
00761                 padlen = -padlen; /* Left Justifty */
00762         
00763         if ((flags & DP_F_ZERO) && (padlen > 0)) {
00764                 if (signvalue) {
00765                         dopr_outch (buffer, currlen, maxlen, signvalue);
00766                         --padlen;
00767                         signvalue = 0;
00768                 }
00769                 while (padlen > 0) {
00770                         dopr_outch (buffer, currlen, maxlen, '0');
00771                         --padlen;
00772                 }
00773         }
00774         while (padlen > 0) {
00775                 dopr_outch (buffer, currlen, maxlen, ' ');
00776                 --padlen;
00777         }
00778         if (signvalue) 
00779                 dopr_outch (buffer, currlen, maxlen, signvalue);
00780         
00781         while (iplace > 0) 
00782                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
00783 
00784 #ifdef DEBUG_SNPRINTF
00785         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
00786 #endif
00787 
00788         /*
00789          * Decimal point.  This should probably use locale to find the correct
00790          * char to print out.
00791          */
00792         if (max > 0) {
00793                 dopr_outch (buffer, currlen, maxlen, '.');
00794                 
00795                 while (zpadlen > 0) {
00796                         dopr_outch (buffer, currlen, maxlen, '0');
00797                         --zpadlen;
00798                 }
00799 
00800                 while (fplace > 0) 
00801                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
00802         }
00803 
00804         while (padlen < 0) {
00805                 dopr_outch (buffer, currlen, maxlen, ' ');
00806                 ++padlen;
00807         }
00808 }
00809 
00810 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
00811 {
00812         if (*currlen < maxlen) {
00813                 buffer[(*currlen)] = c;
00814         }
00815         (*currlen)++;
00816 }
00817 
00818  int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
00819 {
00820         return dopr(str, count, fmt, args);
00821 }
00822 #define vsnprintf rsync_vsnprintf
00823 #endif
00824 
00825 /* yes this really must be a ||. Don't muck with this (tridge)
00826  *
00827  * The logic for these two is that we need our own definition if the
00828  * OS *either* has no definition of *sprintf, or if it does have one
00829  * that doesn't work properly according to the autoconf test.
00830  */
00831 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
00832 int rsync_snprintf(char *str,size_t count,const char *fmt,...)
00833 {
00834         size_t ret;
00835         va_list ap;
00836     
00837         va_start(ap, fmt);
00838         ret = vsnprintf(str, count, fmt, ap);
00839         va_end(ap);
00840         return ret;
00841 }
00842 #define snprintf rsync_snprintf
00843 #endif
00844 
00845 #endif 
00846 
00847 #ifndef HAVE_VASPRINTF
00848  int vasprintf(char **ptr, const char *format, va_list ap)
00849 {
00850         int ret;
00851         va_list ap2;
00852 
00853         VA_COPY(ap2, ap);
00854         
00855         ret = vsnprintf(NULL, 0, format, ap2);
00856         if (ret <= 0) return ret;
00857 
00858         (*ptr) = (char *)malloc(ret+1);
00859         if (!*ptr) return -1;
00860 
00861         VA_COPY(ap2, ap);
00862 
00863         ret = vsnprintf(*ptr, ret+1, format, ap2);
00864 
00865         return ret;
00866 }
00867 #endif
00868 
00869 
00870 #ifndef HAVE_ASPRINTF
00871  int asprintf(char **ptr, const char *format, ...)
00872 {
00873         va_list ap;
00874         int ret;
00875         
00876         *ptr = NULL;
00877         va_start(ap, format);
00878         ret = vasprintf(ptr, format, ap);
00879         va_end(ap);
00880 
00881         return ret;
00882 }
00883 #endif
00884 
00885 #ifdef TEST_SNPRINTF
00886 
00887  int sprintf(char *str,const char *fmt,...);
00888 
00889  int main (void)
00890 {
00891         char buf1[1024];
00892         char buf2[1024];
00893         char *fp_fmt[] = {
00894                 "%1.1f",
00895                 "%-1.5f",
00896                 "%1.5f",
00897                 "%123.9f",
00898                 "%10.5f",
00899                 "% 10.5f",
00900                 "%+22.9f",
00901                 "%+4.9f",
00902                 "%01.3f",
00903                 "%4f",
00904                 "%3.1f",
00905                 "%3.2f",
00906                 "%.0f",
00907                 "%f",
00908                 "-16.16f",
00909                 NULL
00910         };
00911         double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996, 
00912                              0.9996, 1.996, 4.136, 5.030201, 0.00205,
00913                              /* END LIST */ 0};
00914         char *int_fmt[] = {
00915                 "%-1.5d",
00916                 "%1.5d",
00917                 "%123.9d",
00918                 "%5.5d",
00919                 "%10.5d",
00920                 "% 10.5d",
00921                 "%+22.33d",
00922                 "%01.3d",
00923                 "%4d",
00924                 "%d",
00925                 NULL
00926         };
00927         long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
00928         char *str_fmt[] = {
00929                 "10.5s",
00930                 "5.10s",
00931                 "10.1s",
00932                 "0.10s",
00933                 "10.0s",
00934                 "1.10s",
00935                 "%s",
00936                 "%.1s",
00937                 "%.10s",
00938                 "%10s",
00939                 NULL
00940         };
00941         char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
00942         int x, y;
00943         int fail = 0;
00944         int num = 0;
00945 
00946         printf ("Testing snprintf format codes against system sprintf...\n");
00947 
00948         for (x = 0; fp_fmt[x] ; x++) {
00949                 for (y = 0; fp_nums[y] != 0 ; y++) {
00950                         int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
00951                         int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
00952                         sprintf (buf2, fp_fmt[x], fp_nums[y]);
00953                         if (strcmp (buf1, buf2)) {
00954                                 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
00955                                        fp_fmt[x], buf1, buf2);
00956                                 fail++;
00957                         }
00958                         if (l1 != l2) {
00959                                 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
00960                                 fail++;
00961                         }
00962                         num++;
00963                 }
00964         }
00965 
00966         for (x = 0; int_fmt[x] ; x++) {
00967                 for (y = 0; int_nums[y] != 0 ; y++) {
00968                         int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
00969                         int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
00970                         sprintf (buf2, int_fmt[x], int_nums[y]);
00971                         if (strcmp (buf1, buf2)) {
00972                                 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
00973                                        int_fmt[x], buf1, buf2);
00974                                 fail++;
00975                         }
00976                         if (l1 != l2) {
00977                                 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
00978                                 fail++;
00979                         }
00980                         num++;
00981                 }
00982         }
00983 
00984         for (x = 0; str_fmt[x] ; x++) {
00985                 for (y = 0; str_vals[y] != 0 ; y++) {
00986                         int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
00987                         int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
00988                         sprintf (buf2, str_fmt[x], str_vals[y]);
00989                         if (strcmp (buf1, buf2)) {
00990                                 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
00991                                        str_fmt[x], buf1, buf2);
00992                                 fail++;
00993                         }
00994                         if (l1 != l2) {
00995                                 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
00996                                 fail++;
00997                         }
00998                         num++;
00999                 }
01000         }
01001 
01002         printf ("%d tests failed out of %d.\n", fail, num);
01003 
01004         printf("seeing how many digits we support\n");
01005         {
01006                 double v0 = 0.12345678901234567890123456789012345678901;
01007                 for (x=0; x<100; x++) {
01008                         double p = pow(10, x); 
01009                         double r = v0*p;
01010                         snprintf(buf1, sizeof(buf1), "%1.1f", r);
01011                         sprintf(buf2,                "%1.1f", r);
01012                         if (strcmp(buf1, buf2)) {
01013                                 printf("we seem to support %d digits\n", x-1);
01014                                 break;
01015                         }
01016                 }
01017         }
01018 
01019         return 0;
01020 }
01021 #endif /* TEST_SNPRINTF */

rsyncに対してSat Dec 5 19:45:42 2009に生成されました。  doxygen 1.4.7