Header And Logo

PostgreSQL
| The world's most advanced open source database.

snprintf.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1983, 1995, 1996 Eric P. Allman
00003  * Copyright (c) 1988, 1993
00004  *  The Regents of the University of California.  All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the University nor the names of its contributors
00015  *    may be used to endorse or promote products derived from this software
00016  *    without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00019  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00022  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00023  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00024  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00025  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00026  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00027  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * src/port/snprintf.c
00031  */
00032 
00033 #include "c.h"
00034 
00035 #include <limits.h>
00036 #ifndef WIN32
00037 #include <sys/ioctl.h>
00038 #endif
00039 #include <sys/param.h>
00040 
00041 #ifndef NL_ARGMAX
00042 #define NL_ARGMAX 16
00043 #endif
00044 
00045 
00046 /*
00047  *  SNPRINTF, VSNPRINTF and friends
00048  *
00049  * These versions have been grabbed off the net.  They have been
00050  * cleaned up to compile properly and support for most of the Single Unix
00051  * Specification has been added.  Remaining unimplemented features are:
00052  *
00053  * 1. No locale support: the radix character is always '.' and the '
00054  * (single quote) format flag is ignored.
00055  *
00056  * 2. No support for the "%n" format specification.
00057  *
00058  * 3. No support for wide characters ("lc" and "ls" formats).
00059  *
00060  * 4. No support for "long double" ("Lf" and related formats).
00061  *
00062  * 5. Space and '#' flags are not implemented.
00063  *
00064  *
00065  * The result values of these functions are not the same across different
00066  * platforms.  This implementation is compatible with the Single Unix Spec:
00067  *
00068  * 1. -1 is returned only if processing is abandoned due to an invalid
00069  * parameter, such as incorrect format string.  (Although not required by
00070  * the spec, this happens only when no characters have yet been transmitted
00071  * to the destination.)
00072  *
00073  * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0;
00074  * no data has been stored.
00075  *
00076  * 3. Otherwise, the number of bytes actually transmitted to the destination
00077  * is returned (excluding the trailing '\0' for snprintf and sprintf).
00078  *
00079  * For snprintf with nonzero count, the result cannot be more than count-1
00080  * (a trailing '\0' is always stored); it is not possible to distinguish
00081  * buffer overrun from exact fit.  This is unlike some implementations that
00082  * return the number of bytes that would have been needed for the complete
00083  * result string.
00084  */
00085 
00086 /**************************************************************
00087  * Original:
00088  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
00089  * A bombproof version of doprnt (dopr) included.
00090  * Sigh.  This sort of thing is always nasty do deal with.  Note that
00091  * the version here does not include floating point. (now it does ... tgl)
00092  **************************************************************/
00093 
00094 /* Prevent recursion */
00095 #undef  vsnprintf
00096 #undef  snprintf
00097 #undef  sprintf
00098 #undef  vfprintf
00099 #undef  fprintf
00100 #undef  printf
00101 
00102 /* Info about where the formatted output is going */
00103 typedef struct
00104 {
00105     char       *bufptr;         /* next buffer output position */
00106     char       *bufstart;       /* first buffer element */
00107     char       *bufend;         /* last buffer element, or NULL */
00108     /* bufend == NULL is for sprintf, where we assume buf is big enough */
00109     FILE       *stream;         /* eventual output destination, or NULL */
00110     int         nchars;         /* # chars already sent to stream */
00111 } PrintfTarget;
00112 
00113 /*
00114  * Info about the type and value of a formatting parameter.  Note that we
00115  * don't currently support "long double", "wint_t", or "wchar_t *" data,
00116  * nor the '%n' formatting code; else we'd need more types.  Also, at this
00117  * level we need not worry about signed vs unsigned values.
00118  */
00119 typedef enum
00120 {
00121     ATYPE_NONE = 0,
00122     ATYPE_INT,
00123     ATYPE_LONG,
00124     ATYPE_LONGLONG,
00125     ATYPE_DOUBLE,
00126     ATYPE_CHARPTR
00127 } PrintfArgType;
00128 
00129 typedef union
00130 {
00131     int         i;
00132     long        l;
00133     int64       ll;
00134     double      d;
00135     char       *cptr;
00136 } PrintfArgValue;
00137 
00138 
00139 static void flushbuffer(PrintfTarget *target);
00140 static int  dopr(PrintfTarget *target, const char *format, va_list args);
00141 
00142 
00143 int
00144 pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
00145 {
00146     PrintfTarget target;
00147 
00148     if (str == NULL || count == 0)
00149         return 0;
00150     target.bufstart = target.bufptr = str;
00151     target.bufend = str + count - 1;
00152     target.stream = NULL;
00153     /* target.nchars is unused in this case */
00154     if (dopr(&target, fmt, args))
00155     {
00156         *(target.bufptr) = '\0';
00157         errno = EINVAL;         /* bad format */
00158         return -1;
00159     }
00160     *(target.bufptr) = '\0';
00161     return target.bufptr - target.bufstart;
00162 }
00163 
00164 int
00165 pg_snprintf(char *str, size_t count, const char *fmt,...)
00166 {
00167     int         len;
00168     va_list     args;
00169 
00170     va_start(args, fmt);
00171     len = pg_vsnprintf(str, count, fmt, args);
00172     va_end(args);
00173     return len;
00174 }
00175 
00176 static int
00177 pg_vsprintf(char *str, const char *fmt, va_list args)
00178 {
00179     PrintfTarget target;
00180 
00181     if (str == NULL)
00182         return 0;
00183     target.bufstart = target.bufptr = str;
00184     target.bufend = NULL;
00185     target.stream = NULL;
00186     /* target.nchars is unused in this case */
00187     if (dopr(&target, fmt, args))
00188     {
00189         *(target.bufptr) = '\0';
00190         errno = EINVAL;         /* bad format */
00191         return -1;
00192     }
00193     *(target.bufptr) = '\0';
00194     return target.bufptr - target.bufstart;
00195 }
00196 
00197 int
00198 pg_sprintf(char *str, const char *fmt,...)
00199 {
00200     int         len;
00201     va_list     args;
00202 
00203     va_start(args, fmt);
00204     len = pg_vsprintf(str, fmt, args);
00205     va_end(args);
00206     return len;
00207 }
00208 
00209 int
00210 pg_vfprintf(FILE *stream, const char *fmt, va_list args)
00211 {
00212     PrintfTarget target;
00213     char        buffer[1024];   /* size is arbitrary */
00214 
00215     if (stream == NULL)
00216     {
00217         errno = EINVAL;
00218         return -1;
00219     }
00220     target.bufstart = target.bufptr = buffer;
00221     target.bufend = buffer + sizeof(buffer) - 1;
00222     target.stream = stream;
00223     target.nchars = 0;
00224     if (dopr(&target, fmt, args))
00225     {
00226         errno = EINVAL;         /* bad format */
00227         return -1;
00228     }
00229     /* dump any remaining buffer contents */
00230     flushbuffer(&target);
00231     return target.nchars;
00232 }
00233 
00234 int
00235 pg_fprintf(FILE *stream, const char *fmt,...)
00236 {
00237     int         len;
00238     va_list     args;
00239 
00240     va_start(args, fmt);
00241     len = pg_vfprintf(stream, fmt, args);
00242     va_end(args);
00243     return len;
00244 }
00245 
00246 int
00247 pg_printf(const char *fmt,...)
00248 {
00249     int         len;
00250     va_list     args;
00251 
00252     va_start(args, fmt);
00253     len = pg_vfprintf(stdout, fmt, args);
00254     va_end(args);
00255     return len;
00256 }
00257 
00258 /* call this only when stream is defined */
00259 static void
00260 flushbuffer(PrintfTarget *target)
00261 {
00262     size_t      nc = target->bufptr - target->bufstart;
00263 
00264     if (nc > 0)
00265         target->nchars += fwrite(target->bufstart, 1, nc, target->stream);
00266     target->bufptr = target->bufstart;
00267 }
00268 
00269 
00270 static void fmtstr(char *value, int leftjust, int minlen, int maxwidth,
00271        int pointflag, PrintfTarget *target);
00272 static void fmtptr(void *value, PrintfTarget *target);
00273 static void fmtint(int64 value, char type, int forcesign,
00274        int leftjust, int minlen, int zpad, int precision, int pointflag,
00275        PrintfTarget *target);
00276 static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
00277 static void fmtfloat(double value, char type, int forcesign,
00278          int leftjust, int minlen, int zpad, int precision, int pointflag,
00279          PrintfTarget *target);
00280 static void dostr(const char *str, int slen, PrintfTarget *target);
00281 static void dopr_outch(int c, PrintfTarget *target);
00282 static int  adjust_sign(int is_negative, int forcesign, int *signvalue);
00283 static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen);
00284 static void leading_pad(int zpad, int *signvalue, int *padlen,
00285             PrintfTarget *target);
00286 static void trailing_pad(int *padlen, PrintfTarget *target);
00287 
00288 
00289 /*
00290  * dopr(): poor man's version of doprintf
00291  */
00292 static int
00293 dopr(PrintfTarget *target, const char *format, va_list args)
00294 {
00295     const char *format_start = format;
00296     int         ch;
00297     bool        have_dollar;
00298     bool        have_non_dollar;
00299     bool        have_star;
00300     bool        afterstar;
00301     int         accum;
00302     int         longlongflag;
00303     int         longflag;
00304     int         pointflag;
00305     int         leftjust;
00306     int         fieldwidth;
00307     int         precision;
00308     int         zpad;
00309     int         forcesign;
00310     int         last_dollar;
00311     int         fmtpos;
00312     int         cvalue;
00313     int64       numvalue;
00314     double      fvalue;
00315     char       *strvalue;
00316     int         i;
00317     PrintfArgType argtypes[NL_ARGMAX + 1];
00318     PrintfArgValue argvalues[NL_ARGMAX + 1];
00319 
00320     /*
00321      * Parse the format string to determine whether there are %n$ format
00322      * specs, and identify the types and order of the format parameters.
00323      */
00324     have_dollar = have_non_dollar = false;
00325     last_dollar = 0;
00326     MemSet(argtypes, 0, sizeof(argtypes));
00327 
00328     while ((ch = *format++) != '\0')
00329     {
00330         if (ch != '%')
00331             continue;
00332         longflag = longlongflag = pointflag = 0;
00333         fmtpos = accum = 0;
00334         afterstar = false;
00335 nextch1:
00336         ch = *format++;
00337         if (ch == '\0')
00338             break;              /* illegal, but we don't complain */
00339         switch (ch)
00340         {
00341             case '-':
00342             case '+':
00343                 goto nextch1;
00344             case '0':
00345             case '1':
00346             case '2':
00347             case '3':
00348             case '4':
00349             case '5':
00350             case '6':
00351             case '7':
00352             case '8':
00353             case '9':
00354                 accum = accum * 10 + (ch - '0');
00355                 goto nextch1;
00356             case '.':
00357                 pointflag = 1;
00358                 accum = 0;
00359                 goto nextch1;
00360             case '*':
00361                 if (afterstar)
00362                     have_non_dollar = true;     /* multiple stars */
00363                 afterstar = true;
00364                 accum = 0;
00365                 goto nextch1;
00366             case '$':
00367                 have_dollar = true;
00368                 if (accum <= 0 || accum > NL_ARGMAX)
00369                     return -1;
00370                 if (afterstar)
00371                 {
00372                     if (argtypes[accum] &&
00373                         argtypes[accum] != ATYPE_INT)
00374                         return -1;
00375                     argtypes[accum] = ATYPE_INT;
00376                     last_dollar = Max(last_dollar, accum);
00377                     afterstar = false;
00378                 }
00379                 else
00380                     fmtpos = accum;
00381                 accum = 0;
00382                 goto nextch1;
00383             case 'l':
00384                 if (longflag)
00385                     longlongflag = 1;
00386                 else
00387                     longflag = 1;
00388                 goto nextch1;
00389             case 'h':
00390             case '\'':
00391                 /* ignore these */
00392                 goto nextch1;
00393             case 'd':
00394             case 'i':
00395             case 'o':
00396             case 'u':
00397             case 'x':
00398             case 'X':
00399                 if (fmtpos)
00400                 {
00401                     PrintfArgType atype;
00402 
00403                     if (longlongflag)
00404                         atype = ATYPE_LONGLONG;
00405                     else if (longflag)
00406                         atype = ATYPE_LONG;
00407                     else
00408                         atype = ATYPE_INT;
00409                     if (argtypes[fmtpos] &&
00410                         argtypes[fmtpos] != atype)
00411                         return -1;
00412                     argtypes[fmtpos] = atype;
00413                     last_dollar = Max(last_dollar, fmtpos);
00414                 }
00415                 else
00416                     have_non_dollar = true;
00417                 break;
00418             case 'c':
00419                 if (fmtpos)
00420                 {
00421                     if (argtypes[fmtpos] &&
00422                         argtypes[fmtpos] != ATYPE_INT)
00423                         return -1;
00424                     argtypes[fmtpos] = ATYPE_INT;
00425                     last_dollar = Max(last_dollar, fmtpos);
00426                 }
00427                 else
00428                     have_non_dollar = true;
00429                 break;
00430             case 's':
00431             case 'p':
00432                 if (fmtpos)
00433                 {
00434                     if (argtypes[fmtpos] &&
00435                         argtypes[fmtpos] != ATYPE_CHARPTR)
00436                         return -1;
00437                     argtypes[fmtpos] = ATYPE_CHARPTR;
00438                     last_dollar = Max(last_dollar, fmtpos);
00439                 }
00440                 else
00441                     have_non_dollar = true;
00442                 break;
00443             case 'e':
00444             case 'E':
00445             case 'f':
00446             case 'g':
00447             case 'G':
00448                 if (fmtpos)
00449                 {
00450                     if (argtypes[fmtpos] &&
00451                         argtypes[fmtpos] != ATYPE_DOUBLE)
00452                         return -1;
00453                     argtypes[fmtpos] = ATYPE_DOUBLE;
00454                     last_dollar = Max(last_dollar, fmtpos);
00455                 }
00456                 else
00457                     have_non_dollar = true;
00458                 break;
00459             case '%':
00460                 break;
00461         }
00462 
00463         /*
00464          * If we finish the spec with afterstar still set, there's a
00465          * non-dollar star in there.
00466          */
00467         if (afterstar)
00468             have_non_dollar = true;
00469     }
00470 
00471     /* Per spec, you use either all dollar or all not. */
00472     if (have_dollar && have_non_dollar)
00473         return -1;
00474 
00475     /*
00476      * In dollar mode, collect the arguments in physical order.
00477      */
00478     for (i = 1; i <= last_dollar; i++)
00479     {
00480         switch (argtypes[i])
00481         {
00482             case ATYPE_NONE:
00483                 return -1;      /* invalid format */
00484             case ATYPE_INT:
00485                 argvalues[i].i = va_arg(args, int);
00486                 break;
00487             case ATYPE_LONG:
00488                 argvalues[i].l = va_arg(args, long);
00489                 break;
00490             case ATYPE_LONGLONG:
00491                 argvalues[i].ll = va_arg(args, int64);
00492                 break;
00493             case ATYPE_DOUBLE:
00494                 argvalues[i].d = va_arg(args, double);
00495                 break;
00496             case ATYPE_CHARPTR:
00497                 argvalues[i].cptr = va_arg(args, char *);
00498                 break;
00499         }
00500     }
00501 
00502     /*
00503      * At last we can parse the format for real.
00504      */
00505     format = format_start;
00506     while ((ch = *format++) != '\0')
00507     {
00508         if (ch != '%')
00509         {
00510             dopr_outch(ch, target);
00511             continue;
00512         }
00513         fieldwidth = precision = zpad = leftjust = forcesign = 0;
00514         longflag = longlongflag = pointflag = 0;
00515         fmtpos = accum = 0;
00516         have_star = afterstar = false;
00517 nextch2:
00518         ch = *format++;
00519         if (ch == '\0')
00520             break;              /* illegal, but we don't complain */
00521         switch (ch)
00522         {
00523             case '-':
00524                 leftjust = 1;
00525                 goto nextch2;
00526             case '+':
00527                 forcesign = 1;
00528                 goto nextch2;
00529             case '0':
00530                 /* set zero padding if no nonzero digits yet */
00531                 if (accum == 0 && !pointflag)
00532                     zpad = '0';
00533                 /* FALL THRU */
00534             case '1':
00535             case '2':
00536             case '3':
00537             case '4':
00538             case '5':
00539             case '6':
00540             case '7':
00541             case '8':
00542             case '9':
00543                 accum = accum * 10 + (ch - '0');
00544                 goto nextch2;
00545             case '.':
00546                 if (have_star)
00547                     have_star = false;
00548                 else
00549                     fieldwidth = accum;
00550                 pointflag = 1;
00551                 accum = 0;
00552                 goto nextch2;
00553             case '*':
00554                 if (have_dollar)
00555                 {
00556                     /* process value after reading n$ */
00557                     afterstar = true;
00558                 }
00559                 else
00560                 {
00561                     /* fetch and process value now */
00562                     int         starval = va_arg(args, int);
00563 
00564                     if (pointflag)
00565                     {
00566                         precision = starval;
00567                         if (precision < 0)
00568                         {
00569                             precision = 0;
00570                             pointflag = 0;
00571                         }
00572                     }
00573                     else
00574                     {
00575                         fieldwidth = starval;
00576                         if (fieldwidth < 0)
00577                         {
00578                             leftjust = 1;
00579                             fieldwidth = -fieldwidth;
00580                         }
00581                     }
00582                 }
00583                 have_star = true;
00584                 accum = 0;
00585                 goto nextch2;
00586             case '$':
00587                 if (afterstar)
00588                 {
00589                     /* fetch and process star value */
00590                     int         starval = argvalues[accum].i;
00591 
00592                     if (pointflag)
00593                     {
00594                         precision = starval;
00595                         if (precision < 0)
00596                         {
00597                             precision = 0;
00598                             pointflag = 0;
00599                         }
00600                     }
00601                     else
00602                     {
00603                         fieldwidth = starval;
00604                         if (fieldwidth < 0)
00605                         {
00606                             leftjust = 1;
00607                             fieldwidth = -fieldwidth;
00608                         }
00609                     }
00610                     afterstar = false;
00611                 }
00612                 else
00613                     fmtpos = accum;
00614                 accum = 0;
00615                 goto nextch2;
00616             case 'l':
00617                 if (longflag)
00618                     longlongflag = 1;
00619                 else
00620                     longflag = 1;
00621                 goto nextch2;
00622             case 'h':
00623             case '\'':
00624                 /* ignore these */
00625                 goto nextch2;
00626             case 'd':
00627             case 'i':
00628                 if (!have_star)
00629                 {
00630                     if (pointflag)
00631                         precision = accum;
00632                     else
00633                         fieldwidth = accum;
00634                 }
00635                 if (have_dollar)
00636                 {
00637                     if (longlongflag)
00638                         numvalue = argvalues[fmtpos].ll;
00639                     else if (longflag)
00640                         numvalue = argvalues[fmtpos].l;
00641                     else
00642                         numvalue = argvalues[fmtpos].i;
00643                 }
00644                 else
00645                 {
00646                     if (longlongflag)
00647                         numvalue = va_arg(args, int64);
00648                     else if (longflag)
00649                         numvalue = va_arg(args, long);
00650                     else
00651                         numvalue = va_arg(args, int);
00652                 }
00653                 fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
00654                        precision, pointflag, target);
00655                 break;
00656             case 'o':
00657             case 'u':
00658             case 'x':
00659             case 'X':
00660                 if (!have_star)
00661                 {
00662                     if (pointflag)
00663                         precision = accum;
00664                     else
00665                         fieldwidth = accum;
00666                 }
00667                 if (have_dollar)
00668                 {
00669                     if (longlongflag)
00670                         numvalue = (uint64) argvalues[fmtpos].ll;
00671                     else if (longflag)
00672                         numvalue = (unsigned long) argvalues[fmtpos].l;
00673                     else
00674                         numvalue = (unsigned int) argvalues[fmtpos].i;
00675                 }
00676                 else
00677                 {
00678                     if (longlongflag)
00679                         numvalue = (uint64) va_arg(args, int64);
00680                     else if (longflag)
00681                         numvalue = (unsigned long) va_arg(args, long);
00682                     else
00683                         numvalue = (unsigned int) va_arg(args, int);
00684                 }
00685                 fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
00686                        precision, pointflag, target);
00687                 break;
00688             case 'c':
00689                 if (!have_star)
00690                 {
00691                     if (pointflag)
00692                         precision = accum;
00693                     else
00694                         fieldwidth = accum;
00695                 }
00696                 if (have_dollar)
00697                     cvalue = (unsigned char) argvalues[fmtpos].i;
00698                 else
00699                     cvalue = (unsigned char) va_arg(args, int);
00700                 fmtchar(cvalue, leftjust, fieldwidth, target);
00701                 break;
00702             case 's':
00703                 if (!have_star)
00704                 {
00705                     if (pointflag)
00706                         precision = accum;
00707                     else
00708                         fieldwidth = accum;
00709                 }
00710                 if (have_dollar)
00711                     strvalue = argvalues[fmtpos].cptr;
00712                 else
00713                     strvalue = va_arg(args, char *);
00714                 fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
00715                        target);
00716                 break;
00717             case 'p':
00718                 /* fieldwidth/leftjust are ignored ... */
00719                 if (have_dollar)
00720                     strvalue = argvalues[fmtpos].cptr;
00721                 else
00722                     strvalue = va_arg(args, char *);
00723                 fmtptr((void *) strvalue, target);
00724                 break;
00725             case 'e':
00726             case 'E':
00727             case 'f':
00728             case 'g':
00729             case 'G':
00730                 if (!have_star)
00731                 {
00732                     if (pointflag)
00733                         precision = accum;
00734                     else
00735                         fieldwidth = accum;
00736                 }
00737                 if (have_dollar)
00738                     fvalue = argvalues[fmtpos].d;
00739                 else
00740                     fvalue = va_arg(args, double);
00741                 fmtfloat(fvalue, ch, forcesign, leftjust,
00742                          fieldwidth, zpad,
00743                          precision, pointflag,
00744                          target);
00745                 break;
00746             case '%':
00747                 dopr_outch('%', target);
00748                 break;
00749         }
00750     }
00751 
00752     return 0;
00753 }
00754 
00755 static size_t
00756 pg_strnlen(const char *str, size_t maxlen)
00757 {
00758     const char *p = str;
00759 
00760     while (maxlen-- > 0 && *p)
00761         p++;
00762     return p - str;
00763 }
00764 
00765 static void
00766 fmtstr(char *value, int leftjust, int minlen, int maxwidth,
00767        int pointflag, PrintfTarget *target)
00768 {
00769     int         padlen,
00770                 vallen;         /* amount to pad */
00771 
00772     /*
00773      * If a maxwidth (precision) is specified, we must not fetch more bytes
00774      * than that.
00775      */
00776     if (pointflag)
00777         vallen = pg_strnlen(value, maxwidth);
00778     else
00779         vallen = strlen(value);
00780 
00781     adjust_padlen(minlen, vallen, leftjust, &padlen);
00782 
00783     while (padlen > 0)
00784     {
00785         dopr_outch(' ', target);
00786         --padlen;
00787     }
00788 
00789     dostr(value, vallen, target);
00790 
00791     trailing_pad(&padlen, target);
00792 }
00793 
00794 static void
00795 fmtptr(void *value, PrintfTarget *target)
00796 {
00797     int         vallen;
00798     char        convert[64];
00799 
00800     /* we rely on regular C library's sprintf to do the basic conversion */
00801     vallen = sprintf(convert, "%p", value);
00802 
00803     dostr(convert, vallen, target);
00804 }
00805 
00806 static void
00807 fmtint(int64 value, char type, int forcesign, int leftjust,
00808        int minlen, int zpad, int precision, int pointflag,
00809        PrintfTarget *target)
00810 {
00811     uint64      base;
00812     int         dosign;
00813     const char *cvt = "0123456789abcdef";
00814     int         signvalue = 0;
00815     char        convert[64];
00816     int         vallen = 0;
00817     int         padlen = 0;     /* amount to pad */
00818     int         zeropad;        /* extra leading zeroes */
00819 
00820     switch (type)
00821     {
00822         case 'd':
00823         case 'i':
00824             base = 10;
00825             dosign = 1;
00826             break;
00827         case 'o':
00828             base = 8;
00829             dosign = 0;
00830             break;
00831         case 'u':
00832             base = 10;
00833             dosign = 0;
00834             break;
00835         case 'x':
00836             base = 16;
00837             dosign = 0;
00838             break;
00839         case 'X':
00840             cvt = "0123456789ABCDEF";
00841             base = 16;
00842             dosign = 0;
00843             break;
00844         default:
00845             return;             /* keep compiler quiet */
00846     }
00847 
00848     /* Handle +/- */
00849     if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
00850         value = -value;
00851 
00852     /*
00853      * SUS: the result of converting 0 with an explicit precision of 0 is no
00854      * characters
00855      */
00856     if (value == 0 && pointflag && precision == 0)
00857         vallen = 0;
00858     else
00859     {
00860         /* make integer string */
00861         uint64      uvalue = (uint64) value;
00862 
00863         do
00864         {
00865             convert[vallen++] = cvt[uvalue % base];
00866             uvalue = uvalue / base;
00867         } while (uvalue);
00868     }
00869 
00870     zeropad = Max(0, precision - vallen);
00871 
00872     adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
00873 
00874     leading_pad(zpad, &signvalue, &padlen, target);
00875 
00876     while (zeropad-- > 0)
00877         dopr_outch('0', target);
00878 
00879     while (vallen > 0)
00880         dopr_outch(convert[--vallen], target);
00881 
00882     trailing_pad(&padlen, target);
00883 }
00884 
00885 static void
00886 fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
00887 {
00888     int         padlen = 0;     /* amount to pad */
00889 
00890     adjust_padlen(minlen, 1, leftjust, &padlen);
00891 
00892     while (padlen > 0)
00893     {
00894         dopr_outch(' ', target);
00895         --padlen;
00896     }
00897 
00898     dopr_outch(value, target);
00899 
00900     trailing_pad(&padlen, target);
00901 }
00902 
00903 static void
00904 fmtfloat(double value, char type, int forcesign, int leftjust,
00905          int minlen, int zpad, int precision, int pointflag,
00906          PrintfTarget *target)
00907 {
00908     int         signvalue = 0;
00909     int         vallen;
00910     char        fmt[32];
00911     char        convert[512];
00912     int         padlen = 0;     /* amount to pad */
00913 
00914     /* we rely on regular C library's sprintf to do the basic conversion */
00915     if (pointflag)
00916         sprintf(fmt, "%%.%d%c", precision, type);
00917     else
00918         sprintf(fmt, "%%%c", type);
00919 
00920     if (adjust_sign((value < 0), forcesign, &signvalue))
00921         value = -value;
00922 
00923     vallen = sprintf(convert, fmt, value);
00924 
00925     adjust_padlen(minlen, vallen, leftjust, &padlen);
00926 
00927     leading_pad(zpad, &signvalue, &padlen, target);
00928 
00929     dostr(convert, vallen, target);
00930 
00931     trailing_pad(&padlen, target);
00932 }
00933 
00934 static void
00935 dostr(const char *str, int slen, PrintfTarget *target)
00936 {
00937     while (slen > 0)
00938     {
00939         int         avail;
00940 
00941         if (target->bufend != NULL)
00942             avail = target->bufend - target->bufptr;
00943         else
00944             avail = slen;
00945         if (avail <= 0)
00946         {
00947             /* buffer full, can we dump to stream? */
00948             if (target->stream == NULL)
00949                 return;         /* no, lose the data */
00950             flushbuffer(target);
00951             continue;
00952         }
00953         avail = Min(avail, slen);
00954         memmove(target->bufptr, str, avail);
00955         target->bufptr += avail;
00956         str += avail;
00957         slen -= avail;
00958     }
00959 }
00960 
00961 static void
00962 dopr_outch(int c, PrintfTarget *target)
00963 {
00964     if (target->bufend != NULL && target->bufptr >= target->bufend)
00965     {
00966         /* buffer full, can we dump to stream? */
00967         if (target->stream == NULL)
00968             return;             /* no, lose the data */
00969         flushbuffer(target);
00970     }
00971     *(target->bufptr++) = c;
00972 }
00973 
00974 
00975 static int
00976 adjust_sign(int is_negative, int forcesign, int *signvalue)
00977 {
00978     if (is_negative)
00979     {
00980         *signvalue = '-';
00981         return true;
00982     }
00983     else if (forcesign)
00984         *signvalue = '+';
00985     return false;
00986 }
00987 
00988 
00989 static void
00990 adjust_padlen(int minlen, int vallen, int leftjust, int *padlen)
00991 {
00992     *padlen = minlen - vallen;
00993     if (*padlen < 0)
00994         *padlen = 0;
00995     if (leftjust)
00996         *padlen = -(*padlen);
00997 }
00998 
00999 
01000 static void
01001 leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
01002 {
01003     if (*padlen > 0 && zpad)
01004     {
01005         if (*signvalue)
01006         {
01007             dopr_outch(*signvalue, target);
01008             --(*padlen);
01009             *signvalue = 0;
01010         }
01011         while (*padlen > 0)
01012         {
01013             dopr_outch(zpad, target);
01014             --(*padlen);
01015         }
01016     }
01017     while (*padlen > (*signvalue != 0))
01018     {
01019         dopr_outch(' ', target);
01020         --(*padlen);
01021     }
01022     if (*signvalue)
01023     {
01024         dopr_outch(*signvalue, target);
01025         if (*padlen > 0)
01026             --(*padlen);
01027         else if (*padlen < 0)
01028             ++(*padlen);
01029     }
01030 }
01031 
01032 
01033 static void
01034 trailing_pad(int *padlen, PrintfTarget *target)
01035 {
01036     while (*padlen < 0)
01037     {
01038         dopr_outch(' ', target);
01039         ++(*padlen);
01040     }
01041 }