Code

moving trunk for module inkscape
[inkscape.git] / src / dom / js / jsprf.c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is Mozilla Communicator client code, released
16  * March 31, 1998.
17  *
18  * The Initial Developer of the Original Code is
19  * Netscape Communications Corporation.
20  * Portions created by the Initial Developer are Copyright (C) 1998
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either of the GNU General Public License Version 2 or later (the "GPL"),
27  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
39 /*
40 ** Portable safe sprintf code.
41 **
42 ** Author: Kipp E.B. Hickman
43 */
44 #include "jsstddef.h"
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include "jsprf.h"
50 #include "jslong.h"
51 #include "jsutil.h" /* Added by JSIFY */
53 /*
54 ** Note: on some platforms va_list is defined as an array,
55 ** and requires array notation.
56 */
57 #ifdef HAVE_VA_COPY
58 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo,bar)
59 #elif defined(HAVE_VA_LIST_AS_ARRAY)
60 #define VARARGS_ASSIGN(foo, bar)        foo[0] = bar[0]
61 #else
62 #define VARARGS_ASSIGN(foo, bar)        (foo) = (bar)
63 #endif
65 /*
66 ** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it)
67 */
69 /*
70 ** XXX This needs to be internationalized!
71 */
73 typedef struct SprintfStateStr SprintfState;
75 struct SprintfStateStr {
76     int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len);
78     char *base;
79     char *cur;
80     JSUint32 maxlen;
82     int (*func)(void *arg, const char *sp, JSUint32 len);
83     void *arg;
84 };
86 /*
87 ** Numbered Arguement State
88 */
89 struct NumArgState{
90     int     type;               /* type of the current ap                    */
91     va_list ap;                 /* point to the corresponding position on ap */
92 };
94 #define NAS_DEFAULT_NUM 20  /* default number of NumberedArgumentState array */
97 #define TYPE_INT16      0
98 #define TYPE_UINT16     1
99 #define TYPE_INTN       2
100 #define TYPE_UINTN      3
101 #define TYPE_INT32      4
102 #define TYPE_UINT32     5
103 #define TYPE_INT64      6
104 #define TYPE_UINT64     7
105 #define TYPE_STRING     8
106 #define TYPE_DOUBLE     9
107 #define TYPE_INTSTR     10
108 #define TYPE_UNKNOWN    20
110 #define FLAG_LEFT       0x1
111 #define FLAG_SIGNED     0x2
112 #define FLAG_SPACED     0x4
113 #define FLAG_ZEROS      0x8
114 #define FLAG_NEG        0x10
116 /*
117 ** Fill into the buffer using the data in src
118 */
119 static int fill2(SprintfState *ss, const char *src, int srclen, int width,
120                 int flags)
122     char space = ' ';
123     int rv;
125     width -= srclen;
126     if ((width > 0) && ((flags & FLAG_LEFT) == 0)) {    /* Right adjusting */
127         if (flags & FLAG_ZEROS) {
128             space = '0';
129         }
130         while (--width >= 0) {
131             rv = (*ss->stuff)(ss, &space, 1);
132             if (rv < 0) {
133                 return rv;
134             }
135         }
136     }
138     /* Copy out the source data */
139     rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
140     if (rv < 0) {
141         return rv;
142     }
144     if ((width > 0) && ((flags & FLAG_LEFT) != 0)) {    /* Left adjusting */
145         while (--width >= 0) {
146             rv = (*ss->stuff)(ss, &space, 1);
147             if (rv < 0) {
148                 return rv;
149             }
150         }
151     }
152     return 0;
155 /*
156 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
157 */
158 static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
159                   int prec, int type, int flags)
161     int zerowidth = 0;
162     int precwidth = 0;
163     int signwidth = 0;
164     int leftspaces = 0;
165     int rightspaces = 0;
166     int cvtwidth;
167     int rv;
168     char sign;
170     if ((type & 1) == 0) {
171         if (flags & FLAG_NEG) {
172             sign = '-';
173             signwidth = 1;
174         } else if (flags & FLAG_SIGNED) {
175             sign = '+';
176             signwidth = 1;
177         } else if (flags & FLAG_SPACED) {
178             sign = ' ';
179             signwidth = 1;
180         }
181     }
182     cvtwidth = signwidth + srclen;
184     if (prec > 0) {
185         if (prec > srclen) {
186             precwidth = prec - srclen;          /* Need zero filling */
187             cvtwidth += precwidth;
188         }
189     }
191     if ((flags & FLAG_ZEROS) && (prec < 0)) {
192         if (width > cvtwidth) {
193             zerowidth = width - cvtwidth;       /* Zero filling */
194             cvtwidth += zerowidth;
195         }
196     }
198     if (flags & FLAG_LEFT) {
199         if (width > cvtwidth) {
200             /* Space filling on the right (i.e. left adjusting) */
201             rightspaces = width - cvtwidth;
202         }
203     } else {
204         if (width > cvtwidth) {
205             /* Space filling on the left (i.e. right adjusting) */
206             leftspaces = width - cvtwidth;
207         }
208     }
209     while (--leftspaces >= 0) {
210         rv = (*ss->stuff)(ss, " ", 1);
211         if (rv < 0) {
212             return rv;
213         }
214     }
215     if (signwidth) {
216         rv = (*ss->stuff)(ss, &sign, 1);
217         if (rv < 0) {
218             return rv;
219         }
220     }
221     while (--precwidth >= 0) {
222         rv = (*ss->stuff)(ss, "0", 1);
223         if (rv < 0) {
224             return rv;
225         }
226     }
227     while (--zerowidth >= 0) {
228         rv = (*ss->stuff)(ss, "0", 1);
229         if (rv < 0) {
230             return rv;
231         }
232     }
233     rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
234     if (rv < 0) {
235         return rv;
236     }
237     while (--rightspaces >= 0) {
238         rv = (*ss->stuff)(ss, " ", 1);
239         if (rv < 0) {
240             return rv;
241         }
242     }
243     return 0;
246 /*
247 ** Convert a long into its printable form
248 */
249 static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
250                  int type, int flags, const char *hexp)
252     char cvtbuf[100];
253     char *cvt;
254     int digits;
256     /* according to the man page this needs to happen */
257     if ((prec == 0) && (num == 0)) {
258         return 0;
259     }
261     /*
262     ** Converting decimal is a little tricky. In the unsigned case we
263     ** need to stop when we hit 10 digits. In the signed case, we can
264     ** stop when the number is zero.
265     */
266     cvt = cvtbuf + sizeof(cvtbuf);
267     digits = 0;
268     while (num) {
269         int digit = (((unsigned long)num) % radix) & 0xF;
270         *--cvt = hexp[digit];
271         digits++;
272         num = (long)(((unsigned long)num) / radix);
273     }
274     if (digits == 0) {
275         *--cvt = '0';
276         digits++;
277     }
279     /*
280     ** Now that we have the number converted without its sign, deal with
281     ** the sign and zero padding.
282     */
283     return fill_n(ss, cvt, digits, width, prec, type, flags);
286 /*
287 ** Convert a 64-bit integer into its printable form
288 */
289 static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix,
290                   int type, int flags, const char *hexp)
292     char cvtbuf[100];
293     char *cvt;
294     int digits;
295     JSInt64 rad;
297     /* according to the man page this needs to happen */
298     if ((prec == 0) && (JSLL_IS_ZERO(num))) {
299         return 0;
300     }
302     /*
303     ** Converting decimal is a little tricky. In the unsigned case we
304     ** need to stop when we hit 10 digits. In the signed case, we can
305     ** stop when the number is zero.
306     */
307     JSLL_I2L(rad, radix);
308     cvt = cvtbuf + sizeof(cvtbuf);
309     digits = 0;
310     while (!JSLL_IS_ZERO(num)) {
311         JSInt32 digit;
312         JSInt64 quot, rem;
313         JSLL_UDIVMOD(&quot, &rem, num, rad);
314         JSLL_L2I(digit, rem);
315         *--cvt = hexp[digit & 0xf];
316         digits++;
317         num = quot;
318     }
319     if (digits == 0) {
320         *--cvt = '0';
321         digits++;
322     }
324     /*
325     ** Now that we have the number converted without its sign, deal with
326     ** the sign and zero padding.
327     */
328     return fill_n(ss, cvt, digits, width, prec, type, flags);
331 /*
332 ** Convert a double precision floating point number into its printable
333 ** form.
334 **
335 ** XXX stop using sprintf to convert floating point
336 */
337 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
339     char fin[20];
340     char fout[300];
341     int amount = fmt1 - fmt0;
343     JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
344     if (amount >= (int)sizeof(fin)) {
345         /* Totally bogus % command to sprintf. Just ignore it */
346         return 0;
347     }
348     memcpy(fin, fmt0, (size_t)amount);
349     fin[amount] = 0;
351     /* Convert floating point using the native sprintf code */
352 #ifdef DEBUG
353     {
354         const char *p = fin;
355         while (*p) {
356             JS_ASSERT(*p != 'L');
357             p++;
358         }
359     }
360 #endif
361     sprintf(fout, fin, d);
363     /*
364     ** This assert will catch overflow's of fout, when building with
365     ** debugging on. At least this way we can track down the evil piece
366     ** of calling code and fix it!
367     */
368     JS_ASSERT(strlen(fout) < sizeof(fout));
370     return (*ss->stuff)(ss, fout, strlen(fout));
373 /*
374 ** Convert a string into its printable form.  "width" is the output
375 ** width. "prec" is the maximum number of characters of "s" to output,
376 ** where -1 means until NUL.
377 */
378 static int cvt_s(SprintfState *ss, const char *s, int width, int prec,
379                  int flags)
381     int slen;
383     if (prec == 0)
384         return 0;
386     /* Limit string length by precision value */
387     slen = s ? strlen(s) : 6;
388     if (prec > 0) {
389         if (prec < slen) {
390             slen = prec;
391         }
392     }
394     /* and away we go */
395     return fill2(ss, s ? s : "(null)", slen, width, flags);
398 /*
399 ** BiuldArgArray stands for Numbered Argument list Sprintf
400 ** for example,
401 **      fmp = "%4$i, %2$d, %3s, %1d";
402 ** the number must start from 1, and no gap among them
403 */
405 static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )
407     int number = 0, cn = 0, i;
408     const char* p;
409     char  c;
410     struct NumArgState* nas;
413     /*
414     **  first pass:
415     **  detemine how many legal % I have got, then allocate space
416     */
418     p = fmt;
419     *rv = 0;
420     i = 0;
421     while( ( c = *p++ ) != 0 ){
422         if( c != '%' )
423             continue;
424         if( ( c = *p++ ) == '%' )       /* skip %% case */
425             continue;
427         while( c != 0 ){
428             if( c > '9' || c < '0' ){
429                 if( c == '$' ){         /* numbered argument csae */
430                     if( i > 0 ){
431                         *rv = -1;
432                         return NULL;
433                     }
434                     number++;
435                 } else {                /* non-numbered argument case */
436                     if( number > 0 ){
437                         *rv = -1;
438                         return NULL;
439                     }
440                     i = 1;
441                 }
442                 break;
443             }
445             c = *p++;
446         }
447     }
449     if( number == 0 ){
450         return NULL;
451     }
454     if( number > NAS_DEFAULT_NUM ){
455         nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) );
456         if( !nas ){
457             *rv = -1;
458             return NULL;
459         }
460     } else {
461         nas = nasArray;
462     }
464     for( i = 0; i < number; i++ ){
465         nas[i].type = TYPE_UNKNOWN;
466     }
469     /*
470     ** second pass:
471     ** set nas[].type
472     */
474     p = fmt;
475     while( ( c = *p++ ) != 0 ){
476         if( c != '%' )  continue;
477             c = *p++;
478         if( c == '%' )  continue;
480         cn = 0;
481         while( c && c != '$' ){     /* should imporve error check later */
482             cn = cn*10 + c - '0';
483             c = *p++;
484         }
486         if( !c || cn < 1 || cn > number ){
487             *rv = -1;
488             break;
489         }
491         /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
492         cn--;
493         if( nas[cn].type != TYPE_UNKNOWN )
494             continue;
496         c = *p++;
498         /* width */
499         if (c == '*') {
500             /* not supported feature, for the argument is not numbered */
501             *rv = -1;
502             break;
503         }
505         while ((c >= '0') && (c <= '9')) {
506             c = *p++;
507         }
509         /* precision */
510         if (c == '.') {
511             c = *p++;
512             if (c == '*') {
513                 /* not supported feature, for the argument is not numbered */
514                 *rv = -1;
515                 break;
516             }
518             while ((c >= '0') && (c <= '9')) {
519                 c = *p++;
520             }
521         }
523         /* size */
524         nas[cn].type = TYPE_INTN;
525         if (c == 'h') {
526             nas[cn].type = TYPE_INT16;
527             c = *p++;
528         } else if (c == 'L') {
529             /* XXX not quite sure here */
530             nas[cn].type = TYPE_INT64;
531             c = *p++;
532         } else if (c == 'l') {
533             nas[cn].type = TYPE_INT32;
534             c = *p++;
535             if (c == 'l') {
536                 nas[cn].type = TYPE_INT64;
537                 c = *p++;
538             }
539         }
541         /* format */
542         switch (c) {
543         case 'd':
544         case 'c':
545         case 'i':
546         case 'o':
547         case 'u':
548         case 'x':
549         case 'X':
550             break;
552         case 'e':
553         case 'f':
554         case 'g':
555             nas[ cn ].type = TYPE_DOUBLE;
556             break;
558         case 'p':
559             /* XXX should use cpp */
560             if (sizeof(void *) == sizeof(JSInt32)) {
561                 nas[ cn ].type = TYPE_UINT32;
562             } else if (sizeof(void *) == sizeof(JSInt64)) {
563                 nas[ cn ].type = TYPE_UINT64;
564             } else if (sizeof(void *) == sizeof(JSIntn)) {
565                 nas[ cn ].type = TYPE_UINTN;
566             } else {
567                 nas[ cn ].type = TYPE_UNKNOWN;
568             }
569             break;
571         case 'C':
572         case 'S':
573         case 'E':
574         case 'G':
575             /* XXX not supported I suppose */
576             JS_ASSERT(0);
577             nas[ cn ].type = TYPE_UNKNOWN;
578             break;
580         case 's':
581             nas[ cn ].type = TYPE_STRING;
582             break;
584         case 'n':
585             nas[ cn ].type = TYPE_INTSTR;
586             break;
588         default:
589             JS_ASSERT(0);
590             nas[ cn ].type = TYPE_UNKNOWN;
591             break;
592         }
594         /* get a legal para. */
595         if( nas[ cn ].type == TYPE_UNKNOWN ){
596             *rv = -1;
597             break;
598         }
599     }
602     /*
603     ** third pass
604     ** fill the nas[cn].ap
605     */
607     if( *rv < 0 ){
608         if( nas != nasArray )
609             JS_DELETE( nas );
610         return NULL;
611     }
613     cn = 0;
614     while( cn < number ){
615         if( nas[cn].type == TYPE_UNKNOWN ){
616             cn++;
617             continue;
618         }
620         VARARGS_ASSIGN(nas[cn].ap, ap);
622         switch( nas[cn].type ){
623         case TYPE_INT16:
624         case TYPE_UINT16:
625         case TYPE_INTN:
626         case TYPE_UINTN:                (void)va_arg( ap, JSIntn );             break;
628         case TYPE_INT32:                (void)va_arg( ap, JSInt32 );            break;
630         case TYPE_UINT32:       (void)va_arg( ap, JSUint32 );   break;
632         case TYPE_INT64:        (void)va_arg( ap, JSInt64 );            break;
634         case TYPE_UINT64:       (void)va_arg( ap, JSUint64 );           break;
636         case TYPE_STRING:       (void)va_arg( ap, char* );              break;
638         case TYPE_INTSTR:       (void)va_arg( ap, JSIntn* );            break;
640         case TYPE_DOUBLE:       (void)va_arg( ap, double );             break;
642         default:
643             if( nas != nasArray )
644                 JS_DELETE( nas );
645             *rv = -1;
646             return NULL;
647         }
649         cn++;
650     }
653     return nas;
656 /*
657 ** The workhorse sprintf code.
658 */
659 static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
661     char c;
662     int flags, width, prec, radix, type;
663     union {
664         char ch;
665         int i;
666         long l;
667         JSInt64 ll;
668         double d;
669         const char *s;
670         int *ip;
671     } u;
672     const char *fmt0;
673     static char *hex = "0123456789abcdef";
674     static char *HEX = "0123456789ABCDEF";
675     char *hexp;
676     int rv, i;
677     struct NumArgState* nas = NULL;
678     struct NumArgState  nasArray[ NAS_DEFAULT_NUM ];
679     char  pattern[20];
680     const char* dolPt = NULL;  /* in "%4$.2f", dolPt will poiont to . */
683     /*
684     ** build an argument array, IF the fmt is numbered argument
685     ** list style, to contain the Numbered Argument list pointers
686     */
688     nas = BuildArgArray( fmt, ap, &rv, nasArray );
689     if( rv < 0 ){
690         /* the fmt contains error Numbered Argument format, jliu@netscape.com */
691         JS_ASSERT(0);
692         return rv;
693     }
695     while ((c = *fmt++) != 0) {
696         if (c != '%') {
697             rv = (*ss->stuff)(ss, fmt - 1, 1);
698             if (rv < 0) {
699                 return rv;
700             }
701             continue;
702         }
703         fmt0 = fmt - 1;
705         /*
706         ** Gobble up the % format string. Hopefully we have handled all
707         ** of the strange cases!
708         */
709         flags = 0;
710         c = *fmt++;
711         if (c == '%') {
712             /* quoting a % with %% */
713             rv = (*ss->stuff)(ss, fmt - 1, 1);
714             if (rv < 0) {
715                 return rv;
716             }
717             continue;
718         }
720         if( nas != NULL ){
721             /* the fmt contains the Numbered Arguments feature */
722             i = 0;
723             while( c && c != '$' ){         /* should imporve error check later */
724                 i = ( i * 10 ) + ( c - '0' );
725                 c = *fmt++;
726             }
728             if( nas[i-1].type == TYPE_UNKNOWN ){
729                 if( nas && ( nas != nasArray ) )
730                     JS_DELETE( nas );
731                 return -1;
732             }
734             ap = nas[i-1].ap;
735             dolPt = fmt;
736             c = *fmt++;
737         }
739         /*
740          * Examine optional flags.  Note that we do not implement the
741          * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
742          * somewhat ambiguous and not ideal, which is perhaps why
743          * the various sprintf() implementations are inconsistent
744          * on this feature.
745          */
746         while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
747             if (c == '-') flags |= FLAG_LEFT;
748             if (c == '+') flags |= FLAG_SIGNED;
749             if (c == ' ') flags |= FLAG_SPACED;
750             if (c == '0') flags |= FLAG_ZEROS;
751             c = *fmt++;
752         }
753         if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
754         if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
756         /* width */
757         if (c == '*') {
758             c = *fmt++;
759             width = va_arg(ap, int);
760         } else {
761             width = 0;
762             while ((c >= '0') && (c <= '9')) {
763                 width = (width * 10) + (c - '0');
764                 c = *fmt++;
765             }
766         }
768         /* precision */
769         prec = -1;
770         if (c == '.') {
771             c = *fmt++;
772             if (c == '*') {
773                 c = *fmt++;
774                 prec = va_arg(ap, int);
775             } else {
776                 prec = 0;
777                 while ((c >= '0') && (c <= '9')) {
778                     prec = (prec * 10) + (c - '0');
779                     c = *fmt++;
780                 }
781             }
782         }
784         /* size */
785         type = TYPE_INTN;
786         if (c == 'h') {
787             type = TYPE_INT16;
788             c = *fmt++;
789         } else if (c == 'L') {
790             /* XXX not quite sure here */
791             type = TYPE_INT64;
792             c = *fmt++;
793         } else if (c == 'l') {
794             type = TYPE_INT32;
795             c = *fmt++;
796             if (c == 'l') {
797                 type = TYPE_INT64;
798                 c = *fmt++;
799             }
800         }
802         /* format */
803         hexp = hex;
804         switch (c) {
805           case 'd': case 'i':                   /* decimal/integer */
806             radix = 10;
807             goto fetch_and_convert;
809           case 'o':                             /* octal */
810             radix = 8;
811             type |= 1;
812             goto fetch_and_convert;
814           case 'u':                             /* unsigned decimal */
815             radix = 10;
816             type |= 1;
817             goto fetch_and_convert;
819           case 'x':                             /* unsigned hex */
820             radix = 16;
821             type |= 1;
822             goto fetch_and_convert;
824           case 'X':                             /* unsigned HEX */
825             radix = 16;
826             hexp = HEX;
827             type |= 1;
828             goto fetch_and_convert;
830           fetch_and_convert:
831             switch (type) {
832               case TYPE_INT16:
833                 u.l = va_arg(ap, int);
834                 if (u.l < 0) {
835                     u.l = -u.l;
836                     flags |= FLAG_NEG;
837                 }
838                 goto do_long;
839               case TYPE_UINT16:
840                 u.l = va_arg(ap, int) & 0xffff;
841                 goto do_long;
842               case TYPE_INTN:
843                 u.l = va_arg(ap, int);
844                 if (u.l < 0) {
845                     u.l = -u.l;
846                     flags |= FLAG_NEG;
847                 }
848                 goto do_long;
849               case TYPE_UINTN:
850                 u.l = (long)va_arg(ap, unsigned int);
851                 goto do_long;
853               case TYPE_INT32:
854                 u.l = va_arg(ap, JSInt32);
855                 if (u.l < 0) {
856                     u.l = -u.l;
857                     flags |= FLAG_NEG;
858                 }
859                 goto do_long;
860               case TYPE_UINT32:
861                 u.l = (long)va_arg(ap, JSUint32);
862               do_long:
863                 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
864                 if (rv < 0) {
865                     return rv;
866                 }
867                 break;
869               case TYPE_INT64:
870                 u.ll = va_arg(ap, JSInt64);
871                 if (!JSLL_GE_ZERO(u.ll)) {
872                     JSLL_NEG(u.ll, u.ll);
873                     flags |= FLAG_NEG;
874                 }
875                 goto do_longlong;
876               case TYPE_UINT64:
877                 u.ll = va_arg(ap, JSUint64);
878               do_longlong:
879                 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
880                 if (rv < 0) {
881                     return rv;
882                 }
883                 break;
884             }
885             break;
887           case 'e':
888           case 'E':
889           case 'f':
890           case 'g':
891             u.d = va_arg(ap, double);
892             if( nas != NULL ){
893                 i = fmt - dolPt;
894                 if( i < (int)sizeof( pattern ) ){
895                     pattern[0] = '%';
896                     memcpy( &pattern[1], dolPt, (size_t)i );
897                     rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
898                 }
899             } else
900                 rv = cvt_f(ss, u.d, fmt0, fmt);
902             if (rv < 0) {
903                 return rv;
904             }
905             break;
907           case 'c':
908             u.ch = va_arg(ap, int);
909             if ((flags & FLAG_LEFT) == 0) {
910                 while (width-- > 1) {
911                     rv = (*ss->stuff)(ss, " ", 1);
912                     if (rv < 0) {
913                         return rv;
914                     }
915                 }
916             }
917             rv = (*ss->stuff)(ss, &u.ch, 1);
918             if (rv < 0) {
919                 return rv;
920             }
921             if (flags & FLAG_LEFT) {
922                 while (width-- > 1) {
923                     rv = (*ss->stuff)(ss, " ", 1);
924                     if (rv < 0) {
925                         return rv;
926                     }
927                 }
928             }
929             break;
931           case 'p':
932             if (sizeof(void *) == sizeof(JSInt32)) {
933                 type = TYPE_UINT32;
934             } else if (sizeof(void *) == sizeof(JSInt64)) {
935                 type = TYPE_UINT64;
936             } else if (sizeof(void *) == sizeof(int)) {
937                 type = TYPE_UINTN;
938             } else {
939                 JS_ASSERT(0);
940                 break;
941             }
942             radix = 16;
943             goto fetch_and_convert;
945 #if 0
946           case 'C':
947           case 'S':
948           case 'E':
949           case 'G':
950             /* XXX not supported I suppose */
951             JS_ASSERT(0);
952             break;
953 #endif
955           case 's':
956             u.s = va_arg(ap, const char*);
957             rv = cvt_s(ss, u.s, width, prec, flags);
958             if (rv < 0) {
959                 return rv;
960             }
961             break;
963           case 'n':
964             u.ip = va_arg(ap, int*);
965             if (u.ip) {
966                 *u.ip = ss->cur - ss->base;
967             }
968             break;
970           default:
971             /* Not a % token after all... skip it */
972 #if 0
973             JS_ASSERT(0);
974 #endif
975             rv = (*ss->stuff)(ss, "%", 1);
976             if (rv < 0) {
977                 return rv;
978             }
979             rv = (*ss->stuff)(ss, fmt - 1, 1);
980             if (rv < 0) {
981                 return rv;
982             }
983         }
984     }
986     /* Stuff trailing NUL */
987     rv = (*ss->stuff)(ss, "\0", 1);
989     if( nas && ( nas != nasArray ) ){
990         JS_DELETE( nas );
991     }
993     return rv;
996 /************************************************************************/
998 static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)
1000     int rv;
1002     rv = (*ss->func)(ss->arg, sp, len);
1003     if (rv < 0) {
1004         return rv;
1005     }
1006     ss->maxlen += len;
1007     return 0;
1010 JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg,
1011                                     const char *fmt, ...)
1013     va_list ap;
1014     int rv;
1016     va_start(ap, fmt);
1017     rv = JS_vsxprintf(func, arg, fmt, ap);
1018     va_end(ap);
1019     return rv;
1022 JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg,
1023                                      const char *fmt, va_list ap)
1025     SprintfState ss;
1026     int rv;
1028     ss.stuff = FuncStuff;
1029     ss.func = func;
1030     ss.arg = arg;
1031     ss.maxlen = 0;
1032     rv = dosprintf(&ss, fmt, ap);
1033     return (rv < 0) ? (JSUint32)-1 : ss.maxlen;
1036 /*
1037 ** Stuff routine that automatically grows the malloc'd output buffer
1038 ** before it overflows.
1039 */
1040 static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len)
1042     ptrdiff_t off;
1043     char *newbase;
1044     JSUint32 newlen;
1046     off = ss->cur - ss->base;
1047     if (off + len >= ss->maxlen) {
1048         /* Grow the buffer */
1049         newlen = ss->maxlen + ((len > 32) ? len : 32);
1050         if (ss->base) {
1051             newbase = (char*) realloc(ss->base, newlen);
1052         } else {
1053             newbase = (char*) malloc(newlen);
1054         }
1055         if (!newbase) {
1056             /* Ran out of memory */
1057             return -1;
1058         }
1059         ss->base = newbase;
1060         ss->maxlen = newlen;
1061         ss->cur = ss->base + off;
1062     }
1064     /* Copy data */
1065     while (len) {
1066         --len;
1067         *ss->cur++ = *sp++;
1068     }
1069     JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen);
1070     return 0;
1073 /*
1074 ** sprintf into a malloc'd buffer
1075 */
1076 JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...)
1078     va_list ap;
1079     char *rv;
1081     va_start(ap, fmt);
1082     rv = JS_vsmprintf(fmt, ap);
1083     va_end(ap);
1084     return rv;
1087 /*
1088 ** Free memory allocated, for the caller, by JS_smprintf
1089 */
1090 JS_PUBLIC_API(void) JS_smprintf_free(char *mem)
1092         JS_DELETE(mem);
1095 JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap)
1097     SprintfState ss;
1098     int rv;
1100     ss.stuff = GrowStuff;
1101     ss.base = 0;
1102     ss.cur = 0;
1103     ss.maxlen = 0;
1104     rv = dosprintf(&ss, fmt, ap);
1105     if (rv < 0) {
1106         if (ss.base) {
1107             JS_DELETE(ss.base);
1108         }
1109         return 0;
1110     }
1111     return ss.base;
1114 /*
1115 ** Stuff routine that discards overflow data
1116 */
1117 static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len)
1119     JSUint32 limit = ss->maxlen - (ss->cur - ss->base);
1121     if (len > limit) {
1122         len = limit;
1123     }
1124     while (len) {
1125         --len;
1126         *ss->cur++ = *sp++;
1127     }
1128     return 0;
1131 /*
1132 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1133 ** when finished.
1134 */
1135 JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...)
1137     va_list ap;
1138     int rv;
1140     JS_ASSERT((JSInt32)outlen > 0);
1141     if ((JSInt32)outlen <= 0) {
1142         return 0;
1143     }
1145     va_start(ap, fmt);
1146     rv = JS_vsnprintf(out, outlen, fmt, ap);
1147     va_end(ap);
1148     return rv;
1151 JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt,
1152                                   va_list ap)
1154     SprintfState ss;
1155     JSUint32 n;
1157     JS_ASSERT((JSInt32)outlen > 0);
1158     if ((JSInt32)outlen <= 0) {
1159         return 0;
1160     }
1162     ss.stuff = LimitStuff;
1163     ss.base = out;
1164     ss.cur = out;
1165     ss.maxlen = outlen;
1166     (void) dosprintf(&ss, fmt, ap);
1168     /* If we added chars, and we didn't append a null, do it now. */
1169     if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
1170         *(--ss.cur) = '\0';
1172     n = ss.cur - ss.base;
1173     return n ? n - 1 : n;
1176 JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...)
1178     va_list ap;
1179     char *rv;
1181     va_start(ap, fmt);
1182     rv = JS_vsprintf_append(last, fmt, ap);
1183     va_end(ap);
1184     return rv;
1187 JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap)
1189     SprintfState ss;
1190     int rv;
1192     ss.stuff = GrowStuff;
1193     if (last) {
1194         int lastlen = strlen(last);
1195         ss.base = last;
1196         ss.cur = last + lastlen;
1197         ss.maxlen = lastlen;
1198     } else {
1199         ss.base = 0;
1200         ss.cur = 0;
1201         ss.maxlen = 0;
1202     }
1203     rv = dosprintf(&ss, fmt, ap);
1204     if (rv < 0) {
1205         if (ss.base) {
1206             JS_DELETE(ss.base);
1207         }
1208         return 0;
1209     }
1210     return ss.base;