Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Portable safe sprintf code.
9 *
10 * Author: Kipp E.B. Hickman
11 */
13 #include "jsprf.h"
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "jspubtd.h"
21 #include "jsstr.h"
22 #include "jsutil.h"
24 using namespace js;
26 /*
27 * Note: on some platforms va_list is defined as an array,
28 * and requires array notation.
29 */
30 #ifdef HAVE_VA_COPY
31 #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo, bar)
32 #elif defined(HAVE_VA_LIST_AS_ARRAY)
33 #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
34 #else
35 #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
36 #endif
38 struct SprintfState
39 {
40 int (*stuff)(SprintfState *ss, const char *sp, size_t len);
42 char *base;
43 char *cur;
44 size_t maxlen;
46 int (*func)(void *arg, const char *sp, uint32_t len);
47 void *arg;
48 };
50 /*
51 * Numbered Argument State
52 */
53 struct NumArgState
54 {
55 int type; // type of the current ap
56 va_list ap; // point to the corresponding position on ap
57 };
59 const size_t NAS_DEFAULT_NUM = 20; // default number of NumberedArgumentState array
62 #define TYPE_INT16 0
63 #define TYPE_UINT16 1
64 #define TYPE_INTN 2
65 #define TYPE_UINTN 3
66 #define TYPE_INT32 4
67 #define TYPE_UINT32 5
68 #define TYPE_INT64 6
69 #define TYPE_UINT64 7
70 #define TYPE_STRING 8
71 #define TYPE_DOUBLE 9
72 #define TYPE_INTSTR 10
73 #define TYPE_WSTRING 11
74 #define TYPE_UNKNOWN 20
76 #define FLAG_LEFT 0x1
77 #define FLAG_SIGNED 0x2
78 #define FLAG_SPACED 0x4
79 #define FLAG_ZEROS 0x8
80 #define FLAG_NEG 0x10
82 inline int
83 generic_write(SprintfState *ss, const char *src, size_t srclen)
84 {
85 return (*ss->stuff)(ss, src, srclen);
86 }
88 inline int
89 generic_write(SprintfState *ss, const jschar *src, size_t srclen)
90 {
91 const size_t CHUNK_SIZE = 64;
92 char chunk[CHUNK_SIZE];
94 int rv = 0;
95 size_t j = 0;
96 size_t i = 0;
97 while (i < srclen) {
98 // FIXME: truncates characters to 8 bits
99 chunk[j++] = char(src[i++]);
101 if (j == CHUNK_SIZE || i == srclen) {
102 rv = (*ss->stuff)(ss, chunk, j);
103 if (rv != 0)
104 return rv;
105 j = 0;
106 }
107 }
108 return 0;
109 }
111 // Fill into the buffer using the data in src
112 template <typename Char>
113 static int
114 fill2(SprintfState *ss, const Char *src, int srclen, int width, int flags)
115 {
116 char space = ' ';
117 int rv;
119 width -= srclen;
120 if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting
121 if (flags & FLAG_ZEROS)
122 space = '0';
123 while (--width >= 0) {
124 rv = (*ss->stuff)(ss, &space, 1);
125 if (rv < 0)
126 return rv;
127 }
128 }
130 // Copy out the source data
131 rv = generic_write(ss, src, srclen);
132 if (rv < 0)
133 return rv;
135 if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting
136 while (--width >= 0) {
137 rv = (*ss->stuff)(ss, &space, 1);
138 if (rv < 0)
139 return rv;
140 }
141 }
142 return 0;
143 }
145 /*
146 * Fill a number. The order is: optional-sign zero-filling conversion-digits
147 */
148 static int
149 fill_n(SprintfState *ss, const char *src, int srclen, int width, int prec, int type, int flags)
150 {
151 int zerowidth = 0;
152 int precwidth = 0;
153 int signwidth = 0;
154 int leftspaces = 0;
155 int rightspaces = 0;
156 int cvtwidth;
157 int rv;
158 char sign;
160 if ((type & 1) == 0) {
161 if (flags & FLAG_NEG) {
162 sign = '-';
163 signwidth = 1;
164 } else if (flags & FLAG_SIGNED) {
165 sign = '+';
166 signwidth = 1;
167 } else if (flags & FLAG_SPACED) {
168 sign = ' ';
169 signwidth = 1;
170 }
171 }
172 cvtwidth = signwidth + srclen;
174 if (prec > 0) {
175 if (prec > srclen) {
176 precwidth = prec - srclen; // Need zero filling
177 cvtwidth += precwidth;
178 }
179 }
181 if ((flags & FLAG_ZEROS) && (prec < 0)) {
182 if (width > cvtwidth) {
183 zerowidth = width - cvtwidth; // Zero filling
184 cvtwidth += zerowidth;
185 }
186 }
188 if (flags & FLAG_LEFT) {
189 if (width > cvtwidth) {
190 // Space filling on the right (i.e. left adjusting)
191 rightspaces = width - cvtwidth;
192 }
193 } else {
194 if (width > cvtwidth) {
195 // Space filling on the left (i.e. right adjusting)
196 leftspaces = width - cvtwidth;
197 }
198 }
199 while (--leftspaces >= 0) {
200 rv = (*ss->stuff)(ss, " ", 1);
201 if (rv < 0) {
202 return rv;
203 }
204 }
205 if (signwidth) {
206 rv = (*ss->stuff)(ss, &sign, 1);
207 if (rv < 0) {
208 return rv;
209 }
210 }
211 while (--precwidth >= 0) {
212 rv = (*ss->stuff)(ss, "0", 1);
213 if (rv < 0) {
214 return rv;
215 }
216 }
217 while (--zerowidth >= 0) {
218 rv = (*ss->stuff)(ss, "0", 1);
219 if (rv < 0) {
220 return rv;
221 }
222 }
223 rv = (*ss->stuff)(ss, src, uint32_t(srclen));
224 if (rv < 0) {
225 return rv;
226 }
227 while (--rightspaces >= 0) {
228 rv = (*ss->stuff)(ss, " ", 1);
229 if (rv < 0) {
230 return rv;
231 }
232 }
233 return 0;
234 }
236 /* Convert a long into its printable form. */
237 static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
238 int type, int flags, const char *hexp)
239 {
240 char cvtbuf[100];
241 char *cvt;
242 int digits;
244 // according to the man page this needs to happen
245 if ((prec == 0) && (num == 0)) {
246 return 0;
247 }
249 // Converting decimal is a little tricky. In the unsigned case we
250 // need to stop when we hit 10 digits. In the signed case, we can
251 // stop when the number is zero.
252 cvt = cvtbuf + sizeof(cvtbuf);
253 digits = 0;
254 while (num) {
255 int digit = (((unsigned long)num) % radix) & 0xF;
256 *--cvt = hexp[digit];
257 digits++;
258 num = (long)(((unsigned long)num) / radix);
259 }
260 if (digits == 0) {
261 *--cvt = '0';
262 digits++;
263 }
265 // Now that we have the number converted without its sign, deal with
266 // the sign and zero padding.
267 return fill_n(ss, cvt, digits, width, prec, type, flags);
268 }
270 /* Convert a 64-bit integer into its printable form. */
271 static int cvt_ll(SprintfState *ss, int64_t num, int width, int prec, int radix,
272 int type, int flags, const char *hexp)
273 {
274 // According to the man page, this needs to happen.
275 if (prec == 0 && num == 0)
276 return 0;
278 // Converting decimal is a little tricky. In the unsigned case we
279 // need to stop when we hit 10 digits. In the signed case, we can
280 // stop when the number is zero.
281 int64_t rad = int64_t(radix);
282 char cvtbuf[100];
283 char *cvt = cvtbuf + sizeof(cvtbuf);
284 int digits = 0;
285 while (num != 0) {
286 int64_t quot = uint64_t(num) / rad;
287 int64_t rem = uint64_t(num) % rad;
288 int32_t digit = int32_t(rem);
289 *--cvt = hexp[digit & 0xf];
290 digits++;
291 num = quot;
292 }
293 if (digits == 0) {
294 *--cvt = '0';
295 digits++;
296 }
298 // Now that we have the number converted without its sign, deal with
299 // the sign and zero padding.
300 return fill_n(ss, cvt, digits, width, prec, type, flags);
301 }
303 /*
304 * Convert a double precision floating point number into its printable
305 * form.
306 *
307 * XXX stop using sprintf to convert floating point
308 */
309 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
310 {
311 char fin[20];
312 char fout[300];
313 int amount = fmt1 - fmt0;
315 JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
316 if (amount >= (int)sizeof(fin)) {
317 // Totally bogus % command to sprintf. Just ignore it
318 return 0;
319 }
320 js_memcpy(fin, fmt0, (size_t)amount);
321 fin[amount] = 0;
323 // Convert floating point using the native sprintf code
324 #ifdef DEBUG
325 {
326 const char *p = fin;
327 while (*p) {
328 JS_ASSERT(*p != 'L');
329 p++;
330 }
331 }
332 #endif
333 sprintf(fout, fin, d);
335 // This assert will catch overflow's of fout, when building with
336 // debugging on. At least this way we can track down the evil piece
337 // of calling code and fix it!
338 JS_ASSERT(strlen(fout) < sizeof(fout));
340 return (*ss->stuff)(ss, fout, strlen(fout));
341 }
343 static inline const char *generic_null_str(const char *) { return "(null)"; }
344 static inline const jschar *generic_null_str(const jschar *) { return MOZ_UTF16("(null)"); }
346 static inline size_t generic_strlen(const char *s) { return strlen(s); }
347 static inline size_t generic_strlen(const jschar *s) { return js_strlen(s); }
349 /*
350 * Convert a string into its printable form. "width" is the output
351 * width. "prec" is the maximum number of characters of "s" to output,
352 * where -1 means until NUL.
353 */
354 template <typename Char>
355 static int
356 cvt_s(SprintfState *ss, const Char *s, int width, int prec, int flags)
357 {
358 if (prec == 0)
359 return 0;
360 if (!s)
361 s = generic_null_str(s);
363 // Limit string length by precision value
364 int slen = int(generic_strlen(s));
365 if (0 < prec && prec < slen)
366 slen = prec;
368 // and away we go
369 return fill2(ss, s, slen, width, flags);
370 }
372 /*
373 * BuildArgArray stands for Numbered Argument list Sprintf
374 * for example,
375 * fmp = "%4$i, %2$d, %3s, %1d";
376 * the number must start from 1, and no gap among them
377 */
378 static NumArgState *
379 BuildArgArray(const char *fmt, va_list ap, int *rv, NumArgState *nasArray)
380 {
381 size_t number = 0, cn = 0, i;
382 const char *p;
383 char c;
384 NumArgState *nas;
387 // First pass:
388 // Detemine how many legal % I have got, then allocate space.
390 p = fmt;
391 *rv = 0;
392 i = 0;
393 while ((c = *p++) != 0) {
394 if (c != '%')
395 continue;
396 if ((c = *p++) == '%') // skip %% case
397 continue;
399 while (c != 0) {
400 if (c > '9' || c < '0') {
401 if (c == '$') { // numbered argument case
402 if (i > 0) {
403 *rv = -1;
404 return nullptr;
405 }
406 number++;
407 } else { // non-numbered argument case
408 if (number > 0) {
409 *rv = -1;
410 return nullptr;
411 }
412 i = 1;
413 }
414 break;
415 }
417 c = *p++;
418 }
419 }
421 if (number == 0)
422 return nullptr;
424 if (number > NAS_DEFAULT_NUM) {
425 nas = (NumArgState *) js_malloc(number * sizeof(NumArgState));
426 if (!nas) {
427 *rv = -1;
428 return nullptr;
429 }
430 } else {
431 nas = nasArray;
432 }
434 for (i = 0; i < number; i++)
435 nas[i].type = TYPE_UNKNOWN;
438 // Second pass:
439 // Set nas[].type.
441 p = fmt;
442 while ((c = *p++) != 0) {
443 if (c != '%')
444 continue;
445 c = *p++;
446 if (c == '%')
447 continue;
449 cn = 0;
450 while (c && c != '$') { // should improve error check later
451 cn = cn*10 + c - '0';
452 c = *p++;
453 }
455 if (!c || cn < 1 || cn > number) {
456 *rv = -1;
457 break;
458 }
460 // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
461 cn--;
462 if (nas[cn].type != TYPE_UNKNOWN)
463 continue;
465 c = *p++;
467 // width
468 if (c == '*') {
469 // not supported feature, for the argument is not numbered
470 *rv = -1;
471 break;
472 }
474 while ((c >= '0') && (c <= '9')) {
475 c = *p++;
476 }
478 // precision
479 if (c == '.') {
480 c = *p++;
481 if (c == '*') {
482 // not supported feature, for the argument is not numbered
483 *rv = -1;
484 break;
485 }
487 while ((c >= '0') && (c <= '9')) {
488 c = *p++;
489 }
490 }
492 // size
493 nas[cn].type = TYPE_INTN;
494 if (c == 'h') {
495 nas[cn].type = TYPE_INT16;
496 c = *p++;
497 } else if (c == 'L') {
498 // XXX not quite sure here
499 nas[cn].type = TYPE_INT64;
500 c = *p++;
501 } else if (c == 'l') {
502 nas[cn].type = TYPE_INT32;
503 c = *p++;
504 if (c == 'l') {
505 nas[cn].type = TYPE_INT64;
506 c = *p++;
507 }
508 }
510 // format
511 switch (c) {
512 case 'd':
513 case 'c':
514 case 'i':
515 case 'o':
516 case 'u':
517 case 'x':
518 case 'X':
519 break;
521 case 'e':
522 case 'f':
523 case 'g':
524 nas[cn].type = TYPE_DOUBLE;
525 break;
527 case 'p':
528 // XXX should use cpp
529 if (sizeof(void *) == sizeof(int32_t)) {
530 nas[cn].type = TYPE_UINT32;
531 } else if (sizeof(void *) == sizeof(int64_t)) {
532 nas[cn].type = TYPE_UINT64;
533 } else if (sizeof(void *) == sizeof(int)) {
534 nas[cn].type = TYPE_UINTN;
535 } else {
536 nas[cn].type = TYPE_UNKNOWN;
537 }
538 break;
540 case 'C':
541 case 'S':
542 case 'E':
543 case 'G':
544 // XXX not supported I suppose
545 JS_ASSERT(0);
546 nas[cn].type = TYPE_UNKNOWN;
547 break;
549 case 's':
550 nas[cn].type = (nas[cn].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
551 break;
553 case 'n':
554 nas[cn].type = TYPE_INTSTR;
555 break;
557 default:
558 JS_ASSERT(0);
559 nas[cn].type = TYPE_UNKNOWN;
560 break;
561 }
563 // get a legal para.
564 if (nas[cn].type == TYPE_UNKNOWN) {
565 *rv = -1;
566 break;
567 }
568 }
571 // Third pass:
572 // Fill nas[].ap.
574 if (*rv < 0) {
575 if (nas != nasArray)
576 js_free(nas);
577 return nullptr;
578 }
580 cn = 0;
581 while (cn < number) {
582 if (nas[cn].type == TYPE_UNKNOWN) {
583 cn++;
584 continue;
585 }
587 VARARGS_ASSIGN(nas[cn].ap, ap);
589 switch (nas[cn].type) {
590 case TYPE_INT16:
591 case TYPE_UINT16:
592 case TYPE_INTN:
593 case TYPE_UINTN: (void) va_arg(ap, int); break;
594 case TYPE_INT32: (void) va_arg(ap, int32_t); break;
595 case TYPE_UINT32: (void) va_arg(ap, uint32_t); break;
596 case TYPE_INT64: (void) va_arg(ap, int64_t); break;
597 case TYPE_UINT64: (void) va_arg(ap, uint64_t); break;
598 case TYPE_STRING: (void) va_arg(ap, char*); break;
599 case TYPE_WSTRING: (void) va_arg(ap, jschar*); break;
600 case TYPE_INTSTR: (void) va_arg(ap, int*); break;
601 case TYPE_DOUBLE: (void) va_arg(ap, double); break;
603 default:
604 if (nas != nasArray)
605 js_free(nas);
606 *rv = -1;
607 return nullptr;
608 }
610 cn++;
611 }
614 return nas;
615 }
617 /*
618 * The workhorse sprintf code.
619 */
620 static int
621 dosprintf(SprintfState *ss, const char *fmt, va_list ap)
622 {
623 char c;
624 int flags, width, prec, radix, type;
625 union {
626 char ch;
627 jschar wch;
628 int i;
629 long l;
630 int64_t ll;
631 double d;
632 const char *s;
633 const jschar* ws;
634 int *ip;
635 } u;
636 const char *fmt0;
637 static const char hex[] = "0123456789abcdef";
638 static const char HEX[] = "0123456789ABCDEF";
639 const char *hexp;
640 int rv, i;
641 NumArgState *nas = nullptr;
642 NumArgState nasArray[NAS_DEFAULT_NUM];
643 char pattern[20];
644 const char *dolPt = nullptr; // in "%4$.2f", dolPt will point to '.'
646 // Build an argument array, IF the fmt is numbered argument
647 // list style, to contain the Numbered Argument list pointers.
649 nas = BuildArgArray(fmt, ap, &rv, nasArray);
650 if (rv < 0) {
651 // the fmt contains error Numbered Argument format, jliu@netscape.com
652 JS_ASSERT(0);
653 return rv;
654 }
656 while ((c = *fmt++) != 0) {
657 if (c != '%') {
658 rv = (*ss->stuff)(ss, fmt - 1, 1);
659 if (rv < 0) {
660 return rv;
661 }
662 continue;
663 }
664 fmt0 = fmt - 1;
666 // Gobble up the % format string. Hopefully we have handled all
667 // of the strange cases!
668 flags = 0;
669 c = *fmt++;
670 if (c == '%') {
671 // quoting a % with %%
672 rv = (*ss->stuff)(ss, fmt - 1, 1);
673 if (rv < 0) {
674 return rv;
675 }
676 continue;
677 }
679 if (nas != nullptr) {
680 // the fmt contains the Numbered Arguments feature
681 i = 0;
682 while (c && c != '$') { // should improve error check later
683 i = (i * 10) + (c - '0');
684 c = *fmt++;
685 }
687 if (nas[i-1].type == TYPE_UNKNOWN) {
688 if (nas && nas != nasArray)
689 js_free(nas);
690 return -1;
691 }
693 ap = nas[i-1].ap;
694 dolPt = fmt;
695 c = *fmt++;
696 }
698 // Examine optional flags. Note that we do not implement the
699 // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
700 // somewhat ambiguous and not ideal, which is perhaps why
701 // the various sprintf() implementations are inconsistent
702 // on this feature.
703 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
704 if (c == '-') flags |= FLAG_LEFT;
705 if (c == '+') flags |= FLAG_SIGNED;
706 if (c == ' ') flags |= FLAG_SPACED;
707 if (c == '0') flags |= FLAG_ZEROS;
708 c = *fmt++;
709 }
710 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
711 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
713 // width
714 if (c == '*') {
715 c = *fmt++;
716 width = va_arg(ap, int);
717 } else {
718 width = 0;
719 while ((c >= '0') && (c <= '9')) {
720 width = (width * 10) + (c - '0');
721 c = *fmt++;
722 }
723 }
725 // precision
726 prec = -1;
727 if (c == '.') {
728 c = *fmt++;
729 if (c == '*') {
730 c = *fmt++;
731 prec = va_arg(ap, int);
732 } else {
733 prec = 0;
734 while ((c >= '0') && (c <= '9')) {
735 prec = (prec * 10) + (c - '0');
736 c = *fmt++;
737 }
738 }
739 }
741 // size
742 type = TYPE_INTN;
743 if (c == 'h') {
744 type = TYPE_INT16;
745 c = *fmt++;
746 } else if (c == 'L') {
747 // XXX not quite sure here
748 type = TYPE_INT64;
749 c = *fmt++;
750 } else if (c == 'l') {
751 type = TYPE_INT32;
752 c = *fmt++;
753 if (c == 'l') {
754 type = TYPE_INT64;
755 c = *fmt++;
756 }
757 }
759 // format
760 hexp = hex;
761 switch (c) {
762 case 'd': case 'i': // decimal/integer
763 radix = 10;
764 goto fetch_and_convert;
766 case 'o': // octal
767 radix = 8;
768 type |= 1;
769 goto fetch_and_convert;
771 case 'u': // unsigned decimal
772 radix = 10;
773 type |= 1;
774 goto fetch_and_convert;
776 case 'x': // unsigned hex
777 radix = 16;
778 type |= 1;
779 goto fetch_and_convert;
781 case 'X': // unsigned HEX
782 radix = 16;
783 hexp = HEX;
784 type |= 1;
785 goto fetch_and_convert;
787 fetch_and_convert:
788 switch (type) {
789 case TYPE_INT16:
790 u.l = va_arg(ap, int);
791 if (u.l < 0) {
792 u.l = -u.l;
793 flags |= FLAG_NEG;
794 }
795 goto do_long;
796 case TYPE_UINT16:
797 u.l = va_arg(ap, int) & 0xffff;
798 goto do_long;
799 case TYPE_INTN:
800 u.l = va_arg(ap, int);
801 if (u.l < 0) {
802 u.l = -u.l;
803 flags |= FLAG_NEG;
804 }
805 goto do_long;
806 case TYPE_UINTN:
807 u.l = (long)va_arg(ap, unsigned int);
808 goto do_long;
810 case TYPE_INT32:
811 u.l = va_arg(ap, int32_t);
812 if (u.l < 0) {
813 u.l = -u.l;
814 flags |= FLAG_NEG;
815 }
816 goto do_long;
817 case TYPE_UINT32:
818 u.l = (long)va_arg(ap, uint32_t);
819 do_long:
820 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
821 if (rv < 0) {
822 return rv;
823 }
824 break;
826 case TYPE_INT64:
827 u.ll = va_arg(ap, int64_t);
828 if (u.ll < 0) {
829 u.ll = -u.ll;
830 flags |= FLAG_NEG;
831 }
832 goto do_longlong;
833 case TYPE_UINT64:
834 u.ll = va_arg(ap, uint64_t);
835 do_longlong:
836 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
837 if (rv < 0) {
838 return rv;
839 }
840 break;
841 }
842 break;
844 case 'e':
845 case 'E':
846 case 'f':
847 case 'g':
848 u.d = va_arg(ap, double);
849 if (nas != nullptr) {
850 i = fmt - dolPt;
851 if (i < int(sizeof(pattern))) {
852 pattern[0] = '%';
853 js_memcpy(&pattern[1], dolPt, size_t(i));
854 rv = cvt_f(ss, u.d, pattern, &pattern[i + 1]);
855 }
856 } else
857 rv = cvt_f(ss, u.d, fmt0, fmt);
859 if (rv < 0) {
860 return rv;
861 }
862 break;
864 case 'c':
865 if ((flags & FLAG_LEFT) == 0) {
866 while (width-- > 1) {
867 rv = (*ss->stuff)(ss, " ", 1);
868 if (rv < 0) {
869 return rv;
870 }
871 }
872 }
873 switch (type) {
874 case TYPE_INT16:
875 case TYPE_INTN:
876 u.ch = va_arg(ap, int);
877 rv = (*ss->stuff)(ss, &u.ch, 1);
878 break;
879 }
880 if (rv < 0) {
881 return rv;
882 }
883 if (flags & FLAG_LEFT) {
884 while (width-- > 1) {
885 rv = (*ss->stuff)(ss, " ", 1);
886 if (rv < 0) {
887 return rv;
888 }
889 }
890 }
891 break;
893 case 'p':
894 if (sizeof(void *) == sizeof(int32_t)) {
895 type = TYPE_UINT32;
896 } else if (sizeof(void *) == sizeof(int64_t)) {
897 type = TYPE_UINT64;
898 } else if (sizeof(void *) == sizeof(int)) {
899 type = TYPE_UINTN;
900 } else {
901 JS_ASSERT(0);
902 break;
903 }
904 radix = 16;
905 goto fetch_and_convert;
907 #if 0
908 case 'C':
909 case 'S':
910 case 'E':
911 case 'G':
912 // XXX not supported I suppose
913 JS_ASSERT(0);
914 break;
915 #endif
917 case 's':
918 if(type == TYPE_INT16) {
919 u.ws = va_arg(ap, const jschar*);
920 rv = cvt_s(ss, u.ws, width, prec, flags);
921 } else {
922 u.s = va_arg(ap, const char*);
923 rv = cvt_s(ss, u.s, width, prec, flags);
924 }
925 if (rv < 0) {
926 return rv;
927 }
928 break;
930 case 'n':
931 u.ip = va_arg(ap, int*);
932 if (u.ip) {
933 *u.ip = ss->cur - ss->base;
934 }
935 break;
937 default:
938 // Not a % token after all... skip it
939 #if 0
940 JS_ASSERT(0);
941 #endif
942 rv = (*ss->stuff)(ss, "%", 1);
943 if (rv < 0) {
944 return rv;
945 }
946 rv = (*ss->stuff)(ss, fmt - 1, 1);
947 if (rv < 0) {
948 return rv;
949 }
950 }
951 }
953 // Stuff trailing NUL
954 rv = (*ss->stuff)(ss, "\0", 1);
956 if (nas && nas != nasArray)
957 js_free(nas);
959 return rv;
960 }
962 /************************************************************************/
964 /*
965 * Stuff routine that automatically grows the js_malloc'd output buffer
966 * before it overflows.
967 */
968 static int
969 GrowStuff(SprintfState *ss, const char *sp, size_t len)
970 {
971 ptrdiff_t off;
972 char *newbase;
973 size_t newlen;
975 off = ss->cur - ss->base;
976 if (off + len >= ss->maxlen) {
977 /* Grow the buffer */
978 newlen = ss->maxlen + ((len > 32) ? len : 32);
979 newbase = static_cast<char *>(js_realloc(ss->base, newlen));
980 if (!newbase) {
981 /* Ran out of memory */
982 return -1;
983 }
984 ss->base = newbase;
985 ss->maxlen = newlen;
986 ss->cur = ss->base + off;
987 }
989 /* Copy data */
990 while (len) {
991 --len;
992 *ss->cur++ = *sp++;
993 }
994 MOZ_ASSERT(size_t(ss->cur - ss->base) <= ss->maxlen);
995 return 0;
996 }
998 /*
999 * sprintf into a js_malloc'd buffer
1000 */
1001 JS_PUBLIC_API(char *)
1002 JS_smprintf(const char *fmt, ...)
1003 {
1004 va_list ap;
1005 char *rv;
1007 va_start(ap, fmt);
1008 rv = JS_vsmprintf(fmt, ap);
1009 va_end(ap);
1010 return rv;
1011 }
1013 /*
1014 * Free memory allocated, for the caller, by JS_smprintf
1015 */
1016 JS_PUBLIC_API(void)
1017 JS_smprintf_free(char *mem)
1018 {
1019 js_free(mem);
1020 }
1022 JS_PUBLIC_API(char *)
1023 JS_vsmprintf(const char *fmt, va_list ap)
1024 {
1025 SprintfState ss;
1026 int rv;
1028 ss.stuff = GrowStuff;
1029 ss.base = 0;
1030 ss.cur = 0;
1031 ss.maxlen = 0;
1032 rv = dosprintf(&ss, fmt, ap);
1033 if (rv < 0) {
1034 js_free(ss.base);
1035 return 0;
1036 }
1037 return ss.base;
1038 }
1040 /*
1041 * Stuff routine that discards overflow data
1042 */
1043 static int
1044 LimitStuff(SprintfState *ss, const char *sp, size_t len)
1045 {
1046 size_t limit = ss->maxlen - (ss->cur - ss->base);
1048 if (len > limit)
1049 len = limit;
1050 while (len) {
1051 --len;
1052 *ss->cur++ = *sp++;
1053 }
1054 return 0;
1055 }
1057 /*
1058 * sprintf into a fixed size buffer. Make sure there is a NUL at the end
1059 * when finished.
1060 */
1061 JS_PUBLIC_API(uint32_t)
1062 JS_snprintf(char *out, uint32_t outlen, const char *fmt, ...)
1063 {
1064 va_list ap;
1065 int rv;
1067 JS_ASSERT(int32_t(outlen) > 0);
1068 if (int32_t(outlen) <= 0)
1069 return 0;
1071 va_start(ap, fmt);
1072 rv = JS_vsnprintf(out, outlen, fmt, ap);
1073 va_end(ap);
1074 return rv;
1075 }
1077 JS_PUBLIC_API(uint32_t)
1078 JS_vsnprintf(char *out, uint32_t outlen, const char *fmt, va_list ap)
1079 {
1080 SprintfState ss;
1081 uint32_t n;
1083 JS_ASSERT(int32_t(outlen) > 0);
1084 if (int32_t(outlen) <= 0) {
1085 return 0;
1086 }
1088 ss.stuff = LimitStuff;
1089 ss.base = out;
1090 ss.cur = out;
1091 ss.maxlen = outlen;
1092 (void) dosprintf(&ss, fmt, ap);
1094 /* If we added chars, and we didn't append a null, do it now. */
1095 if (ss.cur != ss.base && ss.cur[-1] != '\0')
1096 ss.cur[-1] = '\0';
1098 n = ss.cur - ss.base;
1099 return n ? n - 1 : n;
1100 }
1102 JS_PUBLIC_API(char *)
1103 JS_sprintf_append(char *last, const char *fmt, ...)
1104 {
1105 va_list ap;
1106 char *rv;
1108 va_start(ap, fmt);
1109 rv = JS_vsprintf_append(last, fmt, ap);
1110 va_end(ap);
1111 return rv;
1112 }
1114 JS_PUBLIC_API(char *)
1115 JS_vsprintf_append(char *last, const char *fmt, va_list ap)
1116 {
1117 SprintfState ss;
1118 int rv;
1120 ss.stuff = GrowStuff;
1121 if (last) {
1122 size_t lastlen = strlen(last);
1123 ss.base = last;
1124 ss.cur = last + lastlen;
1125 ss.maxlen = lastlen;
1126 } else {
1127 ss.base = 0;
1128 ss.cur = 0;
1129 ss.maxlen = 0;
1130 }
1131 rv = dosprintf(&ss, fmt, ap);
1132 if (rv < 0) {
1133 js_free(ss.base);
1134 return 0;
1135 }
1136 return ss.base;
1137 }
1139 #undef TYPE_INT16
1140 #undef TYPE_UINT16
1141 #undef TYPE_INTN
1142 #undef TYPE_UINTN
1143 #undef TYPE_INT32
1144 #undef TYPE_UINT32
1145 #undef TYPE_INT64
1146 #undef TYPE_UINT64
1147 #undef TYPE_STRING
1148 #undef TYPE_DOUBLE
1149 #undef TYPE_INTSTR
1150 #undef TYPE_WSTRING
1151 #undef TYPE_UNKNOWN
1153 #undef FLAG_LEFT
1154 #undef FLAG_SIGNED
1155 #undef FLAG_SPACED
1156 #undef FLAG_ZEROS
1157 #undef FLAG_NEG