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)
121 {
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;
153 }
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)
160 {
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;
244 }
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)
251 {
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);
284 }
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)
291 {
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(", &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);
329 }
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)
338 {
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));
371 }
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)
380 {
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);
396 }
398 /*
399 ** BuildArgArray 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 )
406 {
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 improve 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 free( 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 free( nas );
645 *rv = -1;
646 return NULL;
647 }
649 cn++;
650 }
653 return nas;
654 }
656 /*
657 ** The workhorse sprintf code.
658 */
659 static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
660 {
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 free( 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 free( nas );
991 }
993 return rv;
994 }
996 /************************************************************************/
998 static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)
999 {
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;
1008 }
1010 JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg,
1011 const char *fmt, ...)
1012 {
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;
1020 }
1022 JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg,
1023 const char *fmt, va_list ap)
1024 {
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;
1034 }
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)
1041 {
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;
1071 }
1073 /*
1074 ** sprintf into a malloc'd buffer
1075 */
1076 JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...)
1077 {
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;
1085 }
1087 /*
1088 ** Free memory allocated, for the caller, by JS_smprintf
1089 */
1090 JS_PUBLIC_API(void) JS_smprintf_free(char *mem)
1091 {
1092 free(mem);
1093 }
1095 JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap)
1096 {
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 free(ss.base);
1108 }
1109 return 0;
1110 }
1111 return ss.base;
1112 }
1114 /*
1115 ** Stuff routine that discards overflow data
1116 */
1117 static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len)
1118 {
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;
1129 }
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, ...)
1136 {
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;
1149 }
1151 JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt,
1152 va_list ap)
1153 {
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[-1] = '\0';
1172 n = ss.cur - ss.base;
1173 return n ? n - 1 : n;
1174 }
1176 JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...)
1177 {
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;
1185 }
1187 JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap)
1188 {
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 free(ss.base);
1207 }
1208 return 0;
1209 }
1210 return ss.base;
1211 }