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