lib/replace/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  * Darren Tucker (dtucker@zip.com.au) 2005
00094  *    Fix bug allowing read overruns of the source string with "%.*s"
00095  *    Usually harmless unless the read runs outside the process' allocation
00096  *    (eg if your malloc does guard pages) in which case it will segfault.
00097  *    From OpenSSH.  Also added test for same.
00098  *
00099  * Simo Sorce (idra@samba.org) Jan 2006
00100  * 
00101  *    Add support for position independent parameters 
00102  *    fix fmtstr now it conforms to sprintf wrt min.max
00103  *
00104  **************************************************************/
00105 
00106 #include "replace.h"
00107 #include "system/locale.h"
00108 
00109 #ifdef TEST_SNPRINTF /* need math library headers for testing */
00110 
00111 /* In test mode, we pretend that this system doesn't have any snprintf
00112  * functions, regardless of what config.h says. */
00113 #  undef HAVE_SNPRINTF
00114 #  undef HAVE_VSNPRINTF
00115 #  undef HAVE_C99_VSNPRINTF
00116 #  undef HAVE_ASPRINTF
00117 #  undef HAVE_VASPRINTF
00118 #  include <math.h>
00119 #endif /* TEST_SNPRINTF */
00120 
00121 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
00122 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
00123 #include <stdio.h>
00124  /* make the compiler happy with an empty file */
00125  void dummy_snprintf(void);
00126  void dummy_snprintf(void) {} 
00127 #endif /* HAVE_SNPRINTF, etc */
00128 
00129 /* yes this really must be a ||. Don't muck with this (tridge) */
00130 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
00131 
00132 #ifdef HAVE_LONG_DOUBLE
00133 #define LDOUBLE long double
00134 #else
00135 #define LDOUBLE double
00136 #endif
00137 
00138 #ifdef HAVE_LONG_LONG
00139 #define LLONG long long
00140 #else
00141 #define LLONG long
00142 #endif
00143 
00144 #ifndef VA_COPY
00145 #ifdef HAVE_VA_COPY
00146 #define VA_COPY(dest, src) va_copy(dest, src)
00147 #else
00148 #ifdef HAVE___VA_COPY
00149 #define VA_COPY(dest, src) __va_copy(dest, src)
00150 #else
00151 #define VA_COPY(dest, src) (dest) = (src)
00152 #endif
00153 #endif
00154 
00155 /*
00156  * dopr(): poor man's version of doprintf
00157  */
00158 
00159 /* format read states */
00160 #define DP_S_DEFAULT 0
00161 #define DP_S_FLAGS   1
00162 #define DP_S_MIN     2
00163 #define DP_S_DOT     3
00164 #define DP_S_MAX     4
00165 #define DP_S_MOD     5
00166 #define DP_S_CONV    6
00167 #define DP_S_DONE    7
00168 
00169 /* format flags - Bits */
00170 #define DP_F_MINUS      (1 << 0)
00171 #define DP_F_PLUS       (1 << 1)
00172 #define DP_F_SPACE      (1 << 2)
00173 #define DP_F_NUM        (1 << 3)
00174 #define DP_F_ZERO       (1 << 4)
00175 #define DP_F_UP         (1 << 5)
00176 #define DP_F_UNSIGNED   (1 << 6)
00177 
00178 /* Conversion Flags */
00179 #define DP_C_CHAR    1
00180 #define DP_C_SHORT   2
00181 #define DP_C_LONG    3
00182 #define DP_C_LDOUBLE 4
00183 #define DP_C_LLONG   5
00184 #define DP_C_SIZET   6
00185 
00186 /* Chunk types */
00187 #define CNK_FMT_STR 0
00188 #define CNK_INT     1
00189 #define CNK_OCTAL   2
00190 #define CNK_UINT    3
00191 #define CNK_HEX     4
00192 #define CNK_FLOAT   5
00193 #define CNK_CHAR    6
00194 #define CNK_STRING  7
00195 #define CNK_PTR     8
00196 #define CNK_NUM     9
00197 #define CNK_PRCNT   10
00198 
00199 #define char_to_int(p) ((p)- '0')
00200 #ifndef MAX
00201 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
00202 #endif
00203 
00204 struct pr_chunk {
00205         int type; /* chunk type */
00206         int num; /* parameter number */
00207         int min; 
00208         int max;
00209         int flags;
00210         int cflags;
00211         int start;
00212         int len;
00213         LLONG value;
00214         LDOUBLE fvalue;
00215         char *strvalue;
00216         void *pnum;
00217         struct pr_chunk *min_star;
00218         struct pr_chunk *max_star;
00219         struct pr_chunk *next;
00220 };
00221 
00222 struct pr_chunk_x {
00223         struct pr_chunk **chunks;
00224         int num;
00225 };
00226 
00227 static int dopr(char *buffer, size_t maxlen, const char *format, 
00228                    va_list args_in);
00229 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
00230                     char *value, int flags, int min, int max);
00231 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
00232                     LLONG value, int base, int min, int max, int flags);
00233 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
00234                    LDOUBLE fvalue, int min, int max, int flags);
00235 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
00236 static struct pr_chunk *new_chunk(void);
00237 static int add_cnk_list_entry(struct pr_chunk_x **list,
00238                                 int max_num, struct pr_chunk *chunk);
00239 
00240 static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
00241 {
00242         char ch;
00243         int state;
00244         int pflag;
00245         int pnum;
00246         int pfirst;
00247         size_t currlen;
00248         va_list args;
00249         const char *base;
00250         struct pr_chunk *chunks = NULL;
00251         struct pr_chunk *cnk = NULL;
00252         struct pr_chunk_x *clist = NULL;
00253         int max_pos;
00254         int ret = -1;
00255 
00256         VA_COPY(args, args_in);
00257 
00258         state = DP_S_DEFAULT;
00259         pfirst = 1;
00260         pflag = 0;
00261         pnum = 0;
00262 
00263         max_pos = 0;
00264         base = format;
00265         ch = *format++;
00266         
00267         /* retrieve the string structure as chunks */
00268         while (state != DP_S_DONE) {
00269                 if (ch == '\0') 
00270                         state = DP_S_DONE;
00271 
00272                 switch(state) {
00273                 case DP_S_DEFAULT:
00274                         
00275                         if (cnk) {
00276                                 cnk->next = new_chunk();
00277                                 cnk = cnk->next;
00278                         } else {
00279                                 cnk = new_chunk();
00280                         }
00281                         if (!cnk) goto done;
00282                         if (!chunks) chunks = cnk;
00283                         
00284                         if (ch == '%') {
00285                                 state = DP_S_FLAGS;
00286                                 ch = *format++;
00287                         } else {
00288                                 cnk->type = CNK_FMT_STR;
00289                                 cnk->start = format - base -1;
00290                                 while ((ch != '\0') && (ch != '%')) ch = *format++;
00291                                 cnk->len = format - base - cnk->start -1;
00292                         }
00293                         break;
00294                 case DP_S_FLAGS:
00295                         switch (ch) {
00296                         case '-':
00297                                 cnk->flags |= DP_F_MINUS;
00298                                 ch = *format++;
00299                                 break;
00300                         case '+':
00301                                 cnk->flags |= DP_F_PLUS;
00302                                 ch = *format++;
00303                                 break;
00304                         case ' ':
00305                                 cnk->flags |= DP_F_SPACE;
00306                                 ch = *format++;
00307                                 break;
00308                         case '#':
00309                                 cnk->flags |= DP_F_NUM;
00310                                 ch = *format++;
00311                                 break;
00312                         case '0':
00313                                 cnk->flags |= DP_F_ZERO;
00314                                 ch = *format++;
00315                                 break;
00316                         case 'I':
00317                                 /* internationalization not supported yet */
00318                                 ch = *format++;
00319                                 break;
00320                         default:
00321                                 state = DP_S_MIN;
00322                                 break;
00323                         }
00324                         break;
00325                 case DP_S_MIN:
00326                         if (isdigit((unsigned char)ch)) {
00327                                 cnk->min = 10 * cnk->min + char_to_int (ch);
00328                                 ch = *format++;
00329                         } else if (ch == '$') {
00330                                 if (!pfirst && !pflag) {
00331                                         /* parameters must be all positioned or none */
00332                                         goto done;
00333                                 }
00334                                 if (pfirst) {
00335                                         pfirst = 0;
00336                                         pflag = 1;
00337                                 }
00338                                 if (cnk->min == 0) /* what ?? */
00339                                         goto done;
00340                                 cnk->num = cnk->min;
00341                                 cnk->min = 0;
00342                                 ch = *format++;
00343                         } else if (ch == '*') {
00344                                 if (pfirst) pfirst = 0;
00345                                 cnk->min_star = new_chunk();
00346                                 if (!cnk->min_star) /* out of memory :-( */
00347                                         goto done;
00348                                 cnk->min_star->type = CNK_INT;
00349                                 if (pflag) {
00350                                         int num;
00351                                         ch = *format++;
00352                                         if (!isdigit((unsigned char)ch)) {
00353                                                 /* parameters must be all positioned or none */
00354                                                 goto done;
00355                                         }
00356                                         for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
00357                                                 num = 10 * num + char_to_int(ch);
00358                                         }
00359                                         cnk->min_star->num = num;
00360                                         if (ch != '$') /* what ?? */
00361                                                 goto done;
00362                                 } else {
00363                                         cnk->min_star->num = ++pnum;
00364                                 }
00365                                 max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
00366                                 if (max_pos == 0) /* out of memory :-( */
00367                                         goto done;
00368                                 ch = *format++;
00369                                 state = DP_S_DOT;
00370                         } else {
00371                                 if (pfirst) pfirst = 0;
00372                                 state = DP_S_DOT;
00373                         }
00374                         break;
00375                 case DP_S_DOT:
00376                         if (ch == '.') {
00377                                 state = DP_S_MAX;
00378                                 ch = *format++;
00379                         } else { 
00380                                 state = DP_S_MOD;
00381                         }
00382                         break;
00383                 case DP_S_MAX:
00384                         if (isdigit((unsigned char)ch)) {
00385                                 if (cnk->max < 0)
00386                                         cnk->max = 0;
00387                                 cnk->max = 10 * cnk->max + char_to_int (ch);
00388                                 ch = *format++;
00389                         } else if (ch == '$') {
00390                                 if (!pfirst && !pflag) {
00391                                         /* parameters must be all positioned or none */
00392                                         goto done;
00393                                 }
00394                                 if (cnk->max <= 0) /* what ?? */
00395                                         goto done;
00396                                 cnk->num = cnk->max;
00397                                 cnk->max = -1;
00398                                 ch = *format++;
00399                         } else if (ch == '*') {
00400                                 cnk->max_star = new_chunk();
00401                                 if (!cnk->max_star) /* out of memory :-( */
00402                                         goto done;
00403                                 cnk->max_star->type = CNK_INT;
00404                                 if (pflag) {
00405                                         int num;
00406                                         ch = *format++;
00407                                         if (!isdigit((unsigned char)ch)) {
00408                                                 /* parameters must be all positioned or none */
00409                                                 goto done;
00410                                         }
00411                                         for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
00412                                                 num = 10 * num + char_to_int(ch);
00413                                         }
00414                                         cnk->max_star->num = num;
00415                                         if (ch != '$') /* what ?? */
00416                                                 goto done;
00417                                 } else {
00418                                         cnk->max_star->num = ++pnum;
00419                                 }
00420                                 max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
00421                                 if (max_pos == 0) /* out of memory :-( */
00422                                         goto done;
00423 
00424                                 ch = *format++;
00425                                 state = DP_S_MOD;
00426                         } else {
00427                                 state = DP_S_MOD;
00428                         }
00429                         break;
00430                 case DP_S_MOD:
00431                         switch (ch) {
00432                         case 'h':
00433                                 cnk->cflags = DP_C_SHORT;
00434                                 ch = *format++;
00435                                 if (ch == 'h') {
00436                                         cnk->cflags = DP_C_CHAR;
00437                                         ch = *format++;
00438                                 }
00439                                 break;
00440                         case 'l':
00441                                 cnk->cflags = DP_C_LONG;
00442                                 ch = *format++;
00443                                 if (ch == 'l') {        /* It's a long long */
00444                                         cnk->cflags = DP_C_LLONG;
00445                                         ch = *format++;
00446                                 }
00447                                 break;
00448                         case 'L':
00449                                 cnk->cflags = DP_C_LDOUBLE;
00450                                 ch = *format++;
00451                                 break;
00452                         case 'z':
00453                                 cnk->cflags = DP_C_SIZET;
00454                                 ch = *format++;
00455                                 break;
00456                         default:
00457                                 break;
00458                         }
00459                         state = DP_S_CONV;
00460                         break;
00461                 case DP_S_CONV:
00462                         if (cnk->num == 0) cnk->num = ++pnum;
00463                         max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
00464                         if (max_pos == 0) /* out of memory :-( */
00465                                 goto done;
00466                         
00467                         switch (ch) {
00468                         case 'd':
00469                         case 'i':
00470                                 cnk->type = CNK_INT;
00471                                 break;
00472                         case 'o':
00473                                 cnk->type = CNK_OCTAL;
00474                                 cnk->flags |= DP_F_UNSIGNED;
00475                                 break;
00476                         case 'u':
00477                                 cnk->type = CNK_UINT;
00478                                 cnk->flags |= DP_F_UNSIGNED;
00479                                 break;
00480                         case 'X':
00481                                 cnk->flags |= DP_F_UP;
00482                         case 'x':
00483                                 cnk->type = CNK_HEX;
00484                                 cnk->flags |= DP_F_UNSIGNED;
00485                                 break;
00486                         case 'A':
00487                                 /* hex float not supported yet */
00488                         case 'E':
00489                         case 'G':
00490                         case 'F':
00491                                 cnk->flags |= DP_F_UP;
00492                         case 'a':
00493                                 /* hex float not supported yet */
00494                         case 'e':
00495                         case 'f':
00496                         case 'g':
00497                                 cnk->type = CNK_FLOAT;
00498                                 break;
00499                         case 'c':
00500                                 cnk->type = CNK_CHAR;
00501                                 break;
00502                         case 's':
00503                                 cnk->type = CNK_STRING;
00504                                 break;
00505                         case 'p':
00506                                 cnk->type = CNK_PTR;
00507                                 break;
00508                         case 'n':
00509                                 cnk->type = CNK_NUM;
00510                                 break;
00511                         case '%':
00512                                 cnk->type = CNK_PRCNT;
00513                                 break;
00514                         default:
00515                                 /* Unknown, bail out*/
00516                                 goto done;
00517                         }
00518                         ch = *format++;
00519                         state = DP_S_DEFAULT;
00520                         break;
00521                 case DP_S_DONE:
00522                         break;
00523                 default:
00524                         /* hmm? */
00525                         break; /* some picky compilers need this */
00526                 }
00527         }
00528 
00529         /* retieve the format arguments */
00530         for (pnum = 0; pnum < max_pos; pnum++) {
00531                 int i;
00532 
00533                 if (clist[pnum].num == 0) {
00534                         /* ignoring a parameter should not be permitted
00535                          * all parameters must be matched at least once
00536                          * BUT seem some system ignore this rule ...
00537                          * at least my glibc based system does --SSS
00538                          */
00539 #ifdef DEBUG_SNPRINTF
00540                         printf("parameter at position %d not used\n", pnum+1);
00541 #endif
00542                         /* eat the parameter */
00543                         va_arg (args, int);
00544                         continue;
00545                 }
00546                 for (i = 1; i < clist[pnum].num; i++) {
00547                         if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
00548                                 /* nooo noo no!
00549                                  * all the references to a parameter
00550                                  * must be of the same type
00551                                  */
00552                                 goto done;
00553                         }
00554                 }
00555                 cnk = clist[pnum].chunks[0];
00556                 switch (cnk->type) {
00557                 case CNK_INT:
00558                         if (cnk->cflags == DP_C_SHORT) 
00559                                 cnk->value = va_arg (args, int);
00560                         else if (cnk->cflags == DP_C_LONG)
00561                                 cnk->value = va_arg (args, long int);
00562                         else if (cnk->cflags == DP_C_LLONG)
00563                                 cnk->value = va_arg (args, LLONG);
00564                         else if (cnk->cflags == DP_C_SIZET)
00565                                 cnk->value = va_arg (args, ssize_t);
00566                         else
00567                                 cnk->value = va_arg (args, int);
00568 
00569                         for (i = 1; i < clist[pnum].num; i++) {
00570                                 clist[pnum].chunks[i]->value = cnk->value;
00571                         }
00572                         break;
00573 
00574                 case CNK_OCTAL:
00575                 case CNK_UINT:
00576                 case CNK_HEX:
00577                         if (cnk->cflags == DP_C_SHORT)
00578                                 cnk->value = va_arg (args, unsigned int);
00579                         else if (cnk->cflags == DP_C_LONG)
00580                                 cnk->value = (unsigned long int)va_arg (args, unsigned long int);
00581                         else if (cnk->cflags == DP_C_LLONG)
00582                                 cnk->value = (LLONG)va_arg (args, unsigned LLONG);
00583                         else if (cnk->cflags == DP_C_SIZET)
00584                                 cnk->value = (size_t)va_arg (args, size_t);
00585                         else
00586                                 cnk->value = (unsigned int)va_arg (args, unsigned int);
00587 
00588                         for (i = 1; i < clist[pnum].num; i++) {
00589                                 clist[pnum].chunks[i]->value = cnk->value;
00590                         }
00591                         break;
00592 
00593                 case CNK_FLOAT:
00594                         if (cnk->cflags == DP_C_LDOUBLE)
00595                                 cnk->fvalue = va_arg (args, LDOUBLE);
00596                         else
00597                                 cnk->fvalue = va_arg (args, double);
00598 
00599                         for (i = 1; i < clist[pnum].num; i++) {
00600                                 clist[pnum].chunks[i]->fvalue = cnk->fvalue;
00601                         }
00602                         break;
00603 
00604                 case CNK_CHAR:
00605                         cnk->value = va_arg (args, int);
00606 
00607                         for (i = 1; i < clist[pnum].num; i++) {
00608                                 clist[pnum].chunks[i]->value = cnk->value;
00609                         }
00610                         break;
00611 
00612                 case CNK_STRING:
00613                         cnk->strvalue = va_arg (args, char *);
00614                         if (!cnk->strvalue) cnk->strvalue = "(NULL)";
00615 
00616                         for (i = 1; i < clist[pnum].num; i++) {
00617                                 clist[pnum].chunks[i]->strvalue = cnk->strvalue;
00618                         }
00619                         break;
00620 
00621                 case CNK_PTR:
00622                         cnk->strvalue = va_arg (args, void *);
00623                         for (i = 1; i < clist[pnum].num; i++) {
00624                                 clist[pnum].chunks[i]->strvalue = cnk->strvalue;
00625                         }
00626                         break;
00627 
00628                 case CNK_NUM:
00629                         if (cnk->cflags == DP_C_CHAR)
00630                                 cnk->pnum = va_arg (args, char *);
00631                         else if (cnk->cflags == DP_C_SHORT)
00632                                 cnk->pnum = va_arg (args, short int *);
00633                         else if (cnk->cflags == DP_C_LONG)
00634                                 cnk->pnum = va_arg (args, long int *);
00635                         else if (cnk->cflags == DP_C_LLONG)
00636                                 cnk->pnum = va_arg (args, LLONG *);
00637                         else if (cnk->cflags == DP_C_SIZET)
00638                                 cnk->pnum = va_arg (args, ssize_t *);
00639                         else
00640                                 cnk->pnum = va_arg (args, int *);
00641 
00642                         for (i = 1; i < clist[pnum].num; i++) {
00643                                 clist[pnum].chunks[i]->pnum = cnk->pnum;
00644                         }
00645                         break;
00646 
00647                 case CNK_PRCNT:
00648                         break;
00649 
00650                 default:
00651                         /* what ?? */
00652                         goto done;
00653                 }
00654         }
00655         /* print out the actual string from chunks */
00656         currlen = 0;
00657         cnk = chunks;
00658         while (cnk) {
00659                 int len, min, max;
00660 
00661                 if (cnk->min_star) min = cnk->min_star->value;
00662                 else min = cnk->min;
00663                 if (cnk->max_star) max = cnk->max_star->value;
00664                 else max = cnk->max;
00665 
00666                 switch (cnk->type) {
00667 
00668                 case CNK_FMT_STR:
00669                         if (maxlen != 0 && maxlen > currlen) {
00670                                 if (maxlen > (currlen + cnk->len)) len = cnk->len;
00671                                 else len = maxlen - currlen;
00672 
00673                                 memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
00674                         }
00675                         currlen += cnk->len;
00676                                 
00677                         break;
00678 
00679                 case CNK_INT:
00680                 case CNK_UINT:
00681                         fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
00682                         break;
00683 
00684                 case CNK_OCTAL:
00685                         fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
00686                         break;
00687 
00688                 case CNK_HEX:
00689                         fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
00690                         break;
00691 
00692                 case CNK_FLOAT:
00693                         fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
00694                         break;
00695 
00696                 case CNK_CHAR:
00697                         dopr_outch (buffer, &currlen, maxlen, cnk->value);
00698                         break;
00699 
00700                 case CNK_STRING:
00701                         if (max == -1) {
00702                                 max = strlen(cnk->strvalue);
00703                         }
00704                         fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
00705                         break;
00706 
00707                 case CNK_PTR:
00708                         fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
00709                         break;
00710 
00711                 case CNK_NUM:
00712                         if (cnk->cflags == DP_C_CHAR)
00713                                 *((char *)(cnk->pnum)) = (char)currlen;
00714                         else if (cnk->cflags == DP_C_SHORT)
00715                                 *((short int *)(cnk->pnum)) = (short int)currlen;
00716                         else if (cnk->cflags == DP_C_LONG)
00717                                 *((long int *)(cnk->pnum)) = (long int)currlen;
00718                         else if (cnk->cflags == DP_C_LLONG)
00719                                 *((LLONG *)(cnk->pnum)) = (LLONG)currlen;
00720                         else if (cnk->cflags == DP_C_SIZET)
00721                                 *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
00722                         else
00723                                 *((int *)(cnk->pnum)) = (int)currlen;
00724                         break;
00725 
00726                 case CNK_PRCNT:
00727                         dopr_outch (buffer, &currlen, maxlen, '%');
00728                         break;
00729 
00730                 default:
00731                         /* what ?? */
00732                         goto done;
00733                 }
00734                 cnk = cnk->next;
00735         }
00736         if (maxlen != 0) {
00737                 if (currlen < maxlen - 1) 
00738                         buffer[currlen] = '\0';
00739                 else if (maxlen > 0) 
00740                         buffer[maxlen - 1] = '\0';
00741         }
00742         ret = currlen;
00743 
00744 done:
00745         va_end(args);
00746 
00747         while (chunks) {
00748                 cnk = chunks->next;
00749                 free(chunks);
00750                 chunks = cnk;
00751         }
00752         if (clist) {
00753                 for (pnum = 0; pnum < max_pos; pnum++) {
00754                         if (clist[pnum].chunks) free(clist[pnum].chunks);
00755                 }
00756                 free(clist);
00757         }
00758         return ret;
00759 }
00760 
00761 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
00762                     char *value, int flags, int min, int max)
00763 {
00764         int padlen, strln;     /* amount to pad */
00765         int cnt = 0;
00766 
00767 #ifdef DEBUG_SNPRINTF
00768         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
00769 #endif
00770         if (value == 0) {
00771                 value = "<NULL>";
00772         }
00773 
00774         for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
00775         padlen = min - strln;
00776         if (padlen < 0) 
00777                 padlen = 0;
00778         if (flags & DP_F_MINUS) 
00779                 padlen = -padlen; /* Left Justify */
00780         
00781         while (padlen > 0) {
00782                 dopr_outch (buffer, currlen, maxlen, ' ');
00783                 --padlen;
00784         }
00785         while (*value && (cnt < max)) {
00786                 dopr_outch (buffer, currlen, maxlen, *value++);
00787                 ++cnt;
00788         }
00789         while (padlen < 0) {
00790                 dopr_outch (buffer, currlen, maxlen, ' ');
00791                 ++padlen;
00792         }
00793 }
00794 
00795 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
00796 
00797 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
00798                     LLONG value, int base, int min, int max, int flags)
00799 {
00800         int signvalue = 0;
00801         unsigned LLONG uvalue;
00802         char convert[20];
00803         int place = 0;
00804         int spadlen = 0; /* amount to space pad */
00805         int zpadlen = 0; /* amount to zero pad */
00806         int caps = 0;
00807         
00808         if (max < 0)
00809                 max = 0;
00810         
00811         uvalue = value;
00812         
00813         if(!(flags & DP_F_UNSIGNED)) {
00814                 if( value < 0 ) {
00815                         signvalue = '-';
00816                         uvalue = -value;
00817                 } else {
00818                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
00819                                 signvalue = '+';
00820                         else if (flags & DP_F_SPACE)
00821                                 signvalue = ' ';
00822                 }
00823         }
00824   
00825         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
00826 
00827         do {
00828                 convert[place++] =
00829                         (caps? "0123456789ABCDEF":"0123456789abcdef")
00830                         [uvalue % (unsigned)base  ];
00831                 uvalue = (uvalue / (unsigned)base );
00832         } while(uvalue && (place < 20));
00833         if (place == 20) place--;
00834         convert[place] = 0;
00835 
00836         zpadlen = max - place;
00837         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
00838         if (zpadlen < 0) zpadlen = 0;
00839         if (spadlen < 0) spadlen = 0;
00840         if (flags & DP_F_ZERO) {
00841                 zpadlen = MAX(zpadlen, spadlen);
00842                 spadlen = 0;
00843         }
00844         if (flags & DP_F_MINUS) 
00845                 spadlen = -spadlen; /* Left Justifty */
00846 
00847 #ifdef DEBUG_SNPRINTF
00848         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
00849                zpadlen, spadlen, min, max, place);
00850 #endif
00851 
00852         /* Spaces */
00853         while (spadlen > 0) {
00854                 dopr_outch (buffer, currlen, maxlen, ' ');
00855                 --spadlen;
00856         }
00857 
00858         /* Sign */
00859         if (signvalue) 
00860                 dopr_outch (buffer, currlen, maxlen, signvalue);
00861 
00862         /* Zeros */
00863         if (zpadlen > 0) {
00864                 while (zpadlen > 0) {
00865                         dopr_outch (buffer, currlen, maxlen, '0');
00866                         --zpadlen;
00867                 }
00868         }
00869 
00870         /* Digits */
00871         while (place > 0) 
00872                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
00873   
00874         /* Left Justified spaces */
00875         while (spadlen < 0) {
00876                 dopr_outch (buffer, currlen, maxlen, ' ');
00877                 ++spadlen;
00878         }
00879 }
00880 
00881 static LDOUBLE abs_val(LDOUBLE value)
00882 {
00883         LDOUBLE result = value;
00884 
00885         if (value < 0)
00886                 result = -value;
00887         
00888         return result;
00889 }
00890 
00891 static LDOUBLE POW10(int exp)
00892 {
00893         LDOUBLE result = 1;
00894         
00895         while (exp) {
00896                 result *= 10;
00897                 exp--;
00898         }
00899   
00900         return result;
00901 }
00902 
00903 static LLONG ROUND(LDOUBLE value)
00904 {
00905         LLONG intpart;
00906 
00907         intpart = (LLONG)value;
00908         value = value - intpart;
00909         if (value >= 0.5) intpart++;
00910         
00911         return intpart;
00912 }
00913 
00914 /* a replacement for modf that doesn't need the math library. Should
00915    be portable, but slow */
00916 static double my_modf(double x0, double *iptr)
00917 {
00918         int i;
00919         LLONG l=0;
00920         double x = x0;
00921         double f = 1.0;
00922 
00923         for (i=0;i<100;i++) {
00924                 l = (long)x;
00925                 if (l <= (x+1) && l >= (x-1)) break;
00926                 x *= 0.1;
00927                 f *= 10.0;
00928         }
00929 
00930         if (i == 100) {
00931                 /* yikes! the number is beyond what we can handle. What do we do? */
00932                 (*iptr) = 0;
00933                 return 0;
00934         }
00935 
00936         if (i != 0) {
00937                 double i2;
00938                 double ret;
00939 
00940                 ret = my_modf(x0-l*f, &i2);
00941                 (*iptr) = l*f + i2;
00942                 return ret;
00943         } 
00944 
00945         (*iptr) = l;
00946         return x - (*iptr);
00947 }
00948 
00949 
00950 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
00951                    LDOUBLE fvalue, int min, int max, int flags)
00952 {
00953         int signvalue = 0;
00954         double ufvalue;
00955         char iconvert[311];
00956         char fconvert[311];
00957         int iplace = 0;
00958         int fplace = 0;
00959         int padlen = 0; /* amount to pad */
00960         int zpadlen = 0; 
00961         int caps = 0;
00962         int idx;
00963         double intpart;
00964         double fracpart;
00965         double temp;
00966   
00967         /* 
00968          * AIX manpage says the default is 0, but Solaris says the default
00969          * is 6, and sprintf on AIX defaults to 6
00970          */
00971         if (max < 0)
00972                 max = 6;
00973 
00974         ufvalue = abs_val (fvalue);
00975 
00976         if (fvalue < 0) {
00977                 signvalue = '-';
00978         } else {
00979                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
00980                         signvalue = '+';
00981                 } else {
00982                         if (flags & DP_F_SPACE)
00983                                 signvalue = ' ';
00984                 }
00985         }
00986 
00987 #if 0
00988         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
00989 #endif
00990 
00991 #if 0
00992          if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
00993 #endif
00994 
00995         /* 
00996          * Sorry, we only support 9 digits past the decimal because of our 
00997          * conversion method
00998          */
00999         if (max > 9)
01000                 max = 9;
01001 
01002         /* We "cheat" by converting the fractional part to integer by
01003          * multiplying by a factor of 10
01004          */
01005 
01006         temp = ufvalue;
01007         my_modf(temp, &intpart);
01008 
01009         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
01010         
01011         if (fracpart >= POW10(max)) {
01012                 intpart++;
01013                 fracpart -= POW10(max);
01014         }
01015 
01016 
01017         /* Convert integer part */
01018         do {
01019                 temp = intpart*0.1;
01020                 my_modf(temp, &intpart);
01021                 idx = (int) ((temp -intpart +0.05)* 10.0);
01022                 /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
01023                 /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
01024                 iconvert[iplace++] =
01025                         (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
01026         } while (intpart && (iplace < 311));
01027         if (iplace == 311) iplace--;
01028         iconvert[iplace] = 0;
01029 
01030         /* Convert fractional part */
01031         if (fracpart)
01032         {
01033                 do {
01034                         temp = fracpart*0.1;
01035                         my_modf(temp, &fracpart);
01036                         idx = (int) ((temp -fracpart +0.05)* 10.0);
01037                         /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
01038                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
01039                         fconvert[fplace++] =
01040                         (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
01041                 } while(fracpart && (fplace < 311));
01042                 if (fplace == 311) fplace--;
01043         }
01044         fconvert[fplace] = 0;
01045   
01046         /* -1 for decimal point, another -1 if we are printing a sign */
01047         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
01048         zpadlen = max - fplace;
01049         if (zpadlen < 0) zpadlen = 0;
01050         if (padlen < 0) 
01051                 padlen = 0;
01052         if (flags & DP_F_MINUS) 
01053                 padlen = -padlen; /* Left Justifty */
01054         
01055         if ((flags & DP_F_ZERO) && (padlen > 0)) {
01056                 if (signvalue) {
01057                         dopr_outch (buffer, currlen, maxlen, signvalue);
01058                         --padlen;
01059                         signvalue = 0;
01060                 }
01061                 while (padlen > 0) {
01062                         dopr_outch (buffer, currlen, maxlen, '0');
01063                         --padlen;
01064                 }
01065         }
01066         while (padlen > 0) {
01067                 dopr_outch (buffer, currlen, maxlen, ' ');
01068                 --padlen;
01069         }
01070         if (signvalue) 
01071                 dopr_outch (buffer, currlen, maxlen, signvalue);
01072         
01073         while (iplace > 0) 
01074                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
01075 
01076 #ifdef DEBUG_SNPRINTF
01077         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
01078 #endif
01079 
01080         /*
01081          * Decimal point.  This should probably use locale to find the correct
01082          * char to print out.
01083          */
01084         if (max > 0) {
01085                 dopr_outch (buffer, currlen, maxlen, '.');
01086                 
01087                 while (zpadlen > 0) {
01088                         dopr_outch (buffer, currlen, maxlen, '0');
01089                         --zpadlen;
01090                 }
01091 
01092                 while (fplace > 0) 
01093                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
01094         }
01095 
01096         while (padlen < 0) {
01097                 dopr_outch (buffer, currlen, maxlen, ' ');
01098                 ++padlen;
01099         }
01100 }
01101 
01102 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
01103 {
01104         if (*currlen < maxlen) {
01105                 buffer[(*currlen)] = c;
01106         }
01107         (*currlen)++;
01108 }
01109 
01110 static struct pr_chunk *new_chunk(void) {
01111         struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
01112 
01113         if (!new_c)
01114                 return NULL;
01115 
01116         new_c->type = 0;
01117         new_c->num = 0;
01118         new_c->min = 0;
01119         new_c->min_star = NULL;
01120         new_c->max = -1;
01121         new_c->max_star = NULL;
01122         new_c->flags = 0;
01123         new_c->cflags = 0;
01124         new_c->start = 0;
01125         new_c->len = 0;
01126         new_c->value = 0;
01127         new_c->fvalue = 0;
01128         new_c->strvalue = NULL;
01129         new_c->pnum = NULL;
01130         new_c->next = NULL;
01131 
01132         return new_c;
01133 }
01134 
01135 static int add_cnk_list_entry(struct pr_chunk_x **list,
01136                                 int max_num, struct pr_chunk *chunk) {
01137         struct pr_chunk_x *l;
01138         struct pr_chunk **c;
01139         int max;
01140         int cnum;
01141         int i, pos;
01142 
01143         if (chunk->num > max_num) {
01144                 max = chunk->num;
01145         
01146                 if (*list == NULL) {
01147                         l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
01148                         pos = 0;
01149                 } else {
01150                         l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
01151                         pos = max_num;
01152                 }
01153                 if (l == NULL) {
01154                         for (i = 0; i < max; i++) {
01155                                 if ((*list)[i].chunks) free((*list)[i].chunks);
01156                         }
01157                         return 0;
01158                 }
01159                 for (i = pos; i < max; i++) {
01160                         l[i].chunks = NULL;
01161                         l[i].num = 0;
01162                 }
01163         } else {
01164                 l = *list;
01165                 max = max_num;
01166         }
01167 
01168         i = chunk->num - 1;
01169         cnum = l[i].num + 1;
01170         if (l[i].chunks == NULL) {
01171                 c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum); 
01172         } else {
01173                 c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
01174         }
01175         if (c == NULL) {
01176                 for (i = 0; i < max; i++) {
01177                         if (l[i].chunks) free(l[i].chunks);
01178                 }
01179                 return 0;
01180         }
01181         c[l[i].num] = chunk;
01182         l[i].chunks = c;
01183         l[i].num = cnum;
01184 
01185         *list = l;
01186         return max;
01187 }
01188 
01189  int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
01190 {
01191         return dopr(str, count, fmt, args);
01192 }
01193 #endif
01194 
01195 /* yes this really must be a ||. Don't muck with this (tridge)
01196  *
01197  * The logic for these two is that we need our own definition if the
01198  * OS *either* has no definition of *sprintf, or if it does have one
01199  * that doesn't work properly according to the autoconf test.
01200  */
01201 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
01202  int snprintf(char *str,size_t count,const char *fmt,...)
01203 {
01204         size_t ret;
01205         va_list ap;
01206     
01207         va_start(ap, fmt);
01208         ret = vsnprintf(str, count, fmt, ap);
01209         va_end(ap);
01210         return ret;
01211 }
01212 #endif
01213 
01214 #ifndef HAVE_C99_VSNPRINTF
01215  int printf(const char *fmt, ...)
01216 {
01217         va_list ap;
01218         int ret;
01219         char *s;
01220 
01221         s = NULL;
01222         va_start(ap, fmt);
01223         ret = vasprintf(&s, fmt, ap);
01224         va_end(ap);
01225 
01226         if (s) {
01227                 fwrite(s, 1, strlen(s), stdout);
01228         }
01229         free(s);
01230 
01231         return ret;
01232 }
01233 #endif
01234 
01235 #ifndef HAVE_C99_VSNPRINTF
01236  int fprintf(FILE *stream, const char *fmt, ...)
01237 {
01238         va_list ap;
01239         int ret;
01240         char *s;
01241 
01242         s = NULL;
01243         va_start(ap, fmt);
01244         ret = vasprintf(&s, fmt, ap);
01245         va_end(ap);
01246 
01247         if (s) {
01248                 fwrite(s, 1, strlen(s), stream);
01249         }
01250         free(s);
01251 
01252         return ret;
01253 }
01254 #endif
01255 
01256 #endif 
01257 
01258 #ifndef HAVE_VASPRINTF
01259  int vasprintf(char **ptr, const char *format, va_list ap)
01260 {
01261         int ret;
01262         va_list ap2;
01263 
01264         VA_COPY(ap2, ap);
01265         ret = vsnprintf(NULL, 0, format, ap2);
01266         va_end(ap2);
01267         if (ret <= 0) return ret;
01268 
01269         (*ptr) = (char *)malloc(ret+1);
01270         if (!*ptr) return -1;
01271 
01272         VA_COPY(ap2, ap);
01273         ret = vsnprintf(*ptr, ret+1, format, ap2);
01274         va_end(ap2);
01275 
01276         return ret;
01277 }
01278 #endif
01279 
01280 
01281 #ifndef HAVE_ASPRINTF
01282  int asprintf(char **ptr, const char *format, ...)
01283 {
01284         va_list ap;
01285         int ret;
01286         
01287         *ptr = NULL;
01288         va_start(ap, format);
01289         ret = vasprintf(ptr, format, ap);
01290         va_end(ap);
01291 
01292         return ret;
01293 }
01294 #endif
01295 
01296 #ifdef TEST_SNPRINTF
01297 
01298  int sprintf(char *str,const char *fmt,...);
01299  int printf(const char *fmt,...);
01300 
01301  int main (void)
01302 {
01303         char buf1[1024];
01304         char buf2[1024];
01305         char *buf3;
01306         char *fp_fmt[] = {
01307                 "%1.1f",
01308                 "%-1.5f",
01309                 "%1.5f",
01310                 "%123.9f",
01311                 "%10.5f",
01312                 "% 10.5f",
01313                 "%+22.9f",
01314                 "%+4.9f",
01315                 "%01.3f",
01316                 "%4f",
01317                 "%3.1f",
01318                 "%3.2f",
01319                 "%.0f",
01320                 "%f",
01321                 "%-8.8f",
01322                 "%-9.9f",
01323                 NULL
01324         };
01325         double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996, 
01326                              0.9996, 1.996, 4.136, 5.030201, 0.00205,
01327                              /* END LIST */ 0};
01328         char *int_fmt[] = {
01329                 "%-1.5d",
01330                 "%1.5d",
01331                 "%123.9d",
01332                 "%5.5d",
01333                 "%10.5d",
01334                 "% 10.5d",
01335                 "%+22.33d",
01336                 "%01.3d",
01337                 "%4d",
01338                 "%d",
01339                 NULL
01340         };
01341         long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
01342         char *str_fmt[] = {
01343                 "%10.5s",
01344                 "%-10.5s",
01345                 "%5.10s",
01346                 "%-5.10s",
01347                 "%10.1s",
01348                 "%0.10s",
01349                 "%10.0s",
01350                 "%1.10s",
01351                 "%s",
01352                 "%.1s",
01353                 "%.10s",
01354                 "%10s",
01355                 NULL
01356         };
01357         char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
01358 #ifdef HAVE_LONG_LONG
01359         char *ll_fmt[] = {
01360                 "%llu",
01361                 NULL
01362         };
01363         LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
01364 #endif
01365         int x, y;
01366         int fail = 0;
01367         int num = 0;
01368         int l1, l2;
01369         char *ss_fmt[] = {
01370                 "%zd",
01371                 "%zu",
01372                 NULL
01373         };
01374         size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
01375 
01376         printf ("Testing snprintf format codes against system sprintf...\n");
01377 
01378         for (x = 0; fp_fmt[x] ; x++) {
01379                 for (y = 0; fp_nums[y] != 0 ; y++) {
01380                         buf1[0] = buf2[0] = '\0';
01381                         l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
01382                         l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
01383                         buf1[1023] = buf2[1023] = '\0';
01384                         if (strcmp (buf1, buf2) || (l1 != l2)) {
01385                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
01386                                        fp_fmt[x], l1, buf1, l2, buf2);
01387                                 fail++;
01388                         }
01389                         num++;
01390                 }
01391         }
01392 
01393         for (x = 0; int_fmt[x] ; x++) {
01394                 for (y = 0; int_nums[y] != 0 ; y++) {
01395                         buf1[0] = buf2[0] = '\0';
01396                         l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
01397                         l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
01398                         buf1[1023] = buf2[1023] = '\0';
01399                         if (strcmp (buf1, buf2) || (l1 != l2)) {
01400                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
01401                                        int_fmt[x], l1, buf1, l2, buf2);
01402                                 fail++;
01403                         }
01404                         num++;
01405                 }
01406         }
01407 
01408         for (x = 0; str_fmt[x] ; x++) {
01409                 for (y = 0; str_vals[y] != 0 ; y++) {
01410                         buf1[0] = buf2[0] = '\0';
01411                         l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
01412                         l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
01413                         buf1[1023] = buf2[1023] = '\0';
01414                         if (strcmp (buf1, buf2) || (l1 != l2)) {
01415                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
01416                                        str_fmt[x], l1, buf1, l2, buf2);
01417                                 fail++;
01418                         }
01419                         num++;
01420                 }
01421         }
01422 
01423 #ifdef HAVE_LONG_LONG
01424         for (x = 0; ll_fmt[x] ; x++) {
01425                 for (y = 0; ll_nums[y] != 0 ; y++) {
01426                         buf1[0] = buf2[0] = '\0';
01427                         l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
01428                         l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
01429                         buf1[1023] = buf2[1023] = '\0';
01430                         if (strcmp (buf1, buf2) || (l1 != l2)) {
01431                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
01432                                        ll_fmt[x], l1, buf1, l2, buf2);
01433                                 fail++;
01434                         }
01435                         num++;
01436                 }
01437         }
01438 #endif
01439 
01440 #define BUFSZ 2048
01441 
01442         buf1[0] = buf2[0] = '\0';
01443         if ((buf3 = malloc(BUFSZ)) == NULL) {
01444                 fail++;
01445         } else {
01446                 num++;
01447                 memset(buf3, 'a', BUFSZ);
01448                 snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
01449                 buf1[1023] = '\0';
01450                 if (strcmp(buf1, "a") != 0) {
01451                         printf("length limit buf1 '%s' expected 'a'\n", buf1);
01452                         fail++;
01453                 }
01454         }
01455 
01456         buf1[0] = buf2[0] = '\0';
01457         l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
01458         l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
01459         buf1[1023] = buf2[1023] = '\0';
01460         if (strcmp(buf1, buf2) || (l1 != l2)) {
01461                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
01462                                 "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
01463                 fail++;
01464         }
01465 
01466         buf1[0] = buf2[0] = '\0';
01467         l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
01468         l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
01469         buf1[1023] = buf2[1023] = '\0';
01470         if (strcmp(buf1, buf2)) {
01471                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
01472                                 "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
01473                 fail++;
01474         }
01475 
01476         for (x = 0; ss_fmt[x] ; x++) {
01477                 for (y = 0; ss_nums[y] != 0 ; y++) {
01478                         buf1[0] = buf2[0] = '\0';
01479                         l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
01480                         l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
01481                         buf1[1023] = buf2[1023] = '\0';
01482                         if (strcmp (buf1, buf2) || (l1 != l2)) {
01483                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
01484                                        ss_fmt[x], l1, buf1, l2, buf2);
01485                                 fail++;
01486                         }
01487                         num++;
01488                 }
01489         }
01490 #if 0
01491         buf1[0] = buf2[0] = '\0';
01492         l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
01493         l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
01494         buf1[1023] = buf2[1023] = '\0';
01495         if (strcmp(buf1, buf2)) {
01496                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
01497                                 "%lld", l1, buf1, l2, buf2);
01498                 fail++;
01499         }
01500 
01501         buf1[0] = buf2[0] = '\0';
01502         l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
01503         l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
01504         buf1[1023] = buf2[1023] = '\0';
01505         if (strcmp(buf1, buf2)) {
01506                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
01507                                 "%Lf", l1, buf1, l2, buf2);
01508                 fail++;
01509         }
01510 #endif
01511         printf ("%d tests failed out of %d.\n", fail, num);
01512 
01513         printf("seeing how many digits we support\n");
01514         {
01515                 double v0 = 0.12345678901234567890123456789012345678901;
01516                 for (x=0; x<100; x++) {
01517                         double p = pow(10, x); 
01518                         double r = v0*p;
01519                         snprintf(buf1, sizeof(buf1), "%1.1f", r);
01520                         sprintf(buf2,                "%1.1f", r);
01521                         if (strcmp(buf1, buf2)) {
01522                                 printf("we seem to support %d digits\n", x-1);
01523                                 break;
01524                         }
01525                 }
01526         }
01527 
01528         return 0;
01529 }
01530 #endif /* TEST_SNPRINTF */

Sambaに対してSat Aug 29 21:22:58 2009に生成されました。  doxygen 1.4.7