Back to index

lightning-sunbird  0.9+nobinonly
prprf.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 ** Portable safe sprintf code.
00040 **
00041 ** Author: Kipp E.B. Hickman
00042 */
00043 #include <stdarg.h>
00044 #include <stddef.h>
00045 #include <stdio.h>
00046 #include <string.h>
00047 #include "primpl.h"
00048 #include "prprf.h"
00049 #include "prlong.h"
00050 #include "prlog.h"
00051 #include "prmem.h"
00052 
00053 /*
00054 ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
00055 */
00056 
00057 /*
00058 ** XXX This needs to be internationalized!
00059 */
00060 
00061 typedef struct SprintfStateStr SprintfState;
00062 
00063 struct SprintfStateStr {
00064     int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len);
00065 
00066     char *base;
00067     char *cur;
00068     PRUint32 maxlen;
00069 
00070     int (*func)(void *arg, const char *sp, PRUint32 len);
00071     void *arg;
00072 };
00073 
00074 /*
00075 ** Numbered Argument
00076 */
00077 struct NumArg {
00078     int type;           /* type of the numbered argument    */
00079     union {             /* the numbered argument            */
00080        int i;
00081        unsigned int ui;
00082        PRInt32 i32;
00083        PRUint32 ui32;
00084        PRInt64 ll;
00085        PRUint64 ull;
00086        double d;
00087        const char *s;
00088        int *ip;
00089     } u;
00090 };
00091 
00092 #define NAS_DEFAULT_NUM 20  /* default number of NumberedArgument array */
00093 
00094 
00095 #define TYPE_INT16   0
00096 #define TYPE_UINT16  1
00097 #define TYPE_INTN    2
00098 #define TYPE_UINTN   3
00099 #define TYPE_INT32   4
00100 #define TYPE_UINT32  5
00101 #define TYPE_INT64   6
00102 #define TYPE_UINT64  7
00103 #define TYPE_STRING  8
00104 #define TYPE_DOUBLE  9
00105 #define TYPE_INTSTR  10
00106 #define TYPE_UNKNOWN 20
00107 
00108 #define FLAG_LEFT    0x1
00109 #define FLAG_SIGNED  0x2
00110 #define FLAG_SPACED  0x4
00111 #define FLAG_ZEROS   0x8
00112 #define FLAG_NEG     0x10
00113 
00114 /*
00115 ** Fill into the buffer using the data in src
00116 */
00117 static int fill2(SprintfState *ss, const char *src, int srclen, int width,
00118               int flags)
00119 {
00120     char space = ' ';
00121     int rv;
00122 
00123     width -= srclen;
00124     if ((width > 0) && ((flags & FLAG_LEFT) == 0)) {    /* Right adjusting */
00125        if (flags & FLAG_ZEROS) {
00126            space = '0';
00127        }
00128        while (--width >= 0) {
00129            rv = (*ss->stuff)(ss, &space, 1);
00130            if (rv < 0) {
00131               return rv;
00132            }
00133        }
00134     }
00135 
00136     /* Copy out the source data */
00137     rv = (*ss->stuff)(ss, src, srclen);
00138     if (rv < 0) {
00139        return rv;
00140     }
00141 
00142     if ((width > 0) && ((flags & FLAG_LEFT) != 0)) {    /* Left adjusting */
00143        while (--width >= 0) {
00144            rv = (*ss->stuff)(ss, &space, 1);
00145            if (rv < 0) {
00146               return rv;
00147            }
00148        }
00149     }
00150     return 0;
00151 }
00152 
00153 /*
00154 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
00155 */
00156 static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
00157                 int prec, int type, int flags)
00158 {
00159     int zerowidth = 0;
00160     int precwidth = 0;
00161     int signwidth = 0;
00162     int leftspaces = 0;
00163     int rightspaces = 0;
00164     int cvtwidth;
00165     int rv;
00166     char sign;
00167 
00168     if ((type & 1) == 0) {
00169        if (flags & FLAG_NEG) {
00170            sign = '-';
00171            signwidth = 1;
00172        } else if (flags & FLAG_SIGNED) {
00173            sign = '+';
00174            signwidth = 1;
00175        } else if (flags & FLAG_SPACED) {
00176            sign = ' ';
00177            signwidth = 1;
00178        }
00179     }
00180     cvtwidth = signwidth + srclen;
00181 
00182     if (prec > 0) {
00183        if (prec > srclen) {
00184            precwidth = prec - srclen;            /* Need zero filling */
00185            cvtwidth += precwidth;
00186        }
00187     }
00188 
00189     if ((flags & FLAG_ZEROS) && (prec < 0)) {
00190        if (width > cvtwidth) {
00191            zerowidth = width - cvtwidth;  /* Zero filling */
00192            cvtwidth += zerowidth;
00193        }
00194     }
00195 
00196     if (flags & FLAG_LEFT) {
00197        if (width > cvtwidth) {
00198            /* Space filling on the right (i.e. left adjusting) */
00199            rightspaces = width - cvtwidth;
00200        }
00201     } else {
00202        if (width > cvtwidth) {
00203            /* Space filling on the left (i.e. right adjusting) */
00204            leftspaces = width - cvtwidth;
00205        }
00206     }
00207     while (--leftspaces >= 0) {
00208        rv = (*ss->stuff)(ss, " ", 1);
00209        if (rv < 0) {
00210            return rv;
00211        }
00212     }
00213     if (signwidth) {
00214        rv = (*ss->stuff)(ss, &sign, 1);
00215        if (rv < 0) {
00216            return rv;
00217        }
00218     }
00219     while (--precwidth >= 0) {
00220        rv = (*ss->stuff)(ss, "0", 1);
00221        if (rv < 0) {
00222            return rv;
00223        }
00224     }
00225     while (--zerowidth >= 0) {
00226        rv = (*ss->stuff)(ss, "0", 1);
00227        if (rv < 0) {
00228            return rv;
00229        }
00230     }
00231     rv = (*ss->stuff)(ss, src, srclen);
00232     if (rv < 0) {
00233        return rv;
00234     }
00235     while (--rightspaces >= 0) {
00236        rv = (*ss->stuff)(ss, " ", 1);
00237        if (rv < 0) {
00238            return rv;
00239        }
00240     }
00241     return 0;
00242 }
00243 
00244 /*
00245 ** Convert a long into its printable form
00246 */
00247 static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
00248                int type, int flags, const char *hexp)
00249 {
00250     char cvtbuf[100];
00251     char *cvt;
00252     int digits;
00253 
00254     /* according to the man page this needs to happen */
00255     if ((prec == 0) && (num == 0)) {
00256        return 0;
00257     }
00258 
00259     /*
00260     ** Converting decimal is a little tricky. In the unsigned case we
00261     ** need to stop when we hit 10 digits. In the signed case, we can
00262     ** stop when the number is zero.
00263     */
00264     cvt = cvtbuf + sizeof(cvtbuf);
00265     digits = 0;
00266     while (num) {
00267        int digit = (((unsigned long)num) % radix) & 0xF;
00268        *--cvt = hexp[digit];
00269        digits++;
00270        num = (long)(((unsigned long)num) / radix);
00271     }
00272     if (digits == 0) {
00273        *--cvt = '0';
00274        digits++;
00275     }
00276 
00277     /*
00278     ** Now that we have the number converted without its sign, deal with
00279     ** the sign and zero padding.
00280     */
00281     return fill_n(ss, cvt, digits, width, prec, type, flags);
00282 }
00283 
00284 /*
00285 ** Convert a 64-bit integer into its printable form
00286 */
00287 static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix,
00288                 int type, int flags, const char *hexp)
00289 {
00290     char cvtbuf[100];
00291     char *cvt;
00292     int digits;
00293     PRInt64 rad;
00294 
00295     /* according to the man page this needs to happen */
00296     if ((prec == 0) && (LL_IS_ZERO(num))) {
00297        return 0;
00298     }
00299 
00300     /*
00301     ** Converting decimal is a little tricky. In the unsigned case we
00302     ** need to stop when we hit 10 digits. In the signed case, we can
00303     ** stop when the number is zero.
00304     */
00305     LL_I2L(rad, radix);
00306     cvt = cvtbuf + sizeof(cvtbuf);
00307     digits = 0;
00308     while (!LL_IS_ZERO(num)) {
00309        PRInt32 digit;
00310        PRInt64 quot, rem;
00311        LL_UDIVMOD(&quot, &rem, num, rad);
00312        LL_L2I(digit, rem);
00313        *--cvt = hexp[digit & 0xf];
00314        digits++;
00315        num = quot;
00316     }
00317     if (digits == 0) {
00318        *--cvt = '0';
00319        digits++;
00320     }
00321 
00322     /*
00323     ** Now that we have the number converted without its sign, deal with
00324     ** the sign and zero padding.
00325     */
00326     return fill_n(ss, cvt, digits, width, prec, type, flags);
00327 }
00328 
00329 /*
00330 ** Convert a double precision floating point number into its printable
00331 ** form.
00332 **
00333 ** XXX stop using sprintf to convert floating point
00334 */
00335 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
00336 {
00337     char fin[20];
00338     char fout[300];
00339     int amount = fmt1 - fmt0;
00340 
00341     PR_ASSERT((amount > 0) && (amount < sizeof(fin)));
00342     if (amount >= sizeof(fin)) {
00343        /* Totally bogus % command to sprintf. Just ignore it */
00344        return 0;
00345     }
00346     memcpy(fin, fmt0, amount);
00347     fin[amount] = 0;
00348 
00349     /* Convert floating point using the native sprintf code */
00350 #ifdef DEBUG
00351     {
00352         const char *p = fin;
00353         while (*p) {
00354             PR_ASSERT(*p != 'L');
00355             p++;
00356         }
00357     }
00358 #endif
00359     sprintf(fout, fin, d);
00360 
00361     /*
00362     ** This assert will catch overflow's of fout, when building with
00363     ** debugging on. At least this way we can track down the evil piece
00364     ** of calling code and fix it!
00365     */
00366     PR_ASSERT(strlen(fout) < sizeof(fout));
00367 
00368     return (*ss->stuff)(ss, fout, strlen(fout));
00369 }
00370 
00371 /*
00372 ** Convert a string into its printable form.  "width" is the output
00373 ** width. "prec" is the maximum number of characters of "s" to output,
00374 ** where -1 means until NUL.
00375 */
00376 static int cvt_s(SprintfState *ss, const char *str, int width, int prec,
00377                int flags)
00378 {
00379     int slen;
00380 
00381     if (prec == 0)
00382        return 0;
00383 
00384     /* Limit string length by precision value */
00385     if (!str) {
00386        str = "(null)";
00387     } 
00388     if (prec > 0) {
00389        /* this is:  slen = strnlen(str, prec); */
00390        register const char *s;
00391 
00392        for(s = str; prec && *s; s++, prec-- )
00393            ;
00394        slen = s - str;
00395     } else {
00396        slen = strlen(str);
00397     }
00398 
00399     /* and away we go */
00400     return fill2(ss, str, slen, width, flags);
00401 }
00402 
00403 /*
00404 ** BuildArgArray stands for Numbered Argument list Sprintf
00405 ** for example,  
00406 **     fmp = "%4$i, %2$d, %3s, %1d";
00407 ** the number must start from 1, and no gap among them
00408 */
00409 
00410 static struct NumArg* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArg* nasArray )
00411 {
00412     int number = 0, cn = 0, i;
00413     const char* p;
00414     char  c;
00415     struct NumArg* nas;
00416     
00417 
00418     /*
00419     ** first pass:
00420     ** determine how many legal % I have got, then allocate space
00421     */
00422 
00423     p = fmt;
00424     *rv = 0;
00425     i = 0;
00426     while( ( c = *p++ ) != 0 ){
00427        if( c != '%' )
00428            continue;
00429        if( ( c = *p++ ) == '%' )   /* skip %% case */
00430            continue;
00431 
00432        while( c != 0 ){
00433            if( c > '9' || c < '0' ){
00434               if( c == '$' ){             /* numbered argument case */
00435                   if( i > 0 ){
00436                      *rv = -1;
00437                      return NULL;
00438                   }
00439                   number++;
00440               } else{                     /* non-numbered argument case */
00441                   if( number > 0 ){
00442                      *rv = -1;
00443                      return NULL;
00444                   }
00445                   i = 1;
00446               }
00447               break;
00448            }
00449 
00450            c = *p++;
00451        }
00452     }
00453 
00454     if( number == 0 ){
00455        return NULL;
00456     }
00457 
00458     
00459     if( number > NAS_DEFAULT_NUM ){
00460        nas = (struct NumArg*)PR_MALLOC( number * sizeof( struct NumArg ) );
00461        if( !nas ){
00462            *rv = -1;
00463            return NULL;
00464        }
00465     } else {
00466        nas = nasArray;
00467     }
00468 
00469     for( i = 0; i < number; i++ ){
00470        nas[i].type = TYPE_UNKNOWN;
00471     }
00472 
00473 
00474     /*
00475     ** second pass:
00476     ** set nas[].type
00477     */
00478 
00479     p = fmt;
00480     while( ( c = *p++ ) != 0 ){
00481        if( c != '%' )       continue;
00482            c = *p++;
00483        if( c == '%' )       continue;
00484 
00485        cn = 0;
00486        while( c && c != '$' ){         /* should imporve error check later */
00487            cn = cn*10 + c - '0';
00488            c = *p++;
00489        }
00490 
00491        if( !c || cn < 1 || cn > number ){
00492            *rv = -1;
00493            break;
00494         }
00495 
00496        /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
00497         cn--;
00498        if( nas[cn].type != TYPE_UNKNOWN )
00499            continue;
00500 
00501         c = *p++;
00502 
00503         /* width */
00504         if (c == '*') {
00505            /* not supported feature, for the argument is not numbered */
00506            *rv = -1;
00507            break;
00508        }
00509 
00510        while ((c >= '0') && (c <= '9')) {
00511            c = *p++;
00512        }
00513 
00514        /* precision */
00515        if (c == '.') {
00516            c = *p++;
00517            if (c == '*') {
00518                /* not supported feature, for the argument is not numbered */
00519                *rv = -1;
00520                break;
00521            }
00522 
00523            while ((c >= '0') && (c <= '9')) {
00524               c = *p++;
00525            }
00526        }
00527 
00528        /* size */
00529        nas[cn].type = TYPE_INTN;
00530        if (c == 'h') {
00531            nas[cn].type = TYPE_INT16;
00532            c = *p++;
00533        } else if (c == 'L') {
00534            /* XXX not quite sure here */
00535            nas[cn].type = TYPE_INT64;
00536            c = *p++;
00537        } else if (c == 'l') {
00538            nas[cn].type = TYPE_INT32;
00539            c = *p++;
00540            if (c == 'l') {
00541                nas[cn].type = TYPE_INT64;
00542                c = *p++;
00543            }
00544        }
00545 
00546        /* format */
00547        switch (c) {
00548        case 'd':
00549        case 'c':
00550        case 'i':
00551        case 'o':
00552        case 'u':
00553        case 'x':
00554        case 'X':
00555            break;
00556 
00557        case 'e':
00558        case 'f':
00559        case 'g':
00560            nas[ cn ].type = TYPE_DOUBLE;
00561            break;
00562 
00563        case 'p':
00564            /* XXX should use cpp */
00565            if (sizeof(void *) == sizeof(PRInt32)) {
00566               nas[ cn ].type = TYPE_UINT32;
00567            } else if (sizeof(void *) == sizeof(PRInt64)) {
00568                nas[ cn ].type = TYPE_UINT64;
00569            } else if (sizeof(void *) == sizeof(PRIntn)) {
00570                nas[ cn ].type = TYPE_UINTN;
00571            } else {
00572                nas[ cn ].type = TYPE_UNKNOWN;
00573            }
00574            break;
00575 
00576        case 'C':
00577        case 'S':
00578        case 'E':
00579        case 'G':
00580            /* XXX not supported I suppose */
00581            PR_ASSERT(0);
00582            nas[ cn ].type = TYPE_UNKNOWN;
00583            break;
00584 
00585        case 's':
00586            nas[ cn ].type = TYPE_STRING;
00587            break;
00588 
00589        case 'n':
00590            nas[ cn ].type = TYPE_INTSTR;
00591            break;
00592 
00593        default:
00594            PR_ASSERT(0);
00595            nas[ cn ].type = TYPE_UNKNOWN;
00596            break;
00597        }
00598 
00599        /* get a legal para. */
00600        if( nas[ cn ].type == TYPE_UNKNOWN ){
00601            *rv = -1;
00602            break;
00603        }
00604     }
00605 
00606 
00607     /*
00608     ** third pass
00609     ** fill the nas[cn].ap
00610     */
00611 
00612     if( *rv < 0 ){
00613        if( nas != nasArray )
00614            PR_DELETE( nas );
00615        return NULL;
00616     }
00617 
00618     cn = 0;
00619     while( cn < number ){
00620        if( nas[cn].type == TYPE_UNKNOWN ){
00621            cn++;
00622            continue;
00623        }
00624 
00625        switch( nas[cn].type ){
00626        case TYPE_INT16:
00627        case TYPE_UINT16:
00628        case TYPE_INTN:
00629            nas[cn].u.i = va_arg( ap, int );
00630            break;
00631 
00632        case TYPE_UINTN:
00633            nas[cn].u.ui = va_arg( ap, unsigned int );
00634            break;
00635 
00636        case TYPE_INT32:
00637            nas[cn].u.i32 = va_arg( ap, PRInt32 );
00638            break;
00639 
00640        case TYPE_UINT32:
00641            nas[cn].u.ui32 = va_arg( ap, PRUint32 );
00642            break;
00643 
00644        case TYPE_INT64:
00645            nas[cn].u.ll = va_arg( ap, PRInt64 );
00646            break;
00647 
00648        case TYPE_UINT64:
00649            nas[cn].u.ull = va_arg( ap, PRUint64 );
00650            break;
00651 
00652        case TYPE_STRING:
00653            nas[cn].u.s = va_arg( ap, char* );
00654            break;
00655 
00656        case TYPE_INTSTR:
00657            nas[cn].u.ip = va_arg( ap, int* );
00658            break;
00659 
00660        case TYPE_DOUBLE:
00661            nas[cn].u.d = va_arg( ap, double );
00662            break;
00663 
00664        default:
00665            if( nas != nasArray )
00666               PR_DELETE( nas );
00667            *rv = -1;
00668            return NULL;
00669        }
00670 
00671        cn++;
00672     }
00673 
00674 
00675     return nas;
00676 }
00677 
00678 /*
00679 ** The workhorse sprintf code.
00680 */
00681 static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
00682 {
00683     char c;
00684     int flags, width, prec, radix, type;
00685     union {
00686        char ch;
00687        int i;
00688        long l;
00689        PRInt64 ll;
00690        double d;
00691        const char *s;
00692        int *ip;
00693     } u;
00694     const char *fmt0;
00695     static char *hex = "0123456789abcdef";
00696     static char *HEX = "0123456789ABCDEF";
00697     char *hexp;
00698     int rv, i;
00699     struct NumArg* nas = NULL;
00700     struct NumArg* nap;
00701     struct NumArg  nasArray[ NAS_DEFAULT_NUM ];
00702     char  pattern[20];
00703     const char* dolPt = NULL;  /* in "%4$.2f", dolPt will point to . */
00704 
00705 
00706     /*
00707     ** build an argument array, IF the fmt is numbered argument
00708     ** list style, to contain the Numbered Argument list pointers
00709     */
00710 
00711     nas = BuildArgArray( fmt, ap, &rv, nasArray );
00712     if( rv < 0 ){
00713        /* the fmt contains error Numbered Argument format, jliu@netscape.com */
00714        PR_ASSERT(0);
00715        return rv;
00716     }
00717 
00718     while ((c = *fmt++) != 0) {
00719        if (c != '%') {
00720            rv = (*ss->stuff)(ss, fmt - 1, 1);
00721            if (rv < 0) {
00722               return rv;
00723            }
00724            continue;
00725        }
00726        fmt0 = fmt - 1;
00727 
00728        /*
00729        ** Gobble up the % format string. Hopefully we have handled all
00730        ** of the strange cases!
00731        */
00732        flags = 0;
00733        c = *fmt++;
00734        if (c == '%') {
00735            /* quoting a % with %% */
00736            rv = (*ss->stuff)(ss, fmt - 1, 1);
00737            if (rv < 0) {
00738               return rv;
00739            }
00740            continue;
00741        }
00742 
00743        if( nas != NULL ){
00744            /* the fmt contains the Numbered Arguments feature */
00745            i = 0;
00746            while( c && c != '$' ){     /* should imporve error check later */
00747               i = ( i * 10 ) + ( c - '0' );
00748               c = *fmt++;
00749            }
00750 
00751            if( nas[i-1].type == TYPE_UNKNOWN ){
00752               if( nas && ( nas != nasArray ) )
00753                   PR_DELETE( nas );
00754               return -1;
00755            }
00756 
00757            nap = &nas[i-1];
00758            dolPt = fmt;
00759            c = *fmt++;
00760        }
00761 
00762        /*
00763         * Examine optional flags.  Note that we do not implement the
00764         * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
00765         * somewhat ambiguous and not ideal, which is perhaps why
00766         * the various sprintf() implementations are inconsistent
00767         * on this feature.
00768         */
00769        while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
00770            if (c == '-') flags |= FLAG_LEFT;
00771            if (c == '+') flags |= FLAG_SIGNED;
00772            if (c == ' ') flags |= FLAG_SPACED;
00773            if (c == '0') flags |= FLAG_ZEROS;
00774            c = *fmt++;
00775        }
00776        if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
00777        if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
00778 
00779        /* width */
00780        if (c == '*') {
00781            c = *fmt++;
00782            width = va_arg(ap, int);
00783        } else {
00784            width = 0;
00785            while ((c >= '0') && (c <= '9')) {
00786               width = (width * 10) + (c - '0');
00787               c = *fmt++;
00788            }
00789        }
00790 
00791        /* precision */
00792        prec = -1;
00793        if (c == '.') {
00794            c = *fmt++;
00795            if (c == '*') {
00796               c = *fmt++;
00797               prec = va_arg(ap, int);
00798            } else {
00799               prec = 0;
00800               while ((c >= '0') && (c <= '9')) {
00801                   prec = (prec * 10) + (c - '0');
00802                   c = *fmt++;
00803               }
00804            }
00805        }
00806 
00807        /* size */
00808        type = TYPE_INTN;
00809        if (c == 'h') {
00810            type = TYPE_INT16;
00811            c = *fmt++;
00812        } else if (c == 'L') {
00813            /* XXX not quite sure here */
00814            type = TYPE_INT64;
00815            c = *fmt++;
00816        } else if (c == 'l') {
00817            type = TYPE_INT32;
00818            c = *fmt++;
00819            if (c == 'l') {
00820               type = TYPE_INT64;
00821               c = *fmt++;
00822            }
00823        }
00824 
00825        /* format */
00826        hexp = hex;
00827        switch (c) {
00828          case 'd': case 'i':                     /* decimal/integer */
00829            radix = 10;
00830            goto fetch_and_convert;
00831 
00832          case 'o':                        /* octal */
00833            radix = 8;
00834            type |= 1;
00835            goto fetch_and_convert;
00836 
00837          case 'u':                        /* unsigned decimal */
00838            radix = 10;
00839            type |= 1;
00840            goto fetch_and_convert;
00841 
00842          case 'x':                        /* unsigned hex */
00843            radix = 16;
00844            type |= 1;
00845            goto fetch_and_convert;
00846 
00847          case 'X':                        /* unsigned HEX */
00848            radix = 16;
00849            hexp = HEX;
00850            type |= 1;
00851            goto fetch_and_convert;
00852 
00853          fetch_and_convert:
00854            switch (type) {
00855              case TYPE_INT16:
00856               u.l = nas ? nap->u.i : va_arg(ap, int);
00857               if (u.l < 0) {
00858                   u.l = -u.l;
00859                   flags |= FLAG_NEG;
00860               }
00861               goto do_long;
00862              case TYPE_UINT16:
00863               u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff;
00864               goto do_long;
00865              case TYPE_INTN:
00866               u.l = nas ? nap->u.i : va_arg(ap, int);
00867               if (u.l < 0) {
00868                   u.l = -u.l;
00869                   flags |= FLAG_NEG;
00870               }
00871               goto do_long;
00872              case TYPE_UINTN:
00873               u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int));
00874               goto do_long;
00875 
00876              case TYPE_INT32:
00877               u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32);
00878               if (u.l < 0) {
00879                   u.l = -u.l;
00880                   flags |= FLAG_NEG;
00881               }
00882               goto do_long;
00883              case TYPE_UINT32:
00884               u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32));
00885              do_long:
00886               rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
00887               if (rv < 0) {
00888                   return rv;
00889               }
00890               break;
00891 
00892              case TYPE_INT64:
00893               u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64);
00894               if (!LL_GE_ZERO(u.ll)) {
00895                   LL_NEG(u.ll, u.ll);
00896                   flags |= FLAG_NEG;
00897               }
00898               goto do_longlong;
00899              case TYPE_UINT64:
00900               u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64);
00901              do_longlong:
00902               rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
00903               if (rv < 0) {
00904                   return rv;
00905               }
00906               break;
00907            }
00908            break;
00909 
00910          case 'e':
00911          case 'E':
00912          case 'f':
00913          case 'g':
00914            u.d = nas ? nap->u.d : va_arg(ap, double);
00915            if( nas != NULL ){
00916               i = fmt - dolPt;
00917               if( i < sizeof( pattern ) ){
00918                   pattern[0] = '%';
00919                   memcpy( &pattern[1], dolPt, i );
00920                   rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
00921               }
00922            } else
00923               rv = cvt_f(ss, u.d, fmt0, fmt);
00924 
00925            if (rv < 0) {
00926               return rv;
00927            }
00928            break;
00929 
00930          case 'c':
00931            u.ch = nas ? nap->u.i : va_arg(ap, int);
00932             if ((flags & FLAG_LEFT) == 0) {
00933                 while (width-- > 1) {
00934                     rv = (*ss->stuff)(ss, " ", 1);
00935                     if (rv < 0) {
00936                         return rv;
00937                     }
00938                 }
00939             }
00940            rv = (*ss->stuff)(ss, &u.ch, 1);
00941            if (rv < 0) {
00942               return rv;
00943            }
00944             if (flags & FLAG_LEFT) {
00945                 while (width-- > 1) {
00946                     rv = (*ss->stuff)(ss, " ", 1);
00947                     if (rv < 0) {
00948                         return rv;
00949                     }
00950                 }
00951             }
00952            break;
00953 
00954          case 'p':
00955            if (sizeof(void *) == sizeof(PRInt32)) {
00956               type = TYPE_UINT32;
00957            } else if (sizeof(void *) == sizeof(PRInt64)) {
00958               type = TYPE_UINT64;
00959            } else if (sizeof(void *) == sizeof(int)) {
00960               type = TYPE_UINTN;
00961            } else {
00962               PR_ASSERT(0);
00963               break;
00964            }
00965            radix = 16;
00966            goto fetch_and_convert;
00967 
00968 #if 0
00969          case 'C':
00970          case 'S':
00971          case 'E':
00972          case 'G':
00973            /* XXX not supported I suppose */
00974            PR_ASSERT(0);
00975            break;
00976 #endif
00977 
00978          case 's':
00979            u.s = nas ? nap->u.s : va_arg(ap, const char*);
00980            rv = cvt_s(ss, u.s, width, prec, flags);
00981            if (rv < 0) {
00982               return rv;
00983            }
00984            break;
00985 
00986          case 'n':
00987            u.ip = nas ? nap->u.ip : va_arg(ap, int*);
00988            if (u.ip) {
00989               *u.ip = ss->cur - ss->base;
00990            }
00991            break;
00992 
00993          default:
00994            /* Not a % token after all... skip it */
00995 #if 0
00996            PR_ASSERT(0);
00997 #endif
00998            rv = (*ss->stuff)(ss, "%", 1);
00999            if (rv < 0) {
01000               return rv;
01001            }
01002            rv = (*ss->stuff)(ss, fmt - 1, 1);
01003            if (rv < 0) {
01004               return rv;
01005            }
01006        }
01007     }
01008 
01009     /* Stuff trailing NUL */
01010     rv = (*ss->stuff)(ss, "\0", 1);
01011 
01012     if( nas && ( nas != nasArray ) ){
01013        PR_DELETE( nas );
01014     }
01015 
01016     return rv;
01017 }
01018 
01019 /************************************************************************/
01020 
01021 static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len)
01022 {
01023     int rv;
01024 
01025     rv = (*ss->func)(ss->arg, sp, len);
01026     if (rv < 0) {
01027        return rv;
01028     }
01029     ss->maxlen += len;
01030     return 0;
01031 }
01032 
01033 PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg, 
01034                                  const char *fmt, ...)
01035 {
01036     va_list ap;
01037     PRUint32 rv;
01038 
01039     va_start(ap, fmt);
01040     rv = PR_vsxprintf(func, arg, fmt, ap);
01041     va_end(ap);
01042     return rv;
01043 }
01044 
01045 PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg, 
01046                                   const char *fmt, va_list ap)
01047 {
01048     SprintfState ss;
01049     int rv;
01050 
01051     ss.stuff = FuncStuff;
01052     ss.func = func;
01053     ss.arg = arg;
01054     ss.maxlen = 0;
01055     rv = dosprintf(&ss, fmt, ap);
01056     return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
01057 }
01058 
01059 /*
01060 ** Stuff routine that automatically grows the malloc'd output buffer
01061 ** before it overflows.
01062 */
01063 static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len)
01064 {
01065     ptrdiff_t off;
01066     char *newbase;
01067     PRUint32 newlen;
01068 
01069     off = ss->cur - ss->base;
01070     if (off + len >= ss->maxlen) {
01071        /* Grow the buffer */
01072        newlen = ss->maxlen + ((len > 32) ? len : 32);
01073        if (ss->base) {
01074            newbase = (char*) PR_REALLOC(ss->base, newlen);
01075        } else {
01076            newbase = (char*) PR_MALLOC(newlen);
01077        }
01078        if (!newbase) {
01079            /* Ran out of memory */
01080            return -1;
01081        }
01082        ss->base = newbase;
01083        ss->maxlen = newlen;
01084        ss->cur = ss->base + off;
01085     }
01086 
01087     /* Copy data */
01088     while (len) {
01089        --len;
01090        *ss->cur++ = *sp++;
01091     }
01092     PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
01093     return 0;
01094 }
01095 
01096 /*
01097 ** sprintf into a malloc'd buffer
01098 */
01099 PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...)
01100 {
01101     va_list ap;
01102     char *rv;
01103 
01104     va_start(ap, fmt);
01105     rv = PR_vsmprintf(fmt, ap);
01106     va_end(ap);
01107     return rv;
01108 }
01109 
01110 /*
01111 ** Free memory allocated, for the caller, by PR_smprintf
01112 */
01113 PR_IMPLEMENT(void) PR_smprintf_free(char *mem)
01114 {
01115        PR_DELETE(mem);
01116 }
01117 
01118 PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap)
01119 {
01120     SprintfState ss;
01121     int rv;
01122 
01123     ss.stuff = GrowStuff;
01124     ss.base = 0;
01125     ss.cur = 0;
01126     ss.maxlen = 0;
01127     rv = dosprintf(&ss, fmt, ap);
01128     if (rv < 0) {
01129        if (ss.base) {
01130            PR_DELETE(ss.base);
01131        }
01132        return 0;
01133     }
01134     return ss.base;
01135 }
01136 
01137 /*
01138 ** Stuff routine that discards overflow data
01139 */
01140 static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len)
01141 {
01142     PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
01143 
01144     if (len > limit) {
01145        len = limit;
01146     }
01147     while (len) {
01148        --len;
01149        *ss->cur++ = *sp++;
01150     }
01151     return 0;
01152 }
01153 
01154 /*
01155 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
01156 ** when finished.
01157 */
01158 PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...)
01159 {
01160     va_list ap;
01161     PRUint32 rv;
01162 
01163     PR_ASSERT((PRInt32)outlen > 0);
01164     if ((PRInt32)outlen <= 0) {
01165        return 0;
01166     }
01167 
01168     va_start(ap, fmt);
01169     rv = PR_vsnprintf(out, outlen, fmt, ap);
01170     va_end(ap);
01171     return rv;
01172 }
01173 
01174 PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt,
01175                                   va_list ap)
01176 {
01177     SprintfState ss;
01178     PRUint32 n;
01179 
01180     PR_ASSERT((PRInt32)outlen > 0);
01181     if ((PRInt32)outlen <= 0) {
01182        return 0;
01183     }
01184 
01185     ss.stuff = LimitStuff;
01186     ss.base = out;
01187     ss.cur = out;
01188     ss.maxlen = outlen;
01189     (void) dosprintf(&ss, fmt, ap);
01190 
01191     /* If we added chars, and we didn't append a null, do it now. */
01192     if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
01193         *(ss.cur - 1) = '\0';
01194 
01195     n = ss.cur - ss.base;
01196     return n ? n - 1 : n;
01197 }
01198 
01199 PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...)
01200 {
01201     va_list ap;
01202     char *rv;
01203 
01204     va_start(ap, fmt);
01205     rv = PR_vsprintf_append(last, fmt, ap);
01206     va_end(ap);
01207     return rv;
01208 }
01209 
01210 PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap)
01211 {
01212     SprintfState ss;
01213     int rv;
01214 
01215     ss.stuff = GrowStuff;
01216     if (last) {
01217        int lastlen = strlen(last);
01218        ss.base = last;
01219        ss.cur = last + lastlen;
01220        ss.maxlen = lastlen;
01221     } else {
01222        ss.base = 0;
01223        ss.cur = 0;
01224        ss.maxlen = 0;
01225     }
01226     rv = dosprintf(&ss, fmt, ap);
01227     if (rv < 0) {
01228        if (ss.base) {
01229            PR_DELETE(ss.base);
01230        }
01231        return 0;
01232     }
01233     return ss.base;
01234 }
01235