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