Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

snprintf.cc

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

Generated on Fri May 7 21:15:43 2004 by doxygen 1.3.5