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