quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

mprintf.c (32257B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  */
     24 
     25 #include "curl_setup.h"
     26 #include "curlx/dynbuf.h"
     27 #include "curl_printf.h"
     28 #include "curlx/strparse.h"
     29 
     30 #include "curl_memory.h"
     31 /* The last #include file should be: */
     32 #include "memdebug.h"
     33 
     34 #ifdef HAVE_LONGLONG
     35 #  define LONG_LONG_TYPE long long
     36 #  define HAVE_LONG_LONG_TYPE
     37 #elif defined(_MSC_VER)
     38 #  define LONG_LONG_TYPE __int64
     39 #  define HAVE_LONG_LONG_TYPE
     40 #else
     41 #  undef LONG_LONG_TYPE
     42 #  undef HAVE_LONG_LONG_TYPE
     43 #endif
     44 
     45 /*
     46  * Max integer data types that mprintf.c is capable
     47  */
     48 
     49 #ifdef HAVE_LONG_LONG_TYPE
     50 #  define mp_intmax_t LONG_LONG_TYPE
     51 #  define mp_uintmax_t unsigned LONG_LONG_TYPE
     52 #else
     53 #  define mp_intmax_t long
     54 #  define mp_uintmax_t unsigned long
     55 #endif
     56 
     57 #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
     58                         fit negative DBL_MAX (317 letters) */
     59 #define MAX_PARAMETERS 128 /* number of input arguments */
     60 #define MAX_SEGMENTS   128 /* number of output segments */
     61 
     62 #ifdef __AMIGA__
     63 # undef FORMAT_INT
     64 #endif
     65 
     66 /* Lower-case digits.  */
     67 const unsigned char Curl_ldigits[] = "0123456789abcdef";
     68 
     69 /* Upper-case digits.  */
     70 const unsigned char Curl_udigits[] = "0123456789ABCDEF";
     71 
     72 #define OUTCHAR(x)                                       \
     73   do {                                                   \
     74     if(stream((unsigned char)x, userp))                  \
     75       return TRUE;                                       \
     76     (*donep)++;                                          \
     77   } while(0)
     78 
     79 /* Data type to read from the arglist */
     80 typedef enum {
     81   FORMAT_STRING,
     82   FORMAT_PTR,
     83   FORMAT_INTPTR,
     84   FORMAT_INT,
     85   FORMAT_LONG,
     86   FORMAT_LONGLONG,
     87   FORMAT_INTU,
     88   FORMAT_LONGU,
     89   FORMAT_LONGLONGU,
     90   FORMAT_DOUBLE,
     91   FORMAT_LONGDOUBLE,
     92   FORMAT_WIDTH,
     93   FORMAT_PRECISION
     94 } FormatType;
     95 
     96 /* conversion and display flags */
     97 enum {
     98   FLAGS_SPACE      = 1 << 0,
     99   FLAGS_SHOWSIGN   = 1 << 1,
    100   FLAGS_LEFT       = 1 << 2,
    101   FLAGS_ALT        = 1 << 3,
    102   FLAGS_SHORT      = 1 << 4,
    103   FLAGS_LONG       = 1 << 5,
    104   FLAGS_LONGLONG   = 1 << 6,
    105   FLAGS_LONGDOUBLE = 1 << 7,
    106   FLAGS_PAD_NIL    = 1 << 8,
    107   FLAGS_UNSIGNED   = 1 << 9,
    108   FLAGS_OCTAL      = 1 << 10,
    109   FLAGS_HEX        = 1 << 11,
    110   FLAGS_UPPER      = 1 << 12,
    111   FLAGS_WIDTH      = 1 << 13, /* '*' or '*<num>$' used */
    112   FLAGS_WIDTHPARAM = 1 << 14, /* width PARAMETER was specified */
    113   FLAGS_PREC       = 1 << 15, /* precision was specified */
    114   FLAGS_PRECPARAM  = 1 << 16, /* precision PARAMETER was specified */
    115   FLAGS_CHAR       = 1 << 17, /* %c story */
    116   FLAGS_FLOATE     = 1 << 18, /* %e or %E */
    117   FLAGS_FLOATG     = 1 << 19, /* %g or %G */
    118   FLAGS_SUBSTR     = 1 << 20  /* no input, only substring */
    119 };
    120 
    121 enum {
    122   DOLLAR_UNKNOWN,
    123   DOLLAR_NOPE,
    124   DOLLAR_USE
    125 };
    126 
    127 /*
    128  * Describes an input va_arg type and hold its value.
    129  */
    130 struct va_input {
    131   FormatType type; /* FormatType */
    132   union {
    133     const char *str;
    134     void *ptr;
    135     mp_intmax_t nums; /* signed */
    136     mp_uintmax_t numu; /* unsigned */
    137     double dnum;
    138   } val;
    139 };
    140 
    141 /*
    142  * Describes an output segment.
    143  */
    144 struct outsegment {
    145   int width;     /* width OR width parameter number */
    146   int precision; /* precision OR precision parameter number */
    147   unsigned int flags;
    148   unsigned int input; /* input argument array index */
    149   const char *start; /* format string start to output */
    150   size_t outlen;     /* number of bytes from the format string to output */
    151 };
    152 
    153 struct nsprintf {
    154   char *buffer;
    155   size_t length;
    156   size_t max;
    157 };
    158 
    159 struct asprintf {
    160   struct dynbuf *b;
    161   char merr;
    162 };
    163 
    164 /* the provided input number is 1-based but this returns the number 0-based.
    165 
    166    returns -1 if no valid number was provided.
    167 */
    168 static int dollarstring(const char *p, const char **end)
    169 {
    170   curl_off_t num;
    171   if(curlx_str_number(&p, &num, MAX_PARAMETERS) ||
    172      curlx_str_single(&p, '$') || !num)
    173     return -1;
    174   *end = p;
    175   return (int)num - 1;
    176 }
    177 
    178 #define is_arg_used(x,y) ((x)[(y)/8] & (1 << ((y)&7)))
    179 #define mark_arg_used(x,y) ((x)[y/8] |= (unsigned char)(1 << ((y)&7)))
    180 
    181 /*
    182  * Parse the format string.
    183  *
    184  * Create two arrays. One describes the inputs, one describes the outputs.
    185  *
    186  * Returns zero on success.
    187  */
    188 
    189 #define PFMT_OK          0
    190 #define PFMT_DOLLAR      1 /* bad dollar for main param */
    191 #define PFMT_DOLLARWIDTH 2 /* bad dollar use for width */
    192 #define PFMT_DOLLARPREC  3 /* bad dollar use for precision */
    193 #define PFMT_MANYARGS    4 /* too many input arguments used */
    194 #define PFMT_PREC        5 /* precision overflow */
    195 #define PFMT_PRECMIX     6 /* bad mix of precision specifiers */
    196 #define PFMT_WIDTH       7 /* width overflow */
    197 #define PFMT_INPUTGAP    8 /* gap in arguments */
    198 #define PFMT_WIDTHARG    9 /* attempted to use same arg twice, for width */
    199 #define PFMT_PRECARG    10 /* attempted to use same arg twice, for prec */
    200 #define PFMT_MANYSEGS   11 /* maxed out output segments */
    201 
    202 static int parsefmt(const char *format,
    203                     struct outsegment *out,
    204                     struct va_input *in,
    205                     int *opieces,
    206                     int *ipieces, va_list arglist)
    207 {
    208   const char *fmt = format;
    209   int param_num = 0;
    210   int max_param = -1;
    211   int i;
    212   int ocount = 0;
    213   unsigned char usedinput[MAX_PARAMETERS/8];
    214   size_t outlen = 0;
    215   struct outsegment *optr;
    216   int use_dollar = DOLLAR_UNKNOWN;
    217   const char *start = fmt;
    218 
    219   /* clear, set a bit for each used input */
    220   memset(usedinput, 0, sizeof(usedinput));
    221 
    222   while(*fmt) {
    223     if(*fmt == '%') {
    224       struct va_input *iptr;
    225       bool loopit = TRUE;
    226       FormatType type;
    227       unsigned int flags = 0;
    228       int width = 0;
    229       int precision = 0;
    230       int param = -1;
    231       fmt++;
    232       outlen = (size_t)(fmt - start - 1);
    233       if(*fmt == '%') {
    234         /* this means a %% that should be output only as %. Create an output
    235            segment. */
    236         if(outlen) {
    237           optr = &out[ocount++];
    238           if(ocount > MAX_SEGMENTS)
    239             return PFMT_MANYSEGS;
    240           optr->input = 0;
    241           optr->flags = FLAGS_SUBSTR;
    242           optr->start = start;
    243           optr->outlen = outlen;
    244         }
    245         start = fmt;
    246         fmt++;
    247         continue; /* while */
    248       }
    249 
    250       if(use_dollar != DOLLAR_NOPE) {
    251         param = dollarstring(fmt, &fmt);
    252         if(param < 0) {
    253           if(use_dollar == DOLLAR_USE)
    254             /* illegal combo */
    255             return PFMT_DOLLAR;
    256 
    257           /* we got no positional, just get the next arg */
    258           param = -1;
    259           use_dollar = DOLLAR_NOPE;
    260         }
    261         else
    262           use_dollar = DOLLAR_USE;
    263       }
    264 
    265       /* Handle the flags */
    266       while(loopit) {
    267         switch(*fmt++) {
    268         case ' ':
    269           flags |= FLAGS_SPACE;
    270           break;
    271         case '+':
    272           flags |= FLAGS_SHOWSIGN;
    273           break;
    274         case '-':
    275           flags |= FLAGS_LEFT;
    276           flags &= ~(unsigned int)FLAGS_PAD_NIL;
    277           break;
    278         case '#':
    279           flags |= FLAGS_ALT;
    280           break;
    281         case '.':
    282           if('*' == *fmt) {
    283             /* The precision is picked from a specified parameter */
    284             flags |= FLAGS_PRECPARAM;
    285             fmt++;
    286 
    287             if(use_dollar == DOLLAR_USE) {
    288               precision = dollarstring(fmt, &fmt);
    289               if(precision < 0)
    290                 /* illegal combo */
    291                 return PFMT_DOLLARPREC;
    292             }
    293             else
    294               /* get it from the next argument */
    295               precision = -1;
    296           }
    297           else {
    298             bool is_neg;
    299             curl_off_t num;
    300             flags |= FLAGS_PREC;
    301             is_neg = ('-' == *fmt);
    302             if(is_neg)
    303               fmt++;
    304             if(curlx_str_number(&fmt, &num, INT_MAX))
    305               return PFMT_PREC;
    306             precision = (int)num;
    307             if(is_neg)
    308               precision = -precision;
    309           }
    310           if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
    311              (FLAGS_PREC | FLAGS_PRECPARAM))
    312             /* it is not permitted to use both kinds of precision for the same
    313                argument */
    314             return PFMT_PRECMIX;
    315           break;
    316         case 'h':
    317           flags |= FLAGS_SHORT;
    318           break;
    319 #ifdef _WIN32
    320         case 'I':
    321           /* Non-ANSI integer extensions I32 I64 */
    322           if((fmt[0] == '3') && (fmt[1] == '2')) {
    323             flags |= FLAGS_LONG;
    324             fmt += 2;
    325           }
    326           else if((fmt[0] == '6') && (fmt[1] == '4')) {
    327             flags |= FLAGS_LONGLONG;
    328             fmt += 2;
    329           }
    330           else {
    331 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
    332             flags |= FLAGS_LONGLONG;
    333 #else
    334             flags |= FLAGS_LONG;
    335 #endif
    336           }
    337           break;
    338 #endif /* _WIN32 */
    339         case 'l':
    340           if(flags & FLAGS_LONG)
    341             flags |= FLAGS_LONGLONG;
    342           else
    343             flags |= FLAGS_LONG;
    344           break;
    345         case 'L':
    346           flags |= FLAGS_LONGDOUBLE;
    347           break;
    348         case 'q':
    349           flags |= FLAGS_LONGLONG;
    350           break;
    351         case 'z':
    352           /* the code below generates a warning if -Wunreachable-code is
    353              used */
    354 #if (SIZEOF_SIZE_T > SIZEOF_LONG)
    355           flags |= FLAGS_LONGLONG;
    356 #else
    357           flags |= FLAGS_LONG;
    358 #endif
    359           break;
    360         case 'O':
    361 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
    362           flags |= FLAGS_LONGLONG;
    363 #else
    364           flags |= FLAGS_LONG;
    365 #endif
    366           break;
    367         case '0':
    368           if(!(flags & FLAGS_LEFT))
    369             flags |= FLAGS_PAD_NIL;
    370           FALLTHROUGH();
    371         case '1': case '2': case '3': case '4':
    372         case '5': case '6': case '7': case '8': case '9': {
    373           curl_off_t num;
    374           flags |= FLAGS_WIDTH;
    375           fmt--;
    376           if(curlx_str_number(&fmt, &num, INT_MAX))
    377             return PFMT_WIDTH;
    378           width = (int)num;
    379           break;
    380         }
    381         case '*':  /* read width from argument list */
    382           flags |= FLAGS_WIDTHPARAM;
    383           if(use_dollar == DOLLAR_USE) {
    384             width = dollarstring(fmt, &fmt);
    385             if(width < 0)
    386               /* illegal combo */
    387               return PFMT_DOLLARWIDTH;
    388           }
    389           else
    390             /* pick from the next argument */
    391             width = -1;
    392           break;
    393         default:
    394           loopit = FALSE;
    395           fmt--;
    396           break;
    397         } /* switch */
    398       } /* while */
    399 
    400       switch(*fmt) {
    401       case 'S':
    402         flags |= FLAGS_ALT;
    403         FALLTHROUGH();
    404       case 's':
    405         type = FORMAT_STRING;
    406         break;
    407       case 'n':
    408         type = FORMAT_INTPTR;
    409         break;
    410       case 'p':
    411         type = FORMAT_PTR;
    412         break;
    413       case 'd':
    414       case 'i':
    415         if(flags & FLAGS_LONGLONG)
    416           type = FORMAT_LONGLONG;
    417         else if(flags & FLAGS_LONG)
    418           type = FORMAT_LONG;
    419         else
    420           type = FORMAT_INT;
    421         break;
    422       case 'u':
    423         if(flags & FLAGS_LONGLONG)
    424           type = FORMAT_LONGLONGU;
    425         else if(flags & FLAGS_LONG)
    426           type = FORMAT_LONGU;
    427         else
    428           type = FORMAT_INTU;
    429         flags |= FLAGS_UNSIGNED;
    430         break;
    431       case 'o':
    432         if(flags & FLAGS_LONGLONG)
    433           type = FORMAT_LONGLONGU;
    434         else if(flags & FLAGS_LONG)
    435           type = FORMAT_LONGU;
    436         else
    437           type = FORMAT_INTU;
    438         flags |= FLAGS_OCTAL|FLAGS_UNSIGNED;
    439         break;
    440       case 'x':
    441         if(flags & FLAGS_LONGLONG)
    442           type = FORMAT_LONGLONGU;
    443         else if(flags & FLAGS_LONG)
    444           type = FORMAT_LONGU;
    445         else
    446           type = FORMAT_INTU;
    447         flags |= FLAGS_HEX|FLAGS_UNSIGNED;
    448         break;
    449       case 'X':
    450         if(flags & FLAGS_LONGLONG)
    451           type = FORMAT_LONGLONGU;
    452         else if(flags & FLAGS_LONG)
    453           type = FORMAT_LONGU;
    454         else
    455           type = FORMAT_INTU;
    456         flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
    457         break;
    458       case 'c':
    459         type = FORMAT_INT;
    460         flags |= FLAGS_CHAR;
    461         break;
    462       case 'f':
    463         type = FORMAT_DOUBLE;
    464         break;
    465       case 'e':
    466         type = FORMAT_DOUBLE;
    467         flags |= FLAGS_FLOATE;
    468         break;
    469       case 'E':
    470         type = FORMAT_DOUBLE;
    471         flags |= FLAGS_FLOATE|FLAGS_UPPER;
    472         break;
    473       case 'g':
    474         type = FORMAT_DOUBLE;
    475         flags |= FLAGS_FLOATG;
    476         break;
    477       case 'G':
    478         type = FORMAT_DOUBLE;
    479         flags |= FLAGS_FLOATG|FLAGS_UPPER;
    480         break;
    481       default:
    482         /* invalid instruction, disregard and continue */
    483         continue;
    484       } /* switch */
    485 
    486       if(flags & FLAGS_WIDTHPARAM) {
    487         if(width < 0)
    488           width = param_num++;
    489         else {
    490           /* if this identifies a parameter already used, this is illegal */
    491           if(is_arg_used(usedinput, width))
    492             return PFMT_WIDTHARG;
    493         }
    494         if(width >= MAX_PARAMETERS)
    495           return PFMT_MANYARGS;
    496         if(width >= max_param)
    497           max_param = width;
    498 
    499         in[width].type = FORMAT_WIDTH;
    500         /* mark as used */
    501         mark_arg_used(usedinput, width);
    502       }
    503 
    504       if(flags & FLAGS_PRECPARAM) {
    505         if(precision < 0)
    506           precision = param_num++;
    507         else {
    508           /* if this identifies a parameter already used, this is illegal */
    509           if(is_arg_used(usedinput, precision))
    510             return PFMT_PRECARG;
    511         }
    512         if(precision >= MAX_PARAMETERS)
    513           return PFMT_MANYARGS;
    514         if(precision >= max_param)
    515           max_param = precision;
    516 
    517         in[precision].type = FORMAT_PRECISION;
    518         mark_arg_used(usedinput, precision);
    519       }
    520 
    521       /* Handle the specifier */
    522       if(param < 0)
    523         param = param_num++;
    524       if(param >= MAX_PARAMETERS)
    525         return PFMT_MANYARGS;
    526       if(param >= max_param)
    527         max_param = param;
    528 
    529       iptr = &in[param];
    530       iptr->type = type;
    531 
    532       /* mark this input as used */
    533       mark_arg_used(usedinput, param);
    534 
    535       fmt++;
    536       optr = &out[ocount++];
    537       if(ocount > MAX_SEGMENTS)
    538         return PFMT_MANYSEGS;
    539       optr->input = (unsigned int)param;
    540       optr->flags = flags;
    541       optr->width = width;
    542       optr->precision = precision;
    543       optr->start = start;
    544       optr->outlen = outlen;
    545       start = fmt;
    546     }
    547     else
    548       fmt++;
    549   }
    550 
    551   /* is there a trailing piece */
    552   outlen = (size_t)(fmt - start);
    553   if(outlen) {
    554     optr = &out[ocount++];
    555     if(ocount > MAX_SEGMENTS)
    556       return PFMT_MANYSEGS;
    557     optr->input = 0;
    558     optr->flags = FLAGS_SUBSTR;
    559     optr->start = start;
    560     optr->outlen = outlen;
    561   }
    562 
    563   /* Read the arg list parameters into our data list */
    564   for(i = 0; i < max_param + 1; i++) {
    565     struct va_input *iptr = &in[i];
    566     if(!is_arg_used(usedinput, i))
    567       /* bad input */
    568       return PFMT_INPUTGAP;
    569 
    570     /* based on the type, read the correct argument */
    571     switch(iptr->type) {
    572     case FORMAT_STRING:
    573       iptr->val.str = va_arg(arglist, const char *);
    574       break;
    575 
    576     case FORMAT_INTPTR:
    577     case FORMAT_PTR:
    578       iptr->val.ptr = va_arg(arglist, void *);
    579       break;
    580 
    581     case FORMAT_LONGLONGU:
    582       iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
    583       break;
    584 
    585     case FORMAT_LONGLONG:
    586       iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t);
    587       break;
    588 
    589     case FORMAT_LONGU:
    590       iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long);
    591       break;
    592 
    593     case FORMAT_LONG:
    594       iptr->val.nums = (mp_intmax_t)va_arg(arglist, long);
    595       break;
    596 
    597     case FORMAT_INTU:
    598       iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int);
    599       break;
    600 
    601     case FORMAT_INT:
    602     case FORMAT_WIDTH:
    603     case FORMAT_PRECISION:
    604       iptr->val.nums = (mp_intmax_t)va_arg(arglist, int);
    605       break;
    606 
    607     case FORMAT_DOUBLE:
    608       iptr->val.dnum = va_arg(arglist, double);
    609       break;
    610 
    611     default:
    612       DEBUGASSERT(NULL); /* unexpected */
    613       break;
    614     }
    615   }
    616   *ipieces = max_param + 1;
    617   *opieces = ocount;
    618 
    619   return PFMT_OK;
    620 }
    621 
    622 struct mproperty {
    623   int width;            /* Width of a field.  */
    624   int prec;             /* Precision of a field.  */
    625   unsigned int flags;
    626 };
    627 
    628 static bool out_double(void *userp,
    629                        int (*stream)(unsigned char, void *),
    630                        struct mproperty *p,
    631                        double dnum,
    632                        char *work, int *donep)
    633 {
    634   char formatbuf[32]="%";
    635   char *fptr = &formatbuf[1];
    636   size_t left = sizeof(formatbuf)-strlen(formatbuf);
    637   int flags = p->flags;
    638   int width = p->width;
    639   int prec = p->prec;
    640 
    641   if(flags & FLAGS_LEFT)
    642     *fptr++ = '-';
    643   if(flags & FLAGS_SHOWSIGN)
    644     *fptr++ = '+';
    645   if(flags & FLAGS_SPACE)
    646     *fptr++ = ' ';
    647   if(flags & FLAGS_ALT)
    648     *fptr++ = '#';
    649 
    650   *fptr = 0;
    651 
    652   if(width >= 0) {
    653     size_t dlen;
    654     if(width >= BUFFSIZE)
    655       width = BUFFSIZE - 1;
    656     /* RECURSIVE USAGE */
    657     dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
    658     fptr += dlen;
    659     left -= dlen;
    660   }
    661   if(prec >= 0) {
    662     /* for each digit in the integer part, we can have one less
    663        precision */
    664     int maxprec = BUFFSIZE - 1;
    665     double val = dnum;
    666     int len;
    667     if(prec > maxprec)
    668       prec = maxprec - 1;
    669     if(width > 0 && prec <= width)
    670       maxprec -= width;
    671     while(val >= 10.0) {
    672       val /= 10;
    673       maxprec--;
    674     }
    675 
    676     if(prec > maxprec)
    677       prec = maxprec - 1;
    678     if(prec < 0)
    679       prec = 0;
    680     /* RECURSIVE USAGE */
    681     len = curl_msnprintf(fptr, left, ".%d", prec);
    682     fptr += len;
    683   }
    684   if(flags & FLAGS_LONG)
    685     *fptr++ = 'l';
    686 
    687   if(flags & FLAGS_FLOATE)
    688     *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e');
    689   else if(flags & FLAGS_FLOATG)
    690     *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
    691   else
    692     *fptr++ = 'f';
    693 
    694   *fptr = 0; /* and a final null-termination */
    695 
    696 #ifdef __clang__
    697 #pragma clang diagnostic push
    698 #pragma clang diagnostic ignored "-Wformat-nonliteral"
    699 #endif
    700   /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
    701      output characters */
    702 #ifdef HAVE_SNPRINTF
    703   /* !checksrc! disable LONGLINE */
    704   /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */
    705   (snprintf)(work, BUFFSIZE, formatbuf, dnum);
    706 #ifdef _WIN32
    707   /* Old versions of the Windows CRT do not terminate the snprintf output
    708      buffer if it reaches the max size so we do that here. */
    709   work[BUFFSIZE - 1] = 0;
    710 #endif
    711 #else
    712   (sprintf)(work, formatbuf, dnum);
    713 #endif
    714 #ifdef __clang__
    715 #pragma clang diagnostic pop
    716 #endif
    717   DEBUGASSERT(strlen(work) < BUFFSIZE);
    718   while(*work) {
    719     if(stream(*work++, userp))
    720       return TRUE;
    721     (*donep)++;
    722   }
    723   return 0;
    724 }
    725 
    726 static bool out_number(void *userp,
    727                        int (*stream)(unsigned char, void *),
    728                        struct mproperty *p,
    729                        mp_uintmax_t num,
    730                        mp_intmax_t nums,
    731                        char *work, int *donep)
    732 {
    733   const unsigned char *digits = Curl_ldigits;
    734   int flags = p->flags;
    735   int width = p->width;
    736   int prec = p->prec;
    737   bool is_alt = flags & FLAGS_ALT;
    738   bool is_neg = FALSE;
    739   int base = 10;
    740 
    741   /* 'workend' points to the final buffer byte position, but with an extra
    742      byte as margin to avoid the (FALSE?) warning Coverity gives us
    743      otherwise */
    744   char *workend = &work[BUFFSIZE - 2];
    745   char *w;
    746 
    747   if(flags & FLAGS_CHAR) {
    748     /* Character.  */
    749     if(!(flags & FLAGS_LEFT))
    750       while(--width > 0)
    751         OUTCHAR(' ');
    752     OUTCHAR((char) num);
    753     if(flags & FLAGS_LEFT)
    754       while(--width > 0)
    755         OUTCHAR(' ');
    756     return FALSE;
    757   }
    758   if(flags & FLAGS_OCTAL)
    759     /* Octal unsigned integer */
    760     base = 8;
    761 
    762   else if(flags & FLAGS_HEX) {
    763     /* Hexadecimal unsigned integer */
    764     digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits;
    765     base = 16;
    766   }
    767   else if(flags & FLAGS_UNSIGNED)
    768     /* Decimal unsigned integer */
    769     ;
    770 
    771   else {
    772     /* Decimal integer.  */
    773     is_neg = (nums < 0);
    774     if(is_neg) {
    775       /* signed_num might fail to hold absolute negative minimum by 1 */
    776       mp_intmax_t signed_num; /* Used to convert negative in positive.  */
    777       signed_num = nums + (mp_intmax_t)1;
    778       signed_num = -signed_num;
    779       num = (mp_uintmax_t)signed_num;
    780       num += (mp_uintmax_t)1;
    781     }
    782   }
    783 
    784   /* Supply a default precision if none was given.  */
    785   if(prec == -1)
    786     prec = 1;
    787 
    788   /* Put the number in WORK.  */
    789   w = workend;
    790   DEBUGASSERT(base <= 16);
    791   switch(base) {
    792   case 10:
    793     while(num > 0) {
    794       *w-- = (char)('0' + (num % 10));
    795       num /= 10;
    796     }
    797     break;
    798   default:
    799     while(num > 0) {
    800       *w-- = digits[num % base];
    801       num /= base;
    802     }
    803     break;
    804   }
    805   width -= (int)(workend - w);
    806   prec -= (int)(workend - w);
    807 
    808   if(is_alt && base == 8 && prec <= 0) {
    809     *w-- = '0';
    810     --width;
    811   }
    812 
    813   if(prec > 0) {
    814     width -= prec;
    815     while(prec-- > 0 && w >= work)
    816       *w-- = '0';
    817   }
    818 
    819   if(is_alt && base == 16)
    820     width -= 2;
    821 
    822   if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
    823     --width;
    824 
    825   if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
    826     while(width-- > 0)
    827       OUTCHAR(' ');
    828 
    829   if(is_neg)
    830     OUTCHAR('-');
    831   else if(flags & FLAGS_SHOWSIGN)
    832     OUTCHAR('+');
    833   else if(flags & FLAGS_SPACE)
    834     OUTCHAR(' ');
    835 
    836   if(is_alt && base == 16) {
    837     OUTCHAR('0');
    838     if(flags & FLAGS_UPPER)
    839       OUTCHAR('X');
    840     else
    841       OUTCHAR('x');
    842   }
    843 
    844   if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
    845     while(width-- > 0)
    846       OUTCHAR('0');
    847 
    848   /* Write the number.  */
    849   while(++w <= workend) {
    850     OUTCHAR(*w);
    851   }
    852 
    853   if(flags & FLAGS_LEFT)
    854     while(width-- > 0)
    855       OUTCHAR(' ');
    856 
    857   return FALSE;
    858 }
    859 
    860 static const char nilstr[] = "(nil)";
    861 
    862 static bool out_string(void *userp,
    863                        int (*stream)(unsigned char, void *),
    864                        struct mproperty *p,
    865                        const char *str,
    866                        int *donep)
    867 {
    868   int flags = p->flags;
    869   int width = p->width;
    870   int prec = p->prec;
    871   size_t len;
    872 
    873   if(!str) {
    874     /* Write null string if there is space.  */
    875     if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
    876       str = nilstr;
    877       len = sizeof(nilstr) - 1;
    878       /* Disable quotes around (nil) */
    879       flags &= ~(unsigned int)FLAGS_ALT;
    880     }
    881     else {
    882       str = "";
    883       len = 0;
    884     }
    885   }
    886   else if(prec != -1)
    887     len = (size_t)prec;
    888   else if(*str == '\0')
    889     len = 0;
    890   else
    891     len = strlen(str);
    892 
    893   width -= (len > INT_MAX) ? INT_MAX : (int)len;
    894 
    895   if(flags & FLAGS_ALT)
    896     OUTCHAR('"');
    897 
    898   if(!(flags & FLAGS_LEFT))
    899     while(width-- > 0)
    900       OUTCHAR(' ');
    901 
    902   for(; len && *str; len--)
    903     OUTCHAR(*str++);
    904   if(flags & FLAGS_LEFT)
    905     while(width-- > 0)
    906       OUTCHAR(' ');
    907 
    908   if(flags & FLAGS_ALT)
    909     OUTCHAR('"');
    910 
    911   return FALSE;
    912 }
    913 
    914 static bool out_pointer(void *userp,
    915                         int (*stream)(unsigned char, void *),
    916                         struct mproperty *p,
    917                         const char *ptr,
    918                         char *work,
    919                         int *donep)
    920 {
    921   /* Generic pointer.  */
    922   if(ptr) {
    923     size_t num = (size_t) ptr;
    924 
    925     /* If the pointer is not NULL, write it as a %#x spec.  */
    926     p->flags |= FLAGS_HEX|FLAGS_ALT;
    927     if(out_number(userp, stream, p, num, 0, work, donep))
    928       return TRUE;
    929   }
    930   else {
    931     /* Write "(nil)" for a nil pointer.  */
    932     const char *point;
    933     int width = p->width;
    934     int flags = p->flags;
    935 
    936     width -= (int)(sizeof(nilstr) - 1);
    937     if(flags & FLAGS_LEFT)
    938       while(width-- > 0)
    939         OUTCHAR(' ');
    940     for(point = nilstr; *point; ++point)
    941       OUTCHAR(*point);
    942     if(!(flags & FLAGS_LEFT))
    943       while(width-- > 0)
    944         OUTCHAR(' ');
    945   }
    946   return FALSE;
    947 }
    948 
    949 /*
    950  * formatf() - the general printf function.
    951  *
    952  * It calls parsefmt() to parse the format string. It populates two arrays;
    953  * one that describes the input arguments and one that describes a number of
    954  * output segments.
    955  *
    956  * On success, the input array describes the type of all arguments and their
    957  * values.
    958  *
    959  * The function then iterates over the output segments and outputs them one
    960  * by one until done. Using the appropriate input arguments (if any).
    961  *
    962  * All output is sent to the 'stream()' callback, one byte at a time.
    963  */
    964 
    965 static int formatf(
    966   void *userp, /* untouched by format(), just sent to the stream() function in
    967                   the second argument */
    968   /* function pointer called for each output character */
    969   int (*stream)(unsigned char, void *),
    970   const char *format,    /* %-formatted string */
    971   va_list ap_save) /* list of parameters */
    972 {
    973   int done = 0;   /* number of characters written  */
    974   int i;
    975   int ocount = 0; /* number of output segments */
    976   int icount = 0; /* number of input arguments */
    977 
    978   struct outsegment output[MAX_SEGMENTS];
    979   struct va_input input[MAX_PARAMETERS];
    980   char work[BUFFSIZE + 2];
    981 
    982   /* Parse the format string */
    983   if(parsefmt(format, output, input, &ocount, &icount, ap_save))
    984     return 0;
    985 
    986   for(i = 0; i < ocount; i++) {
    987     struct outsegment *optr = &output[i];
    988     struct va_input *iptr = &input[optr->input];
    989     struct mproperty p;
    990     size_t outlen = optr->outlen;
    991 
    992     if(outlen) {
    993       const char *str = optr->start;
    994       for(; outlen && *str; outlen--) {
    995         if(stream(*str++, userp))
    996           return done;
    997         done++;
    998       }
    999       if(optr->flags & FLAGS_SUBSTR)
   1000         /* this is just a substring */
   1001         continue;
   1002     }
   1003 
   1004     p.flags = optr->flags;
   1005 
   1006     /* pick up the specified width */
   1007     if(p.flags & FLAGS_WIDTHPARAM) {
   1008       p.width = (int)input[optr->width].val.nums;
   1009       if(p.width < 0) {
   1010         /* "A negative field width is taken as a '-' flag followed by a
   1011            positive field width." */
   1012         if(p.width == INT_MIN)
   1013           p.width = INT_MAX;
   1014         else
   1015           p.width = -p.width;
   1016         p.flags |= FLAGS_LEFT;
   1017         p.flags &= ~(unsigned int)FLAGS_PAD_NIL;
   1018       }
   1019     }
   1020     else
   1021       p.width = optr->width;
   1022 
   1023     /* pick up the specified precision */
   1024     if(p.flags & FLAGS_PRECPARAM) {
   1025       p.prec = (int)input[optr->precision].val.nums;
   1026       if(p.prec < 0)
   1027         /* "A negative precision is taken as if the precision were
   1028            omitted." */
   1029         p.prec = -1;
   1030     }
   1031     else if(p.flags & FLAGS_PREC)
   1032       p.prec = optr->precision;
   1033     else
   1034       p.prec = -1;
   1035 
   1036     switch(iptr->type) {
   1037     case FORMAT_INTU:
   1038     case FORMAT_LONGU:
   1039     case FORMAT_LONGLONGU:
   1040       p.flags |= FLAGS_UNSIGNED;
   1041       if(out_number(userp, stream, &p, iptr->val.numu, 0, work, &done))
   1042         return done;
   1043       break;
   1044 
   1045     case FORMAT_INT:
   1046     case FORMAT_LONG:
   1047     case FORMAT_LONGLONG:
   1048       if(out_number(userp, stream, &p, iptr->val.numu,
   1049                     iptr->val.nums, work, &done))
   1050         return done;
   1051       break;
   1052 
   1053     case FORMAT_STRING:
   1054       if(out_string(userp, stream, &p, iptr->val.str, &done))
   1055         return done;
   1056       break;
   1057 
   1058     case FORMAT_PTR:
   1059       if(out_pointer(userp, stream, &p, iptr->val.ptr, work, &done))
   1060         return done;
   1061       break;
   1062 
   1063     case FORMAT_DOUBLE:
   1064       if(out_double(userp, stream, &p, iptr->val.dnum, work, &done))
   1065         return done;
   1066       break;
   1067 
   1068     case FORMAT_INTPTR:
   1069       /* Answer the count of characters written.  */
   1070 #ifdef HAVE_LONG_LONG_TYPE
   1071       if(p.flags & FLAGS_LONGLONG)
   1072         *(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done;
   1073       else
   1074 #endif
   1075         if(p.flags & FLAGS_LONG)
   1076           *(long *) iptr->val.ptr = (long)done;
   1077       else if(!(p.flags & FLAGS_SHORT))
   1078         *(int *) iptr->val.ptr = (int)done;
   1079       else
   1080         *(short *) iptr->val.ptr = (short)done;
   1081       break;
   1082 
   1083     default:
   1084       break;
   1085     }
   1086   }
   1087   return done;
   1088 }
   1089 
   1090 /* fputc() look-alike */
   1091 static int addbyter(unsigned char outc, void *f)
   1092 {
   1093   struct nsprintf *infop = f;
   1094   if(infop->length < infop->max) {
   1095     /* only do this if we have not reached max length yet */
   1096     *infop->buffer++ = (char)outc; /* store */
   1097     infop->length++; /* we are now one byte larger */
   1098     return 0;     /* fputc() returns like this on success */
   1099   }
   1100   return 1;
   1101 }
   1102 
   1103 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
   1104                     va_list ap_save)
   1105 {
   1106   int retcode;
   1107   struct nsprintf info;
   1108 
   1109   info.buffer = buffer;
   1110   info.length = 0;
   1111   info.max = maxlength;
   1112 
   1113   retcode = formatf(&info, addbyter, format, ap_save);
   1114   if(info.max) {
   1115     /* we terminate this with a zero byte */
   1116     if(info.max == info.length) {
   1117       /* we are at maximum, scrap the last letter */
   1118       info.buffer[-1] = 0;
   1119       DEBUGASSERT(retcode);
   1120       retcode--; /* do not count the nul byte */
   1121     }
   1122     else
   1123       info.buffer[0] = 0;
   1124   }
   1125   return retcode;
   1126 }
   1127 
   1128 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
   1129 {
   1130   int retcode;
   1131   va_list ap_save; /* argument pointer */
   1132   va_start(ap_save, format);
   1133   retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
   1134   va_end(ap_save);
   1135   return retcode;
   1136 }
   1137 
   1138 /* fputc() look-alike */
   1139 static int alloc_addbyter(unsigned char outc, void *f)
   1140 {
   1141   struct asprintf *infop = f;
   1142   CURLcode result = curlx_dyn_addn(infop->b, &outc, 1);
   1143   if(result) {
   1144     infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
   1145     return 1 ; /* fail */
   1146   }
   1147   return 0;
   1148 }
   1149 
   1150 /* appends the formatted string, returns MERR error code */
   1151 int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
   1152 {
   1153   struct asprintf info;
   1154   info.b = dyn;
   1155   info.merr = MERR_OK;
   1156 
   1157   (void)formatf(&info, alloc_addbyter, format, ap_save);
   1158   if(info.merr) {
   1159     curlx_dyn_free(info.b);
   1160     return info.merr;
   1161   }
   1162   return 0;
   1163 }
   1164 
   1165 char *curl_mvaprintf(const char *format, va_list ap_save)
   1166 {
   1167   struct asprintf info;
   1168   struct dynbuf dyn;
   1169   info.b = &dyn;
   1170   curlx_dyn_init(info.b, DYN_APRINTF);
   1171   info.merr = MERR_OK;
   1172 
   1173   (void)formatf(&info, alloc_addbyter, format, ap_save);
   1174   if(info.merr) {
   1175     curlx_dyn_free(info.b);
   1176     return NULL;
   1177   }
   1178   if(curlx_dyn_len(info.b))
   1179     return curlx_dyn_ptr(info.b);
   1180   return strdup("");
   1181 }
   1182 
   1183 char *curl_maprintf(const char *format, ...)
   1184 {
   1185   va_list ap_save;
   1186   char *s;
   1187   va_start(ap_save, format);
   1188   s = curl_mvaprintf(format, ap_save);
   1189   va_end(ap_save);
   1190   return s;
   1191 }
   1192 
   1193 static int storebuffer(unsigned char outc, void *f)
   1194 {
   1195   char **buffer = f;
   1196   **buffer = (char)outc;
   1197   (*buffer)++;
   1198   return 0;
   1199 }
   1200 
   1201 int curl_msprintf(char *buffer, const char *format, ...)
   1202 {
   1203   va_list ap_save; /* argument pointer */
   1204   int retcode;
   1205   va_start(ap_save, format);
   1206   retcode = formatf(&buffer, storebuffer, format, ap_save);
   1207   va_end(ap_save);
   1208   *buffer = 0; /* we terminate this with a zero byte */
   1209   return retcode;
   1210 }
   1211 
   1212 static int fputc_wrapper(unsigned char outc, void *f)
   1213 {
   1214   int out = outc;
   1215   FILE *s = f;
   1216   int rc = fputc(out, s);
   1217   return rc == EOF;
   1218 }
   1219 
   1220 int curl_mprintf(const char *format, ...)
   1221 {
   1222   int retcode;
   1223   va_list ap_save; /* argument pointer */
   1224   va_start(ap_save, format);
   1225   retcode = formatf(stdout, fputc_wrapper, format, ap_save);
   1226   va_end(ap_save);
   1227   return retcode;
   1228 }
   1229 
   1230 int curl_mfprintf(FILE *whereto, const char *format, ...)
   1231 {
   1232   int retcode;
   1233   va_list ap_save; /* argument pointer */
   1234   va_start(ap_save, format);
   1235   retcode = formatf(whereto, fputc_wrapper, format, ap_save);
   1236   va_end(ap_save);
   1237   return retcode;
   1238 }
   1239 
   1240 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
   1241 {
   1242   int retcode = formatf(&buffer, storebuffer, format, ap_save);
   1243   *buffer = 0; /* we terminate this with a zero byte */
   1244   return retcode;
   1245 }
   1246 
   1247 int curl_mvprintf(const char *format, va_list ap_save)
   1248 {
   1249   return formatf(stdout, fputc_wrapper, format, ap_save);
   1250 }
   1251 
   1252 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
   1253 {
   1254   return formatf(whereto, fputc_wrapper, format, ap_save);
   1255 }