LCOV - code coverage report
Current view: top level - nsprpub/pr/src/io - prprf.c (source / functions) Hit Total Coverage
Test: output.info Lines: 196 538 36.4 %
Date: 2017-07-14 16:53:18 Functions: 8 11 72.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13