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