tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prprf.c (27882B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 ** Portable safe sprintf code.
      8 **
      9 ** Author: Kipp E.B. Hickman
     10 */
     11 #include <stdarg.h>
     12 #include <stddef.h>
     13 #include <stdio.h>
     14 #include <string.h>
     15 #include "primpl.h"
     16 #include "prprf.h"
     17 #include "prlong.h"
     18 #include "prlog.h"
     19 #include "prmem.h"
     20 
     21 #if defined(_MSC_VER) && _MSC_VER < 1900
     22 #  define snprintf _snprintf
     23 #endif
     24 
     25 /*
     26 ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
     27 */
     28 
     29 /*
     30 ** XXX This needs to be internationalized!
     31 */
     32 
     33 typedef struct SprintfStateStr SprintfState;
     34 
     35 struct SprintfStateStr {
     36  int (*stuff)(SprintfState* ss, const char* sp, PRUint32 len);
     37 
     38  char* base;
     39  char* cur;
     40  PRUint32 maxlen; /* Must not exceed PR_INT32_MAX. */
     41 
     42  int (*func)(void* arg, const char* sp, PRUint32 len);
     43  void* arg;
     44 };
     45 
     46 /*
     47 ** Numbered Argument
     48 */
     49 struct NumArg {
     50  int type; /* type of the numbered argument    */
     51  union {   /* the numbered argument            */
     52    int i;
     53    unsigned int ui;
     54    PRInt32 i32;
     55    PRUint32 ui32;
     56    PRInt64 ll;
     57    PRUint64 ull;
     58    double d;
     59    const char* s;
     60    int* ip;
     61 #ifdef WIN32
     62    const WCHAR* ws;
     63 #endif
     64  } u;
     65 };
     66 
     67 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
     68 
     69 /*
     70 ** For numeric types, the signed versions must have even values,
     71 ** and their corresponding unsigned versions must have the subsequent
     72 ** odd value.
     73 */
     74 #define TYPE_INT16 0
     75 #define TYPE_UINT16 1
     76 #define TYPE_INTN 2
     77 #define TYPE_UINTN 3
     78 #define TYPE_INT32 4
     79 #define TYPE_UINT32 5
     80 #define TYPE_INT64 6
     81 #define TYPE_UINT64 7
     82 #define TYPE_STRING 8
     83 #define TYPE_DOUBLE 9
     84 #define TYPE_INTSTR 10
     85 #ifdef WIN32
     86 #  define TYPE_WSTRING 11
     87 #endif
     88 #define TYPE_UNKNOWN 20
     89 
     90 #define FLAG_LEFT 0x1
     91 #define FLAG_SIGNED 0x2
     92 #define FLAG_SPACED 0x4
     93 #define FLAG_ZEROS 0x8
     94 #define FLAG_NEG 0x10
     95 
     96 /*
     97 ** Fill into the buffer using the data in src
     98 */
     99 static int fill2(SprintfState* ss, const char* src, int srclen, int width,
    100                 int flags) {
    101  char space = ' ';
    102  int rv;
    103 
    104  width -= srclen;
    105  if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
    106    if (flags & FLAG_ZEROS) {
    107      space = '0';
    108    }
    109    while (--width >= 0) {
    110      rv = (*ss->stuff)(ss, &space, 1);
    111      if (rv < 0) {
    112        return rv;
    113      }
    114    }
    115  }
    116 
    117  /* Copy out the source data */
    118  rv = (*ss->stuff)(ss, src, srclen);
    119  if (rv < 0) {
    120    return rv;
    121  }
    122 
    123  if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
    124    while (--width >= 0) {
    125      rv = (*ss->stuff)(ss, &space, 1);
    126      if (rv < 0) {
    127        return rv;
    128      }
    129    }
    130  }
    131  return 0;
    132 }
    133 
    134 /*
    135 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
    136 */
    137 static int fill_n(SprintfState* ss, const char* src, int srclen, int width,
    138                  int prec, int type, int flags) {
    139  int zerowidth = 0;
    140  int precwidth = 0;
    141  int signwidth = 0;
    142  int leftspaces = 0;
    143  int rightspaces = 0;
    144  int cvtwidth;
    145  int rv;
    146  char sign;
    147 
    148  if ((type & 1) == 0) {
    149    if (flags & FLAG_NEG) {
    150      sign = '-';
    151      signwidth = 1;
    152    } else if (flags & FLAG_SIGNED) {
    153      sign = '+';
    154      signwidth = 1;
    155    } else if (flags & FLAG_SPACED) {
    156      sign = ' ';
    157      signwidth = 1;
    158    }
    159  }
    160  cvtwidth = signwidth + srclen;
    161 
    162  if (prec > 0) {
    163    if (prec > srclen) {
    164      precwidth = prec - srclen; /* Need zero filling */
    165      cvtwidth += precwidth;
    166    }
    167  }
    168 
    169  if ((flags & FLAG_ZEROS) && (prec < 0)) {
    170    if (width > cvtwidth) {
    171      zerowidth = width - cvtwidth; /* Zero filling */
    172      cvtwidth += zerowidth;
    173    }
    174  }
    175 
    176  if (flags & FLAG_LEFT) {
    177    if (width > cvtwidth) {
    178      /* Space filling on the right (i.e. left adjusting) */
    179      rightspaces = width - cvtwidth;
    180    }
    181  } else {
    182    if (width > cvtwidth) {
    183      /* Space filling on the left (i.e. right adjusting) */
    184      leftspaces = width - cvtwidth;
    185    }
    186  }
    187  while (--leftspaces >= 0) {
    188    rv = (*ss->stuff)(ss, " ", 1);
    189    if (rv < 0) {
    190      return rv;
    191    }
    192  }
    193  if (signwidth) {
    194    rv = (*ss->stuff)(ss, &sign, 1);
    195    if (rv < 0) {
    196      return rv;
    197    }
    198  }
    199  while (--precwidth >= 0) {
    200    rv = (*ss->stuff)(ss, "0", 1);
    201    if (rv < 0) {
    202      return rv;
    203    }
    204  }
    205  while (--zerowidth >= 0) {
    206    rv = (*ss->stuff)(ss, "0", 1);
    207    if (rv < 0) {
    208      return rv;
    209    }
    210  }
    211  rv = (*ss->stuff)(ss, src, srclen);
    212  if (rv < 0) {
    213    return rv;
    214  }
    215  while (--rightspaces >= 0) {
    216    rv = (*ss->stuff)(ss, " ", 1);
    217    if (rv < 0) {
    218      return rv;
    219    }
    220  }
    221  return 0;
    222 }
    223 
    224 /*
    225 ** Convert a long into its printable form
    226 */
    227 static int cvt_l(SprintfState* ss, long num, int width, int prec, int radix,
    228                 int type, int flags, const char* hexp) {
    229  char cvtbuf[100];
    230  char* cvt;
    231  int digits;
    232 
    233  /* according to the man page this needs to happen */
    234  if ((prec == 0) && (num == 0)) {
    235    return 0;
    236  }
    237 
    238  /*
    239  ** Converting decimal is a little tricky. In the unsigned case we
    240  ** need to stop when we hit 10 digits. In the signed case, we can
    241  ** stop when the number is zero.
    242  */
    243  cvt = cvtbuf + sizeof(cvtbuf);
    244  digits = 0;
    245  while (num) {
    246    int digit = (((unsigned long)num) % radix) & 0xF;
    247    *--cvt = hexp[digit];
    248    digits++;
    249    num = (long)(((unsigned long)num) / radix);
    250  }
    251  if (digits == 0) {
    252    *--cvt = '0';
    253    digits++;
    254  }
    255 
    256  /*
    257  ** Now that we have the number converted without its sign, deal with
    258  ** the sign and zero padding.
    259  */
    260  return fill_n(ss, cvt, digits, width, prec, type, flags);
    261 }
    262 
    263 /*
    264 ** Convert a 64-bit integer into its printable form
    265 */
    266 static int cvt_ll(SprintfState* ss, PRInt64 num, int width, int prec, int radix,
    267                  int type, int flags, const char* hexp) {
    268  char cvtbuf[100];
    269  char* cvt;
    270  int digits;
    271  PRInt64 rad;
    272 
    273  /* according to the man page this needs to happen */
    274  if ((prec == 0) && (LL_IS_ZERO(num))) {
    275    return 0;
    276  }
    277 
    278  /*
    279  ** Converting decimal is a little tricky. In the unsigned case we
    280  ** need to stop when we hit 10 digits. In the signed case, we can
    281  ** stop when the number is zero.
    282  */
    283  LL_I2L(rad, radix);
    284  cvt = cvtbuf + sizeof(cvtbuf);
    285  digits = 0;
    286  while (!LL_IS_ZERO(num)) {
    287    PRInt32 digit;
    288    PRInt64 quot, rem;
    289    LL_UDIVMOD(&quot, &rem, num, rad);
    290    LL_L2I(digit, rem);
    291    *--cvt = hexp[digit & 0xf];
    292    digits++;
    293    num = quot;
    294  }
    295  if (digits == 0) {
    296    *--cvt = '0';
    297    digits++;
    298  }
    299 
    300  /*
    301  ** Now that we have the number converted without its sign, deal with
    302  ** the sign and zero padding.
    303  */
    304  return fill_n(ss, cvt, digits, width, prec, type, flags);
    305 }
    306 
    307 /*
    308 ** Convert a double precision floating point number into its printable
    309 ** form.
    310 **
    311 ** XXX stop using snprintf to convert floating point
    312 */
    313 static int cvt_f(SprintfState* ss, double d, const char* fmt0,
    314                 const char* fmt1) {
    315  char fin[20];
    316  char fout[300];
    317  int amount = fmt1 - fmt0;
    318 
    319  if (amount <= 0 || amount >= sizeof(fin)) {
    320    /* Totally bogus % command to snprintf. Just ignore it */
    321    return 0;
    322  }
    323  memcpy(fin, fmt0, amount);
    324  fin[amount] = 0;
    325 
    326  /* Convert floating point using the native snprintf code */
    327 #ifdef DEBUG
    328  {
    329    const char* p = fin;
    330    while (*p) {
    331      PR_ASSERT(*p != 'L');
    332      p++;
    333    }
    334  }
    335 #endif
    336  memset(fout, 0, sizeof(fout));
    337  snprintf(fout, sizeof(fout), fin, d);
    338  /* Explicitly null-terminate fout because on Windows snprintf doesn't
    339   * append a null-terminator if the buffer is too small. */
    340  fout[sizeof(fout) - 1] = '\0';
    341 
    342  return (*ss->stuff)(ss, fout, strlen(fout));
    343 }
    344 
    345 /*
    346 ** Convert a string into its printable form.  "width" is the output
    347 ** width. "prec" is the maximum number of characters of "s" to output,
    348 ** where -1 means until NUL.
    349 */
    350 static int cvt_s(SprintfState* ss, const char* str, int width, int prec,
    351                 int flags) {
    352  int slen;
    353 
    354  if (prec == 0) {
    355    return 0;
    356  }
    357 
    358  /* Limit string length by precision value */
    359  if (!str) {
    360    str = "(null)";
    361  }
    362  if (prec > 0) {
    363    /* this is:  slen = strnlen(str, prec); */
    364    register const char* s;
    365 
    366    for (s = str; prec && *s; s++, prec--);
    367    slen = s - str;
    368  } else {
    369    slen = strlen(str);
    370  }
    371 
    372  /* and away we go */
    373  return fill2(ss, str, slen, width, flags);
    374 }
    375 
    376 /*
    377 ** BuildArgArray stands for Numbered Argument list Sprintf
    378 ** for example,
    379 **  fmt = "%4$i, %2$d, %3s, %1d";
    380 ** the number must start from 1, and no gap among them
    381 */
    382 
    383 static struct NumArg* BuildArgArray(const char* fmt, va_list ap, int* rv,
    384                                    struct NumArg* nasArray) {
    385  int number = 0, cn = 0, i;
    386  const char* p;
    387  char c;
    388  struct NumArg* nas;
    389 
    390  /*
    391  **  first pass:
    392  **  determine how many legal % I have got, then allocate space
    393  */
    394 
    395  p = fmt;
    396  *rv = 0;
    397  i = 0;
    398  while ((c = *p++) != 0) {
    399    if (c != '%') {
    400      continue;
    401    }
    402    if ((c = *p++) == '%') { /* skip %% case */
    403      continue;
    404    }
    405 
    406    while (c != 0) {
    407      if (c > '9' || c < '0') {
    408        if (c == '$') { /* numbered argument case */
    409          if (i > 0) {
    410            *rv = -1;
    411            return NULL;
    412          }
    413          number++;
    414        } else { /* non-numbered argument case */
    415          if (number > 0) {
    416            *rv = -1;
    417            return NULL;
    418          }
    419          i = 1;
    420        }
    421        break;
    422      }
    423 
    424      c = *p++;
    425    }
    426  }
    427 
    428  if (number == 0) {
    429    return NULL;
    430  }
    431 
    432  if (number > NAS_DEFAULT_NUM) {
    433    nas = (struct NumArg*)PR_MALLOC(number * sizeof(struct NumArg));
    434    if (!nas) {
    435      *rv = -1;
    436      return NULL;
    437    }
    438  } else {
    439    nas = nasArray;
    440  }
    441 
    442  for (i = 0; i < number; i++) {
    443    nas[i].type = TYPE_UNKNOWN;
    444  }
    445 
    446  /*
    447  ** second pass:
    448  ** set nas[].type
    449  */
    450 
    451  p = fmt;
    452  while ((c = *p++) != 0) {
    453    if (c != '%') {
    454      continue;
    455    }
    456    c = *p++;
    457    if (c == '%') {
    458      continue;
    459    }
    460 
    461    cn = 0;
    462    while (c && c != '$') { /* should improve error check later */
    463      cn = cn * 10 + c - '0';
    464      c = *p++;
    465    }
    466 
    467    if (!c || cn < 1 || cn > number) {
    468      *rv = -1;
    469      break;
    470    }
    471 
    472    /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
    473    cn--;
    474    if (nas[cn].type != TYPE_UNKNOWN) {
    475      continue;
    476    }
    477 
    478    c = *p++;
    479 
    480    /* width */
    481    if (c == '*') {
    482      /* not supported feature, for the argument is not numbered */
    483      *rv = -1;
    484      break;
    485    }
    486 
    487    while ((c >= '0') && (c <= '9')) {
    488      c = *p++;
    489    }
    490 
    491    /* precision */
    492    if (c == '.') {
    493      c = *p++;
    494      if (c == '*') {
    495        /* not supported feature, for the argument is not numbered */
    496        *rv = -1;
    497        break;
    498      }
    499 
    500      while ((c >= '0') && (c <= '9')) {
    501        c = *p++;
    502      }
    503    }
    504 
    505    /* size */
    506    nas[cn].type = TYPE_INTN;
    507    if (c == 'h') {
    508      nas[cn].type = TYPE_INT16;
    509      c = *p++;
    510    } else if (c == 'L') {
    511      /* XXX not quite sure here */
    512      nas[cn].type = TYPE_INT64;
    513      c = *p++;
    514    } else if (c == 'l') {
    515      nas[cn].type = TYPE_INT32;
    516      c = *p++;
    517      if (c == 'l') {
    518        nas[cn].type = TYPE_INT64;
    519        c = *p++;
    520      }
    521    } else if (c == 'z') {
    522      if (sizeof(size_t) == sizeof(PRInt32)) {
    523        nas[cn].type = TYPE_INT32;
    524      } else if (sizeof(size_t) == sizeof(PRInt64)) {
    525        nas[cn].type = TYPE_INT64;
    526      } else {
    527        nas[cn].type = TYPE_UNKNOWN;
    528      }
    529      c = *p++;
    530    }
    531 
    532    /* format */
    533    switch (c) {
    534      case 'd':
    535      case 'c':
    536      case 'i':
    537      case 'o':
    538      case 'u':
    539      case 'x':
    540      case 'X':
    541        break;
    542 
    543      case 'e':
    544      case 'f':
    545      case 'g':
    546        nas[cn].type = TYPE_DOUBLE;
    547        break;
    548 
    549      case 'p':
    550        /* XXX should use cpp */
    551        if (sizeof(void*) == sizeof(PRInt32)) {
    552          nas[cn].type = TYPE_UINT32;
    553        } else if (sizeof(void*) == sizeof(PRInt64)) {
    554          nas[cn].type = TYPE_UINT64;
    555        } else if (sizeof(void*) == sizeof(PRIntn)) {
    556          nas[cn].type = TYPE_UINTN;
    557        } else {
    558          nas[cn].type = TYPE_UNKNOWN;
    559        }
    560        break;
    561 
    562      case 'S':
    563 #ifdef WIN32
    564        nas[cn].type = TYPE_WSTRING;
    565        break;
    566 #endif
    567      case 'C':
    568      case 'E':
    569      case 'G':
    570        /* XXX not supported I suppose */
    571        PR_ASSERT(0);
    572        nas[cn].type = TYPE_UNKNOWN;
    573        break;
    574 
    575      case 's':
    576        nas[cn].type = TYPE_STRING;
    577        break;
    578 
    579      case 'n':
    580        nas[cn].type = TYPE_INTSTR;
    581        break;
    582 
    583      default:
    584        PR_ASSERT(0);
    585        nas[cn].type = TYPE_UNKNOWN;
    586        break;
    587    }
    588 
    589    /* get a legal para. */
    590    if (nas[cn].type == TYPE_UNKNOWN) {
    591      *rv = -1;
    592      break;
    593    }
    594  }
    595 
    596  /*
    597  ** third pass
    598  ** fill the nas[cn].ap
    599  */
    600 
    601  if (*rv < 0) {
    602    if (nas != nasArray) {
    603      PR_DELETE(nas);
    604    }
    605    return NULL;
    606  }
    607 
    608  cn = 0;
    609  while (cn < number) {
    610    if (nas[cn].type == TYPE_UNKNOWN) {
    611      cn++;
    612      continue;
    613    }
    614 
    615    switch (nas[cn].type) {
    616      case TYPE_INT16:
    617      case TYPE_UINT16:
    618      case TYPE_INTN:
    619        nas[cn].u.i = va_arg(ap, int);
    620        break;
    621 
    622      case TYPE_UINTN:
    623        nas[cn].u.ui = va_arg(ap, unsigned int);
    624        break;
    625 
    626      case TYPE_INT32:
    627        nas[cn].u.i32 = va_arg(ap, PRInt32);
    628        break;
    629 
    630      case TYPE_UINT32:
    631        nas[cn].u.ui32 = va_arg(ap, PRUint32);
    632        break;
    633 
    634      case TYPE_INT64:
    635        nas[cn].u.ll = va_arg(ap, PRInt64);
    636        break;
    637 
    638      case TYPE_UINT64:
    639        nas[cn].u.ull = va_arg(ap, PRUint64);
    640        break;
    641 
    642      case TYPE_STRING:
    643        nas[cn].u.s = va_arg(ap, char*);
    644        break;
    645 
    646 #ifdef WIN32
    647      case TYPE_WSTRING:
    648        nas[cn].u.ws = va_arg(ap, WCHAR*);
    649        break;
    650 #endif
    651 
    652      case TYPE_INTSTR:
    653        nas[cn].u.ip = va_arg(ap, int*);
    654        break;
    655 
    656      case TYPE_DOUBLE:
    657        nas[cn].u.d = va_arg(ap, double);
    658        break;
    659 
    660      default:
    661        if (nas != nasArray) {
    662          PR_DELETE(nas);
    663        }
    664        *rv = -1;
    665        return NULL;
    666    }
    667 
    668    cn++;
    669  }
    670 
    671  return nas;
    672 }
    673 
    674 /*
    675 ** The workhorse sprintf code.
    676 */
    677 static int dosprintf(SprintfState* ss, const char* fmt, va_list ap) {
    678  char c;
    679  int flags, width, prec, radix, type;
    680  union {
    681    char ch;
    682    int i;
    683    long l;
    684    PRInt64 ll;
    685    double d;
    686    const char* s;
    687    int* ip;
    688 #ifdef WIN32
    689    const WCHAR* ws;
    690 #endif
    691  } u;
    692  const char* fmt0;
    693  static char* hex = "0123456789abcdef";
    694  static char* HEX = "0123456789ABCDEF";
    695  char* hexp;
    696  int rv, i;
    697  struct NumArg* nas = NULL;
    698  struct NumArg* nap = NULL;
    699  struct NumArg nasArray[NAS_DEFAULT_NUM];
    700  char pattern[20];
    701  const char* dolPt = NULL; /* in "%4$.2f", dolPt will point to . */
    702 #ifdef WIN32
    703  char* pBuf = NULL;
    704 #endif
    705 
    706  /*
    707  ** build an argument array, IF the fmt is numbered argument
    708  ** list style, to contain the Numbered Argument list pointers
    709  */
    710 
    711  nas = BuildArgArray(fmt, ap, &rv, nasArray);
    712  if (rv < 0) {
    713    /* the fmt contains error Numbered Argument format, jliu@netscape.com */
    714    PR_ASSERT(0);
    715    return rv;
    716  }
    717 
    718  while ((c = *fmt++) != 0) {
    719    if (c != '%') {
    720      rv = (*ss->stuff)(ss, fmt - 1, 1);
    721      if (rv < 0) {
    722        return rv;
    723      }
    724      continue;
    725    }
    726    fmt0 = fmt - 1;
    727 
    728    /*
    729    ** Gobble up the % format string. Hopefully we have handled all
    730    ** of the strange cases!
    731    */
    732    flags = 0;
    733    c = *fmt++;
    734    if (c == '%') {
    735      /* quoting a % with %% */
    736      rv = (*ss->stuff)(ss, fmt - 1, 1);
    737      if (rv < 0) {
    738        return rv;
    739      }
    740      continue;
    741    }
    742 
    743    if (nas != NULL) {
    744      /* the fmt contains the Numbered Arguments feature */
    745      i = 0;
    746      while (c && c != '$') { /* should improve error check later */
    747        i = (i * 10) + (c - '0');
    748        c = *fmt++;
    749      }
    750 
    751      if (nas[i - 1].type == TYPE_UNKNOWN) {
    752        if (nas && (nas != nasArray)) {
    753          PR_DELETE(nas);
    754        }
    755        return -1;
    756      }
    757 
    758      nap = &nas[i - 1];
    759      dolPt = fmt;
    760      c = *fmt++;
    761    }
    762 
    763    /*
    764     * Examine optional flags.  Note that we do not implement the
    765     * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
    766     * somewhat ambiguous and not ideal, which is perhaps why
    767     * the various sprintf() implementations are inconsistent
    768     * on this feature.
    769     */
    770    while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
    771      if (c == '-') {
    772        flags |= FLAG_LEFT;
    773      }
    774      if (c == '+') {
    775        flags |= FLAG_SIGNED;
    776      }
    777      if (c == ' ') {
    778        flags |= FLAG_SPACED;
    779      }
    780      if (c == '0') {
    781        flags |= FLAG_ZEROS;
    782      }
    783      c = *fmt++;
    784    }
    785    if (flags & FLAG_SIGNED) {
    786      flags &= ~FLAG_SPACED;
    787    }
    788    if (flags & FLAG_LEFT) {
    789      flags &= ~FLAG_ZEROS;
    790    }
    791 
    792    /* width */
    793    if (c == '*') {
    794      c = *fmt++;
    795      width = va_arg(ap, int);
    796    } else {
    797      width = 0;
    798      while ((c >= '0') && (c <= '9')) {
    799        width = (width * 10) + (c - '0');
    800        c = *fmt++;
    801      }
    802    }
    803 
    804    /* precision */
    805    prec = -1;
    806    if (c == '.') {
    807      c = *fmt++;
    808      if (c == '*') {
    809        c = *fmt++;
    810        prec = va_arg(ap, int);
    811      } else {
    812        prec = 0;
    813        while ((c >= '0') && (c <= '9')) {
    814          prec = (prec * 10) + (c - '0');
    815          c = *fmt++;
    816        }
    817      }
    818    }
    819 
    820    /* size */
    821    type = TYPE_INTN;
    822    if (c == 'h') {
    823      type = TYPE_INT16;
    824      c = *fmt++;
    825    } else if (c == 'L') {
    826      /* XXX not quite sure here */
    827      type = TYPE_INT64;
    828      c = *fmt++;
    829    } else if (c == 'l') {
    830      type = TYPE_INT32;
    831      c = *fmt++;
    832      if (c == 'l') {
    833        type = TYPE_INT64;
    834        c = *fmt++;
    835      }
    836    } else if (c == 'z') {
    837      if (sizeof(size_t) == sizeof(PRInt32)) {
    838        type = TYPE_INT32;
    839      } else if (sizeof(size_t) == sizeof(PRInt64)) {
    840        type = TYPE_INT64;
    841      }
    842      c = *fmt++;
    843    }
    844 
    845    /* format */
    846    hexp = hex;
    847    switch (c) {
    848      case 'd':
    849      case 'i': /* decimal/integer */
    850        radix = 10;
    851        goto fetch_and_convert;
    852 
    853      case 'o': /* octal */
    854        radix = 8;
    855        type |= 1;
    856        goto fetch_and_convert;
    857 
    858      case 'u': /* unsigned decimal */
    859        radix = 10;
    860        type |= 1;
    861        goto fetch_and_convert;
    862 
    863      case 'x': /* unsigned hex */
    864        radix = 16;
    865        type |= 1;
    866        goto fetch_and_convert;
    867 
    868      case 'X': /* unsigned HEX */
    869        radix = 16;
    870        hexp = HEX;
    871        type |= 1;
    872        goto fetch_and_convert;
    873 
    874      fetch_and_convert:
    875        switch (type) {
    876          case TYPE_INT16:
    877            u.l = nas ? nap->u.i : va_arg(ap, int);
    878            if (u.l < 0) {
    879              u.l = -u.l;
    880              flags |= FLAG_NEG;
    881            }
    882            goto do_long;
    883          case TYPE_UINT16:
    884            u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff;
    885            goto do_long;
    886          case TYPE_INTN:
    887            u.l = nas ? nap->u.i : va_arg(ap, int);
    888            if (u.l < 0) {
    889              u.l = -u.l;
    890              flags |= FLAG_NEG;
    891            }
    892            goto do_long;
    893          case TYPE_UINTN:
    894            u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int));
    895            goto do_long;
    896 
    897          case TYPE_INT32:
    898            u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32);
    899            if (u.l < 0) {
    900              u.l = -u.l;
    901              flags |= FLAG_NEG;
    902            }
    903            goto do_long;
    904          case TYPE_UINT32:
    905            u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32));
    906          do_long:
    907            rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
    908            if (rv < 0) {
    909              return rv;
    910            }
    911            break;
    912 
    913          case TYPE_INT64:
    914            u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64);
    915            if (!LL_GE_ZERO(u.ll)) {
    916              LL_NEG(u.ll, u.ll);
    917              flags |= FLAG_NEG;
    918            }
    919            goto do_longlong;
    920          case TYPE_UINT64:
    921            u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64);
    922          do_longlong:
    923            rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
    924            if (rv < 0) {
    925              return rv;
    926            }
    927            break;
    928        }
    929        break;
    930 
    931      case 'e':
    932      case 'E':
    933      case 'f':
    934      case 'g':
    935        u.d = nas ? nap->u.d : va_arg(ap, double);
    936        if (nas != NULL) {
    937          i = fmt - dolPt;
    938          if (i < sizeof(pattern)) {
    939            pattern[0] = '%';
    940            memcpy(&pattern[1], dolPt, i);
    941            rv = cvt_f(ss, u.d, pattern, &pattern[i + 1]);
    942          }
    943        } else {
    944          rv = cvt_f(ss, u.d, fmt0, fmt);
    945        }
    946 
    947        if (rv < 0) {
    948          return rv;
    949        }
    950        break;
    951 
    952      case 'c':
    953        u.ch = nas ? nap->u.i : va_arg(ap, int);
    954        if ((flags & FLAG_LEFT) == 0) {
    955          while (width-- > 1) {
    956            rv = (*ss->stuff)(ss, " ", 1);
    957            if (rv < 0) {
    958              return rv;
    959            }
    960          }
    961        }
    962        rv = (*ss->stuff)(ss, &u.ch, 1);
    963        if (rv < 0) {
    964          return rv;
    965        }
    966        if (flags & FLAG_LEFT) {
    967          while (width-- > 1) {
    968            rv = (*ss->stuff)(ss, " ", 1);
    969            if (rv < 0) {
    970              return rv;
    971            }
    972          }
    973        }
    974        break;
    975 
    976      case 'p':
    977        if (sizeof(void*) == sizeof(PRInt32)) {
    978          type = TYPE_UINT32;
    979        } else if (sizeof(void*) == sizeof(PRInt64)) {
    980          type = TYPE_UINT64;
    981        } else if (sizeof(void*) == sizeof(int)) {
    982          type = TYPE_UINTN;
    983        } else {
    984          PR_ASSERT(0);
    985          break;
    986        }
    987        radix = 16;
    988        goto fetch_and_convert;
    989 
    990 #ifndef WIN32
    991      case 'S':
    992        /* XXX not supported I suppose */
    993        PR_ASSERT(0);
    994        break;
    995 #endif
    996 
    997 #if 0
    998            case 'C':
    999            case 'E':
   1000            case 'G':
   1001                /* XXX not supported I suppose */
   1002                PR_ASSERT(0);
   1003                break;
   1004 #endif
   1005 
   1006 #ifdef WIN32
   1007      case 'S':
   1008        u.ws = nas ? nap->u.ws : va_arg(ap, const WCHAR*);
   1009 
   1010        /* Get the required size in rv */
   1011        rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL);
   1012        if (rv == 0) {
   1013          rv = 1;
   1014        }
   1015        pBuf = PR_MALLOC(rv);
   1016        WideCharToMultiByte(CP_ACP, 0, u.ws, -1, pBuf, (int)rv, NULL, NULL);
   1017        pBuf[rv - 1] = '\0';
   1018 
   1019        rv = cvt_s(ss, pBuf, width, prec, flags);
   1020 
   1021        /* We don't need the allocated buffer anymore */
   1022        PR_Free(pBuf);
   1023        if (rv < 0) {
   1024          return rv;
   1025        }
   1026        break;
   1027 
   1028 #endif
   1029 
   1030      case 's':
   1031        u.s = nas ? nap->u.s : va_arg(ap, const char*);
   1032        rv = cvt_s(ss, u.s, width, prec, flags);
   1033        if (rv < 0) {
   1034          return rv;
   1035        }
   1036        break;
   1037 
   1038      case 'n':
   1039        u.ip = nas ? nap->u.ip : va_arg(ap, int*);
   1040        if (u.ip) {
   1041          *u.ip = ss->cur - ss->base;
   1042        }
   1043        break;
   1044 
   1045      default:
   1046        /* Not a % token after all... skip it */
   1047 #if 0
   1048                PR_ASSERT(0);
   1049 #endif
   1050        rv = (*ss->stuff)(ss, "%", 1);
   1051        if (rv < 0) {
   1052          return rv;
   1053        }
   1054        rv = (*ss->stuff)(ss, fmt - 1, 1);
   1055        if (rv < 0) {
   1056          return rv;
   1057        }
   1058    }
   1059  }
   1060 
   1061  /* Stuff trailing NUL */
   1062  rv = (*ss->stuff)(ss, "\0", 1);
   1063 
   1064  if (nas && (nas != nasArray)) {
   1065    PR_DELETE(nas);
   1066  }
   1067 
   1068  return rv;
   1069 }
   1070 
   1071 /************************************************************************/
   1072 
   1073 static int FuncStuff(SprintfState* ss, const char* sp, PRUint32 len) {
   1074  int rv;
   1075 
   1076  /*
   1077  ** We will add len to ss->maxlen at the end of the function. First check
   1078  ** if ss->maxlen + len would overflow or be greater than PR_INT32_MAX.
   1079  */
   1080  if (PR_UINT32_MAX - ss->maxlen < len || ss->maxlen + len > PR_INT32_MAX) {
   1081    return -1;
   1082  }
   1083  rv = (*ss->func)(ss->arg, sp, len);
   1084  if (rv < 0) {
   1085    return rv;
   1086  }
   1087  ss->maxlen += len;
   1088  return 0;
   1089 }
   1090 
   1091 PR_IMPLEMENT(PRUint32)
   1092 PR_sxprintf(PRStuffFunc func, void* arg, const char* fmt, ...) {
   1093  va_list ap;
   1094  PRUint32 rv;
   1095 
   1096  va_start(ap, fmt);
   1097  rv = PR_vsxprintf(func, arg, fmt, ap);
   1098  va_end(ap);
   1099  return rv;
   1100 }
   1101 
   1102 PR_IMPLEMENT(PRUint32)
   1103 PR_vsxprintf(PRStuffFunc func, void* arg, const char* fmt, va_list ap) {
   1104  SprintfState ss;
   1105  int rv;
   1106 
   1107  ss.stuff = FuncStuff;
   1108  ss.func = func;
   1109  ss.arg = arg;
   1110  ss.maxlen = 0;
   1111  rv = dosprintf(&ss, fmt, ap);
   1112  return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
   1113 }
   1114 
   1115 /*
   1116 ** Stuff routine that automatically grows the malloc'd output buffer
   1117 ** before it overflows.
   1118 */
   1119 static int GrowStuff(SprintfState* ss, const char* sp, PRUint32 len) {
   1120  ptrdiff_t off;
   1121  char* newbase;
   1122  PRUint32 newlen;
   1123 
   1124  off = ss->cur - ss->base;
   1125  if (PR_UINT32_MAX - len < off) {
   1126    /* off + len would be too big. */
   1127    return -1;
   1128  }
   1129  if (off + len >= ss->maxlen) {
   1130    /* Grow the buffer */
   1131    PRUint32 increment = (len > 32) ? len : 32;
   1132    if (PR_UINT32_MAX - ss->maxlen < increment) {
   1133      /* ss->maxlen + increment would overflow. */
   1134      return -1;
   1135    }
   1136    newlen = ss->maxlen + increment;
   1137    if (newlen > PR_INT32_MAX) {
   1138      return -1;
   1139    }
   1140    if (ss->base) {
   1141      newbase = (char*)PR_REALLOC(ss->base, newlen);
   1142    } else {
   1143      newbase = (char*)PR_MALLOC(newlen);
   1144    }
   1145    if (!newbase) {
   1146      /* Ran out of memory */
   1147      return -1;
   1148    }
   1149    ss->base = newbase;
   1150    ss->maxlen = newlen;
   1151    ss->cur = ss->base + off;
   1152  }
   1153 
   1154  /* Copy data */
   1155  while (len) {
   1156    --len;
   1157    *ss->cur++ = *sp++;
   1158  }
   1159  PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
   1160  return 0;
   1161 }
   1162 
   1163 /*
   1164 ** sprintf into a malloc'd buffer
   1165 */
   1166 PR_IMPLEMENT(char*) PR_smprintf(const char* fmt, ...) {
   1167  va_list ap;
   1168  char* rv;
   1169 
   1170  va_start(ap, fmt);
   1171  rv = PR_vsmprintf(fmt, ap);
   1172  va_end(ap);
   1173  return rv;
   1174 }
   1175 
   1176 /*
   1177 ** Free memory allocated, for the caller, by PR_smprintf
   1178 */
   1179 PR_IMPLEMENT(void) PR_smprintf_free(char* mem) { PR_DELETE(mem); }
   1180 
   1181 PR_IMPLEMENT(char*) PR_vsmprintf(const char* fmt, va_list ap) {
   1182  SprintfState ss;
   1183  int rv;
   1184 
   1185  ss.stuff = GrowStuff;
   1186  ss.base = 0;
   1187  ss.cur = 0;
   1188  ss.maxlen = 0;
   1189  rv = dosprintf(&ss, fmt, ap);
   1190  if (rv < 0) {
   1191    if (ss.base) {
   1192      PR_DELETE(ss.base);
   1193    }
   1194    return 0;
   1195  }
   1196  return ss.base;
   1197 }
   1198 
   1199 /*
   1200 ** Stuff routine that discards overflow data
   1201 */
   1202 static int LimitStuff(SprintfState* ss, const char* sp, PRUint32 len) {
   1203  PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
   1204 
   1205  if (len > limit) {
   1206    len = limit;
   1207  }
   1208  while (len) {
   1209    --len;
   1210    *ss->cur++ = *sp++;
   1211  }
   1212  return 0;
   1213 }
   1214 
   1215 /*
   1216 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
   1217 ** when finished.
   1218 */
   1219 PR_IMPLEMENT(PRUint32)
   1220 PR_snprintf(char* out, PRUint32 outlen, const char* fmt, ...) {
   1221  va_list ap;
   1222  PRUint32 rv;
   1223 
   1224  va_start(ap, fmt);
   1225  rv = PR_vsnprintf(out, outlen, fmt, ap);
   1226  va_end(ap);
   1227  return rv;
   1228 }
   1229 
   1230 PR_IMPLEMENT(PRUint32)
   1231 PR_vsnprintf(char* out, PRUint32 outlen, const char* fmt, va_list ap) {
   1232  SprintfState ss;
   1233  PRUint32 n;
   1234 
   1235  PR_ASSERT(outlen != 0 && outlen <= PR_INT32_MAX);
   1236  if (outlen == 0 || outlen > PR_INT32_MAX) {
   1237    return 0;
   1238  }
   1239 
   1240  ss.stuff = LimitStuff;
   1241  ss.base = out;
   1242  ss.cur = out;
   1243  ss.maxlen = outlen;
   1244  (void)dosprintf(&ss, fmt, ap);
   1245 
   1246  /* If we added chars, and we didn't append a null, do it now. */
   1247  if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) {
   1248    *(ss.cur - 1) = '\0';
   1249  }
   1250 
   1251  n = ss.cur - ss.base;
   1252  return n ? n - 1 : n;
   1253 }
   1254 
   1255 PR_IMPLEMENT(char*) PR_sprintf_append(char* last, const char* fmt, ...) {
   1256  va_list ap;
   1257  char* rv;
   1258 
   1259  va_start(ap, fmt);
   1260  rv = PR_vsprintf_append(last, fmt, ap);
   1261  va_end(ap);
   1262  return rv;
   1263 }
   1264 
   1265 PR_IMPLEMENT(char*)
   1266 PR_vsprintf_append(char* last, const char* fmt, va_list ap) {
   1267  SprintfState ss;
   1268  int rv;
   1269 
   1270  ss.stuff = GrowStuff;
   1271  if (last) {
   1272    size_t lastlen = strlen(last);
   1273    if (lastlen > PR_INT32_MAX) {
   1274      return 0;
   1275    }
   1276    ss.base = last;
   1277    ss.cur = last + lastlen;
   1278    ss.maxlen = lastlen;
   1279  } else {
   1280    ss.base = 0;
   1281    ss.cur = 0;
   1282    ss.maxlen = 0;
   1283  }
   1284  rv = dosprintf(&ss, fmt, ap);
   1285  if (rv < 0) {
   1286    if (ss.base) {
   1287      PR_DELETE(ss.base);
   1288    }
   1289    return 0;
   1290  }
   1291  return ss.base;
   1292 }