Back to index

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