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 */
52 #include "jspubtd.h"
53 #include "jsstr.h"
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
67 /*
68 ** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it)
69 */
71 /*
72 ** XXX This needs to be internationalized!
73 */
75 typedef struct SprintfStateStr SprintfState;
77 struct SprintfStateStr {
78 int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len);
80 char *base;
81 char *cur;
82 JSUint32 maxlen;
84 int (*func)(void *arg, const char *sp, JSUint32 len);
85 void *arg;
86 };
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 };
96 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
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
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
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 {
125 char space = ' ';
126 int rv;
128 width -= srclen;
129 if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
130 if (flags & FLAG_ZEROS) {
131 space = '0';
132 }
133 while (--width >= 0) {
134 rv = (*ss->stuff)(ss, &space, 1);
135 if (rv < 0) {
136 return rv;
137 }
138 }
139 }
141 /* Copy out the source data */
142 rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
143 if (rv < 0) {
144 return rv;
145 }
147 if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
148 while (--width >= 0) {
149 rv = (*ss->stuff)(ss, &space, 1);
150 if (rv < 0) {
151 return rv;
152 }
153 }
154 }
155 return 0;
156 }
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 {
164 int zerowidth = 0;
165 int precwidth = 0;
166 int signwidth = 0;
167 int leftspaces = 0;
168 int rightspaces = 0;
169 int cvtwidth;
170 int rv;
171 char sign;
173 if ((type & 1) == 0) {
174 if (flags & FLAG_NEG) {
175 sign = '-';
176 signwidth = 1;
177 } else if (flags & FLAG_SIGNED) {
178 sign = '+';
179 signwidth = 1;
180 } else if (flags & FLAG_SPACED) {
181 sign = ' ';
182 signwidth = 1;
183 }
184 }
185 cvtwidth = signwidth + srclen;
187 if (prec > 0) {
188 if (prec > srclen) {
189 precwidth = prec - srclen; /* Need zero filling */
190 cvtwidth += precwidth;
191 }
192 }
194 if ((flags & FLAG_ZEROS) && (prec < 0)) {
195 if (width > cvtwidth) {
196 zerowidth = width - cvtwidth; /* Zero filling */
197 cvtwidth += zerowidth;
198 }
199 }
201 if (flags & FLAG_LEFT) {
202 if (width > cvtwidth) {
203 /* Space filling on the right (i.e. left adjusting) */
204 rightspaces = width - cvtwidth;
205 }
206 } else {
207 if (width > cvtwidth) {
208 /* Space filling on the left (i.e. right adjusting) */
209 leftspaces = width - cvtwidth;
210 }
211 }
212 while (--leftspaces >= 0) {
213 rv = (*ss->stuff)(ss, " ", 1);
214 if (rv < 0) {
215 return rv;
216 }
217 }
218 if (signwidth) {
219 rv = (*ss->stuff)(ss, &sign, 1);
220 if (rv < 0) {
221 return rv;
222 }
223 }
224 while (--precwidth >= 0) {
225 rv = (*ss->stuff)(ss, "0", 1);
226 if (rv < 0) {
227 return rv;
228 }
229 }
230 while (--zerowidth >= 0) {
231 rv = (*ss->stuff)(ss, "0", 1);
232 if (rv < 0) {
233 return rv;
234 }
235 }
236 rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
237 if (rv < 0) {
238 return rv;
239 }
240 while (--rightspaces >= 0) {
241 rv = (*ss->stuff)(ss, " ", 1);
242 if (rv < 0) {
243 return rv;
244 }
245 }
246 return 0;
247 }
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 {
255 char cvtbuf[100];
256 char *cvt;
257 int digits;
259 /* according to the man page this needs to happen */
260 if ((prec == 0) && (num == 0)) {
261 return 0;
262 }
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 cvt = cvtbuf + sizeof(cvtbuf);
270 digits = 0;
271 while (num) {
272 int digit = (((unsigned long)num) % radix) & 0xF;
273 *--cvt = hexp[digit];
274 digits++;
275 num = (long)(((unsigned long)num) / radix);
276 }
277 if (digits == 0) {
278 *--cvt = '0';
279 digits++;
280 }
282 /*
283 ** Now that we have the number converted without its sign, deal with
284 ** the sign and zero padding.
285 */
286 return fill_n(ss, cvt, digits, width, prec, type, flags);
287 }
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 {
295 char cvtbuf[100];
296 char *cvt;
297 int digits;
298 JSInt64 rad;
300 /* according to the man page this needs to happen */
301 if ((prec == 0) && (JSLL_IS_ZERO(num))) {
302 return 0;
303 }
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 JSLL_I2L(rad, radix);
311 cvt = cvtbuf + sizeof(cvtbuf);
312 digits = 0;
313 while (!JSLL_IS_ZERO(num)) {
314 JSInt32 digit;
315 JSInt64 quot, rem;
316 JSLL_UDIVMOD(", &rem, num, rad);
317 JSLL_L2I(digit, rem);
318 *--cvt = hexp[digit & 0xf];
319 digits++;
320 num = quot;
321 }
322 if (digits == 0) {
323 *--cvt = '0';
324 digits++;
325 }
327 /*
328 ** Now that we have the number converted without its sign, deal with
329 ** the sign and zero padding.
330 */
331 return fill_n(ss, cvt, digits, width, prec, type, flags);
332 }
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 {
342 char fin[20];
343 char fout[300];
344 int amount = fmt1 - fmt0;
346 JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
347 if (amount >= (int)sizeof(fin)) {
348 /* Totally bogus % command to sprintf. Just ignore it */
349 return 0;
350 }
351 memcpy(fin, fmt0, (size_t)amount);
352 fin[amount] = 0;
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 sprintf(fout, fin, d);
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 JS_ASSERT(strlen(fout) < sizeof(fout));
373 return (*ss->stuff)(ss, fout, strlen(fout));
374 }
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 {
384 int slen;
386 if (prec == 0)
387 return 0;
389 /* Limit string length by precision value */
390 slen = s ? strlen(s) : 6;
391 if (prec > 0) {
392 if (prec < slen) {
393 slen = prec;
394 }
395 }
397 /* and away we go */
398 return fill2(ss, s ? s : "(null)", slen, width, flags);
399 }
401 static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec,
402 int flags)
403 {
404 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 if (ws) {
410 int slen = js_strlen(ws);
411 char *s = js_DeflateString(NULL, ws, slen);
412 if (!s)
413 return -1; /* JSStuffFunc error indicator. */
414 result = cvt_s(ss, s, width, prec, flags);
415 free(s);
416 } else {
417 result = cvt_s(ss, NULL, width, prec, flags);
418 }
419 return result;
420 }
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 */
429 static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )
430 {
431 int number = 0, cn = 0, i;
432 const char *p;
433 char c;
434 struct NumArgState *nas;
437 /*
438 ** first pass:
439 ** detemine how many legal % I have got, then allocate space
440 */
442 p = fmt;
443 *rv = 0;
444 i = 0;
445 while( ( c = *p++ ) != 0 ){
446 if( c != '%' )
447 continue;
448 if( ( c = *p++ ) == '%' ) /* skip %% case */
449 continue;
451 while( c != 0 ){
452 if( c > '9' || c < '0' ){
453 if( c == '$' ){ /* numbered argument csae */
454 if( i > 0 ){
455 *rv = -1;
456 return NULL;
457 }
458 number++;
459 } else { /* non-numbered argument case */
460 if( number > 0 ){
461 *rv = -1;
462 return NULL;
463 }
464 i = 1;
465 }
466 break;
467 }
469 c = *p++;
470 }
471 }
473 if( number == 0 ){
474 return NULL;
475 }
478 if( number > NAS_DEFAULT_NUM ){
479 nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) );
480 if( !nas ){
481 *rv = -1;
482 return NULL;
483 }
484 } else {
485 nas = nasArray;
486 }
488 for( i = 0; i < number; i++ ){
489 nas[i].type = TYPE_UNKNOWN;
490 }
493 /*
494 ** second pass:
495 ** set nas[].type
496 */
498 p = fmt;
499 while( ( c = *p++ ) != 0 ){
500 if( c != '%' ) continue;
501 c = *p++;
502 if( c == '%' ) continue;
504 cn = 0;
505 while( c && c != '$' ){ /* should improve error check later */
506 cn = cn*10 + c - '0';
507 c = *p++;
508 }
510 if( !c || cn < 1 || cn > number ){
511 *rv = -1;
512 break;
513 }
515 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
516 cn--;
517 if( nas[cn].type != TYPE_UNKNOWN )
518 continue;
520 c = *p++;
522 /* width */
523 if (c == '*') {
524 /* not supported feature, for the argument is not numbered */
525 *rv = -1;
526 break;
527 }
529 while ((c >= '0') && (c <= '9')) {
530 c = *p++;
531 }
533 /* precision */
534 if (c == '.') {
535 c = *p++;
536 if (c == '*') {
537 /* not supported feature, for the argument is not numbered */
538 *rv = -1;
539 break;
540 }
542 while ((c >= '0') && (c <= '9')) {
543 c = *p++;
544 }
545 }
547 /* size */
548 nas[cn].type = TYPE_INTN;
549 if (c == 'h') {
550 nas[cn].type = TYPE_INT16;
551 c = *p++;
552 } else if (c == 'L') {
553 /* XXX not quite sure here */
554 nas[cn].type = TYPE_INT64;
555 c = *p++;
556 } else if (c == 'l') {
557 nas[cn].type = TYPE_INT32;
558 c = *p++;
559 if (c == 'l') {
560 nas[cn].type = TYPE_INT64;
561 c = *p++;
562 }
563 }
565 /* format */
566 switch (c) {
567 case 'd':
568 case 'c':
569 case 'i':
570 case 'o':
571 case 'u':
572 case 'x':
573 case 'X':
574 break;
576 case 'e':
577 case 'f':
578 case 'g':
579 nas[ cn ].type = TYPE_DOUBLE;
580 break;
582 case 'p':
583 /* XXX should use cpp */
584 if (sizeof(void *) == sizeof(JSInt32)) {
585 nas[ cn ].type = TYPE_UINT32;
586 } else if (sizeof(void *) == sizeof(JSInt64)) {
587 nas[ cn ].type = TYPE_UINT64;
588 } else if (sizeof(void *) == sizeof(JSIntn)) {
589 nas[ cn ].type = TYPE_UINTN;
590 } else {
591 nas[ cn ].type = TYPE_UNKNOWN;
592 }
593 break;
595 case 'C':
596 case 'S':
597 case 'E':
598 case 'G':
599 /* XXX not supported I suppose */
600 JS_ASSERT(0);
601 nas[ cn ].type = TYPE_UNKNOWN;
602 break;
604 case 's':
605 nas[ cn ].type = (nas[ cn ].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
606 break;
608 case 'n':
609 nas[ cn ].type = TYPE_INTSTR;
610 break;
612 default:
613 JS_ASSERT(0);
614 nas[ cn ].type = TYPE_UNKNOWN;
615 break;
616 }
618 /* get a legal para. */
619 if( nas[ cn ].type == TYPE_UNKNOWN ){
620 *rv = -1;
621 break;
622 }
623 }
626 /*
627 ** third pass
628 ** fill the nas[cn].ap
629 */
631 if( *rv < 0 ){
632 if( nas != nasArray )
633 free( nas );
634 return NULL;
635 }
637 cn = 0;
638 while( cn < number ){
639 if( nas[cn].type == TYPE_UNKNOWN ){
640 cn++;
641 continue;
642 }
644 VARARGS_ASSIGN(nas[cn].ap, ap);
646 switch( nas[cn].type ){
647 case TYPE_INT16:
648 case TYPE_UINT16:
649 case TYPE_INTN:
650 case TYPE_UINTN: (void)va_arg( ap, JSIntn ); break;
652 case TYPE_INT32: (void)va_arg( ap, JSInt32 ); break;
654 case TYPE_UINT32: (void)va_arg( ap, JSUint32 ); break;
656 case TYPE_INT64: (void)va_arg( ap, JSInt64 ); break;
658 case TYPE_UINT64: (void)va_arg( ap, JSUint64 ); break;
660 case TYPE_STRING: (void)va_arg( ap, char* ); break;
662 case TYPE_WSTRING: (void)va_arg( ap, jschar* ); break;
664 case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break;
666 case TYPE_DOUBLE: (void)va_arg( ap, double ); break;
668 default:
669 if( nas != nasArray )
670 free( nas );
671 *rv = -1;
672 return NULL;
673 }
675 cn++;
676 }
679 return nas;
680 }
682 /*
683 ** The workhorse sprintf code.
684 */
685 static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
686 {
687 char c;
688 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 } u;
700 const char *fmt0;
701 static char *hex = "0123456789abcdef";
702 static char *HEX = "0123456789ABCDEF";
703 char *hexp;
704 int rv, i;
705 struct NumArgState *nas = NULL;
706 struct NumArgState nasArray[ NAS_DEFAULT_NUM ];
707 char pattern[20];
708 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
714 /*
715 ** build an argument array, IF the fmt is numbered argument
716 ** list style, to contain the Numbered Argument list pointers
717 */
719 nas = BuildArgArray( fmt, ap, &rv, nasArray );
720 if( rv < 0 ){
721 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
722 JS_ASSERT(0);
723 return rv;
724 }
726 while ((c = *fmt++) != 0) {
727 if (c != '%') {
728 rv = (*ss->stuff)(ss, fmt - 1, 1);
729 if (rv < 0) {
730 return rv;
731 }
732 continue;
733 }
734 fmt0 = fmt - 1;
736 /*
737 ** Gobble up the % format string. Hopefully we have handled all
738 ** of the strange cases!
739 */
740 flags = 0;
741 c = *fmt++;
742 if (c == '%') {
743 /* quoting a % with %% */
744 rv = (*ss->stuff)(ss, fmt - 1, 1);
745 if (rv < 0) {
746 return rv;
747 }
748 continue;
749 }
751 if( nas != NULL ){
752 /* the fmt contains the Numbered Arguments feature */
753 i = 0;
754 while( c && c != '$' ){ /* should imporve error check later */
755 i = ( i * 10 ) + ( c - '0' );
756 c = *fmt++;
757 }
759 if( nas[i-1].type == TYPE_UNKNOWN ){
760 if( nas && ( nas != nasArray ) )
761 free( nas );
762 return -1;
763 }
765 ap = nas[i-1].ap;
766 dolPt = fmt;
767 c = *fmt++;
768 }
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 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
778 if (c == '-') flags |= FLAG_LEFT;
779 if (c == '+') flags |= FLAG_SIGNED;
780 if (c == ' ') flags |= FLAG_SPACED;
781 if (c == '0') flags |= FLAG_ZEROS;
782 c = *fmt++;
783 }
784 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
785 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
787 /* width */
788 if (c == '*') {
789 c = *fmt++;
790 width = va_arg(ap, int);
791 } else {
792 width = 0;
793 while ((c >= '0') && (c <= '9')) {
794 width = (width * 10) + (c - '0');
795 c = *fmt++;
796 }
797 }
799 /* precision */
800 prec = -1;
801 if (c == '.') {
802 c = *fmt++;
803 if (c == '*') {
804 c = *fmt++;
805 prec = va_arg(ap, int);
806 } else {
807 prec = 0;
808 while ((c >= '0') && (c <= '9')) {
809 prec = (prec * 10) + (c - '0');
810 c = *fmt++;
811 }
812 }
813 }
815 /* size */
816 type = TYPE_INTN;
817 if (c == 'h') {
818 type = TYPE_INT16;
819 c = *fmt++;
820 } else if (c == 'L') {
821 /* XXX not quite sure here */
822 type = TYPE_INT64;
823 c = *fmt++;
824 } else if (c == 'l') {
825 type = TYPE_INT32;
826 c = *fmt++;
827 if (c == 'l') {
828 type = TYPE_INT64;
829 c = *fmt++;
830 }
831 }
833 /* format */
834 hexp = hex;
835 switch (c) {
836 case 'd': case 'i': /* decimal/integer */
837 radix = 10;
838 goto fetch_and_convert;
840 case 'o': /* octal */
841 radix = 8;
842 type |= 1;
843 goto fetch_and_convert;
845 case 'u': /* unsigned decimal */
846 radix = 10;
847 type |= 1;
848 goto fetch_and_convert;
850 case 'x': /* unsigned hex */
851 radix = 16;
852 type |= 1;
853 goto fetch_and_convert;
855 case 'X': /* unsigned HEX */
856 radix = 16;
857 hexp = HEX;
858 type |= 1;
859 goto fetch_and_convert;
861 fetch_and_convert:
862 switch (type) {
863 case TYPE_INT16:
864 u.l = va_arg(ap, int);
865 if (u.l < 0) {
866 u.l = -u.l;
867 flags |= FLAG_NEG;
868 }
869 goto do_long;
870 case TYPE_UINT16:
871 u.l = va_arg(ap, int) & 0xffff;
872 goto do_long;
873 case TYPE_INTN:
874 u.l = va_arg(ap, int);
875 if (u.l < 0) {
876 u.l = -u.l;
877 flags |= FLAG_NEG;
878 }
879 goto do_long;
880 case TYPE_UINTN:
881 u.l = (long)va_arg(ap, unsigned int);
882 goto do_long;
884 case TYPE_INT32:
885 u.l = va_arg(ap, JSInt32);
886 if (u.l < 0) {
887 u.l = -u.l;
888 flags |= FLAG_NEG;
889 }
890 goto do_long;
891 case TYPE_UINT32:
892 u.l = (long)va_arg(ap, JSUint32);
893 do_long:
894 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
895 if (rv < 0) {
896 return rv;
897 }
898 break;
900 case TYPE_INT64:
901 u.ll = va_arg(ap, JSInt64);
902 if (!JSLL_GE_ZERO(u.ll)) {
903 JSLL_NEG(u.ll, u.ll);
904 flags |= FLAG_NEG;
905 }
906 goto do_longlong;
907 case TYPE_UINT64:
908 u.ll = va_arg(ap, JSUint64);
909 do_longlong:
910 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
911 if (rv < 0) {
912 return rv;
913 }
914 break;
915 }
916 break;
918 case 'e':
919 case 'E':
920 case 'f':
921 case 'g':
922 u.d = va_arg(ap, double);
923 if( nas != NULL ){
924 i = fmt - dolPt;
925 if( i < (int)sizeof( pattern ) ){
926 pattern[0] = '%';
927 memcpy( &pattern[1], dolPt, (size_t)i );
928 rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
929 }
930 } else
931 rv = cvt_f(ss, u.d, fmt0, fmt);
933 if (rv < 0) {
934 return rv;
935 }
936 break;
938 case 'c':
939 if ((flags & FLAG_LEFT) == 0) {
940 while (width-- > 1) {
941 rv = (*ss->stuff)(ss, " ", 1);
942 if (rv < 0) {
943 return rv;
944 }
945 }
946 }
947 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 u.ch = va_arg(ap, int);
958 rv = (*ss->stuff)(ss, &u.ch, 1);
959 break;
960 }
961 if (rv < 0) {
962 return rv;
963 }
964 if (flags & FLAG_LEFT) {
965 while (width-- > 1) {
966 rv = (*ss->stuff)(ss, " ", 1);
967 if (rv < 0) {
968 return rv;
969 }
970 }
971 }
972 break;
974 case 'p':
975 if (sizeof(void *) == sizeof(JSInt32)) {
976 type = TYPE_UINT32;
977 } else if (sizeof(void *) == sizeof(JSInt64)) {
978 type = TYPE_UINT64;
979 } else if (sizeof(void *) == sizeof(int)) {
980 type = TYPE_UINTN;
981 } else {
982 JS_ASSERT(0);
983 break;
984 }
985 radix = 16;
986 goto fetch_and_convert;
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
998 case 's':
999 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 u.ws = va_arg(ap, const jschar*);
1005 rv = cvt_ws(ss, u.ws, width, prec, flags);
1006 } else {
1007 u.s = va_arg(ap, const char*);
1008 rv = cvt_s(ss, u.s, width, prec, flags);
1009 }
1010 if (rv < 0) {
1011 return rv;
1012 }
1013 break;
1015 case 'n':
1016 u.ip = va_arg(ap, int*);
1017 if (u.ip) {
1018 *u.ip = ss->cur - ss->base;
1019 }
1020 break;
1022 default:
1023 /* Not a % token after all... skip it */
1024 #if 0
1025 JS_ASSERT(0);
1026 #endif
1027 rv = (*ss->stuff)(ss, "%", 1);
1028 if (rv < 0) {
1029 return rv;
1030 }
1031 rv = (*ss->stuff)(ss, fmt - 1, 1);
1032 if (rv < 0) {
1033 return rv;
1034 }
1035 }
1036 }
1038 /* Stuff trailing NUL */
1039 rv = (*ss->stuff)(ss, "\0", 1);
1041 if( nas && ( nas != nasArray ) ){
1042 free( nas );
1043 }
1045 return rv;
1046 }
1048 /************************************************************************/
1050 static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)
1051 {
1052 int rv;
1054 rv = (*ss->func)(ss->arg, sp, len);
1055 if (rv < 0) {
1056 return rv;
1057 }
1058 ss->maxlen += len;
1059 return 0;
1060 }
1062 JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg,
1063 const char *fmt, ...)
1064 {
1065 va_list ap;
1066 int rv;
1068 va_start(ap, fmt);
1069 rv = JS_vsxprintf(func, arg, fmt, ap);
1070 va_end(ap);
1071 return rv;
1072 }
1074 JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg,
1075 const char *fmt, va_list ap)
1076 {
1077 SprintfState ss;
1078 int rv;
1080 ss.stuff = FuncStuff;
1081 ss.func = func;
1082 ss.arg = arg;
1083 ss.maxlen = 0;
1084 rv = dosprintf(&ss, fmt, ap);
1085 return (rv < 0) ? (JSUint32)-1 : ss.maxlen;
1086 }
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 {
1094 ptrdiff_t off;
1095 char *newbase;
1096 JSUint32 newlen;
1098 off = ss->cur - ss->base;
1099 if (off + len >= ss->maxlen) {
1100 /* Grow the buffer */
1101 newlen = ss->maxlen + ((len > 32) ? len : 32);
1102 if (ss->base) {
1103 newbase = (char*) realloc(ss->base, newlen);
1104 } else {
1105 newbase = (char*) malloc(newlen);
1106 }
1107 if (!newbase) {
1108 /* Ran out of memory */
1109 return -1;
1110 }
1111 ss->base = newbase;
1112 ss->maxlen = newlen;
1113 ss->cur = ss->base + off;
1114 }
1116 /* Copy data */
1117 while (len) {
1118 --len;
1119 *ss->cur++ = *sp++;
1120 }
1121 JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen);
1122 return 0;
1123 }
1125 /*
1126 ** sprintf into a malloc'd buffer
1127 */
1128 JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...)
1129 {
1130 va_list ap;
1131 char *rv;
1133 va_start(ap, fmt);
1134 rv = JS_vsmprintf(fmt, ap);
1135 va_end(ap);
1136 return rv;
1137 }
1139 /*
1140 ** Free memory allocated, for the caller, by JS_smprintf
1141 */
1142 JS_PUBLIC_API(void) JS_smprintf_free(char *mem)
1143 {
1144 free(mem);
1145 }
1147 JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap)
1148 {
1149 SprintfState ss;
1150 int rv;
1152 ss.stuff = GrowStuff;
1153 ss.base = 0;
1154 ss.cur = 0;
1155 ss.maxlen = 0;
1156 rv = dosprintf(&ss, fmt, ap);
1157 if (rv < 0) {
1158 if (ss.base) {
1159 free(ss.base);
1160 }
1161 return 0;
1162 }
1163 return ss.base;
1164 }
1166 /*
1167 ** Stuff routine that discards overflow data
1168 */
1169 static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len)
1170 {
1171 JSUint32 limit = ss->maxlen - (ss->cur - ss->base);
1173 if (len > limit) {
1174 len = limit;
1175 }
1176 while (len) {
1177 --len;
1178 *ss->cur++ = *sp++;
1179 }
1180 return 0;
1181 }
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 {
1189 va_list ap;
1190 int rv;
1192 JS_ASSERT((JSInt32)outlen > 0);
1193 if ((JSInt32)outlen <= 0) {
1194 return 0;
1195 }
1197 va_start(ap, fmt);
1198 rv = JS_vsnprintf(out, outlen, fmt, ap);
1199 va_end(ap);
1200 return rv;
1201 }
1203 JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt,
1204 va_list ap)
1205 {
1206 SprintfState ss;
1207 JSUint32 n;
1209 JS_ASSERT((JSInt32)outlen > 0);
1210 if ((JSInt32)outlen <= 0) {
1211 return 0;
1212 }
1214 ss.stuff = LimitStuff;
1215 ss.base = out;
1216 ss.cur = out;
1217 ss.maxlen = outlen;
1218 (void) dosprintf(&ss, fmt, ap);
1220 /* If we added chars, and we didn't append a null, do it now. */
1221 if( (ss.cur != ss.base) && (ss.cur[-1] != '\0') )
1222 ss.cur[-1] = '\0';
1224 n = ss.cur - ss.base;
1225 return n ? n - 1 : n;
1226 }
1228 JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...)
1229 {
1230 va_list ap;
1231 char *rv;
1233 va_start(ap, fmt);
1234 rv = JS_vsprintf_append(last, fmt, ap);
1235 va_end(ap);
1236 return rv;
1237 }
1239 JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap)
1240 {
1241 SprintfState ss;
1242 int rv;
1244 ss.stuff = GrowStuff;
1245 if (last) {
1246 int lastlen = strlen(last);
1247 ss.base = last;
1248 ss.cur = last + lastlen;
1249 ss.maxlen = lastlen;
1250 } else {
1251 ss.base = 0;
1252 ss.cur = 0;
1253 ss.maxlen = 0;
1254 }
1255 rv = dosprintf(&ss, fmt, ap);
1256 if (rv < 0) {
1257 if (ss.base) {
1258 free(ss.base);
1259 }
1260 return 0;
1261 }
1262 return ss.base;
1263 }