Line data Source code
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 "mozilla/AllocPolicy.h"
14 : #include "mozilla/Printf.h"
15 : #include "mozilla/Sprintf.h"
16 : #include "mozilla/UniquePtrExtensions.h"
17 : #include "mozilla/Vector.h"
18 :
19 : #include <stdarg.h>
20 : #include <stdio.h>
21 : #include <stdlib.h>
22 : #include <string.h>
23 :
24 : #if defined(XP_WIN)
25 : #include <windows.h>
26 : #endif
27 :
28 : /*
29 : * Note: on some platforms va_list is defined as an array,
30 : * and requires array notation.
31 : */
32 : #ifdef HAVE_VA_COPY
33 : #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo, bar)
34 : #elif defined(HAVE_VA_LIST_AS_ARRAY)
35 : #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
36 : #else
37 : #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
38 : #endif
39 :
40 : /*
41 : * Numbered Argument State
42 : */
43 : struct NumArgState
44 : {
45 : int type; // type of the current ap
46 : va_list ap; // point to the corresponding position on ap
47 : };
48 :
49 : typedef mozilla::Vector<NumArgState, 20, mozilla::MallocAllocPolicy> NumArgStateVector;
50 :
51 :
52 : #define TYPE_SHORT 0
53 : #define TYPE_USHORT 1
54 : #define TYPE_INTN 2
55 : #define TYPE_UINTN 3
56 : #define TYPE_LONG 4
57 : #define TYPE_ULONG 5
58 : #define TYPE_LONGLONG 6
59 : #define TYPE_ULONGLONG 7
60 : #define TYPE_STRING 8
61 : #define TYPE_DOUBLE 9
62 : #define TYPE_INTSTR 10
63 : #define TYPE_POINTER 11
64 : #if defined(XP_WIN)
65 : #define TYPE_WSTRING 12
66 : #endif
67 : #define TYPE_UNKNOWN 20
68 :
69 : #define FLAG_LEFT 0x1
70 : #define FLAG_SIGNED 0x2
71 : #define FLAG_SPACED 0x4
72 : #define FLAG_ZEROS 0x8
73 : #define FLAG_NEG 0x10
74 :
75 : // Fill into the buffer using the data in src
76 : bool
77 8436 : mozilla::PrintfTarget::fill2(const char* src, int srclen, int width, int flags)
78 : {
79 8436 : char space = ' ';
80 :
81 8436 : width -= srclen;
82 8436 : if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting
83 0 : if (flags & FLAG_ZEROS)
84 0 : space = '0';
85 0 : while (--width >= 0) {
86 0 : if (!emit(&space, 1))
87 0 : return false;
88 : }
89 : }
90 :
91 : // Copy out the source data
92 8436 : if (!emit(src, srclen))
93 0 : return false;
94 :
95 8436 : if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting
96 0 : while (--width >= 0) {
97 0 : if (!emit(&space, 1))
98 0 : return false;
99 : }
100 : }
101 8436 : return true;
102 : }
103 :
104 : /*
105 : * Fill a number. The order is: optional-sign zero-filling conversion-digits
106 : */
107 : bool
108 15184 : mozilla::PrintfTarget::fill_n(const char* src, int srclen, int width, int prec, int type, int flags)
109 : {
110 15184 : int zerowidth = 0;
111 15184 : int precwidth = 0;
112 15184 : int signwidth = 0;
113 15184 : int leftspaces = 0;
114 15184 : int rightspaces = 0;
115 : int cvtwidth;
116 : char sign;
117 :
118 15184 : if ((type & 1) == 0) {
119 733 : if (flags & FLAG_NEG) {
120 13 : sign = '-';
121 13 : signwidth = 1;
122 720 : } else if (flags & FLAG_SIGNED) {
123 0 : sign = '+';
124 0 : signwidth = 1;
125 720 : } else if (flags & FLAG_SPACED) {
126 0 : sign = ' ';
127 0 : signwidth = 1;
128 : }
129 : }
130 15184 : cvtwidth = signwidth + srclen;
131 :
132 15184 : if (prec > 0) {
133 1 : if (prec > srclen) {
134 0 : precwidth = prec - srclen; // Need zero filling
135 0 : cvtwidth += precwidth;
136 : }
137 : }
138 :
139 15184 : if ((flags & FLAG_ZEROS) && (prec < 0)) {
140 18 : if (width > cvtwidth) {
141 8 : zerowidth = width - cvtwidth; // Zero filling
142 8 : cvtwidth += zerowidth;
143 : }
144 : }
145 :
146 15184 : if (flags & FLAG_LEFT) {
147 0 : if (width > cvtwidth) {
148 : // Space filling on the right (i.e. left adjusting)
149 0 : rightspaces = width - cvtwidth;
150 : }
151 : } else {
152 15184 : if (width > cvtwidth) {
153 : // Space filling on the left (i.e. right adjusting)
154 0 : leftspaces = width - cvtwidth;
155 : }
156 : }
157 15184 : while (--leftspaces >= 0) {
158 0 : if (!emit(" ", 1))
159 0 : return false;
160 : }
161 15184 : if (signwidth) {
162 13 : if (!emit(&sign, 1))
163 0 : return false;
164 : }
165 15184 : while (--precwidth >= 0) {
166 0 : if (!emit("0", 1))
167 0 : return false;
168 : }
169 15200 : while (--zerowidth >= 0) {
170 8 : if (!emit("0", 1))
171 0 : return false;
172 : }
173 15184 : if (!emit(src, uint32_t(srclen)))
174 0 : return false;
175 15184 : while (--rightspaces >= 0) {
176 0 : if (!emit(" ", 1))
177 0 : return false;
178 : }
179 15184 : return true;
180 : }
181 :
182 : /* Convert a long into its printable form. */
183 : bool
184 15116 : mozilla::PrintfTarget::cvt_l(long num, int width, int prec, int radix,
185 : int type, int flags, const char* hexp)
186 : {
187 : char cvtbuf[100];
188 : char* cvt;
189 : int digits;
190 :
191 : // according to the man page this needs to happen
192 15116 : if ((prec == 0) && (num == 0))
193 0 : return true;
194 :
195 : // Converting decimal is a little tricky. In the unsigned case we
196 : // need to stop when we hit 10 digits. In the signed case, we can
197 : // stop when the number is zero.
198 15116 : cvt = cvtbuf + sizeof(cvtbuf);
199 15116 : digits = 0;
200 96834 : while (num) {
201 40859 : int digit = (((unsigned long)num) % radix) & 0xF;
202 40859 : *--cvt = hexp[digit];
203 40859 : digits++;
204 40859 : num = (long)(((unsigned long)num) / radix);
205 : }
206 15116 : if (digits == 0) {
207 4253 : *--cvt = '0';
208 4253 : digits++;
209 : }
210 :
211 : // Now that we have the number converted without its sign, deal with
212 : // the sign and zero padding.
213 15116 : return fill_n(cvt, digits, width, prec, type, flags);
214 : }
215 :
216 : /* Convert a 64-bit integer into its printable form. */
217 : bool
218 68 : mozilla::PrintfTarget::cvt_ll(int64_t num, int width, int prec, int radix,
219 : int type, int flags, const char* hexp)
220 : {
221 : // According to the man page, this needs to happen.
222 68 : if (prec == 0 && num == 0)
223 0 : return true;
224 :
225 : // Converting decimal is a little tricky. In the unsigned case we
226 : // need to stop when we hit 10 digits. In the signed case, we can
227 : // stop when the number is zero.
228 68 : int64_t rad = int64_t(radix);
229 : char cvtbuf[100];
230 68 : char* cvt = cvtbuf + sizeof(cvtbuf);
231 68 : int digits = 0;
232 620 : while (num != 0) {
233 276 : int64_t quot = uint64_t(num) / rad;
234 276 : int64_t rem = uint64_t(num) % rad;
235 276 : int32_t digit = int32_t(rem);
236 276 : *--cvt = hexp[digit & 0xf];
237 276 : digits++;
238 276 : num = quot;
239 : }
240 68 : if (digits == 0) {
241 45 : *--cvt = '0';
242 45 : digits++;
243 : }
244 :
245 : // Now that we have the number converted without its sign, deal with
246 : // the sign and zero padding.
247 68 : return fill_n(cvt, digits, width, prec, type, flags);
248 : }
249 :
250 : /*
251 : * Convert a double precision floating point number into its printable
252 : * form.
253 : */
254 : bool
255 0 : mozilla::PrintfTarget::cvt_f(double d, const char* fmt0, const char* fmt1)
256 : {
257 : char fin[20];
258 : // The size is chosen such that we can print DBL_MAX. See bug#1350097.
259 : char fout[320];
260 0 : int amount = fmt1 - fmt0;
261 :
262 0 : MOZ_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
263 0 : if (amount >= (int)sizeof(fin)) {
264 : // Totally bogus % command to sprintf. Just ignore it
265 0 : return true;
266 : }
267 0 : memcpy(fin, fmt0, (size_t)amount);
268 0 : fin[amount] = 0;
269 :
270 : // Convert floating point using the native snprintf code
271 : #ifdef DEBUG
272 : {
273 0 : const char* p = fin;
274 0 : while (*p) {
275 0 : MOZ_ASSERT(*p != 'L');
276 0 : p++;
277 : }
278 : }
279 : #endif
280 0 : size_t len = SprintfLiteral(fout, fin, d);
281 0 : MOZ_RELEASE_ASSERT(len <= sizeof(fout));
282 :
283 0 : return emit(fout, len);
284 : }
285 :
286 : /*
287 : * Convert a string into its printable form. "width" is the output
288 : * width. "prec" is the maximum number of characters of "s" to output,
289 : * where -1 means until NUL.
290 : */
291 : bool
292 8436 : mozilla::PrintfTarget::cvt_s(const char* s, int width, int prec, int flags)
293 : {
294 8436 : if (prec == 0)
295 0 : return true;
296 8436 : if (!s)
297 0 : s = "(null)";
298 :
299 : // Limit string length by precision value
300 8436 : int slen = int(strlen(s));
301 8436 : if (0 < prec && prec < slen)
302 0 : slen = prec;
303 :
304 : // and away we go
305 8436 : return fill2(s, slen, width, flags);
306 : }
307 :
308 : /*
309 : * BuildArgArray stands for Numbered Argument list Sprintf
310 : * for example,
311 : * fmp = "%4$i, %2$d, %3s, %1d";
312 : * the number must start from 1, and no gap among them
313 : */
314 : static bool
315 9199 : BuildArgArray(const char* fmt, va_list ap, NumArgStateVector& nas)
316 : {
317 9199 : size_t number = 0, cn = 0, i;
318 : const char* p;
319 : char c;
320 :
321 :
322 : // First pass:
323 : // Detemine how many legal % I have got, then allocate space.
324 :
325 9199 : p = fmt;
326 9199 : i = 0;
327 136689 : while ((c = *p++) != 0) {
328 63745 : if (c != '%')
329 40113 : continue;
330 23632 : if ((c = *p++) == '%') // skip %% case
331 0 : continue;
332 :
333 23704 : while (c != 0) {
334 23668 : if (c > '9' || c < '0') {
335 23632 : if (c == '$') { // numbered argument case
336 0 : if (i > 0)
337 0 : MOZ_CRASH("Bad format string");
338 0 : number++;
339 : } else { // non-numbered argument case
340 23632 : if (number > 0)
341 0 : MOZ_CRASH("Bad format string");
342 23632 : i = 1;
343 : }
344 23632 : break;
345 : }
346 :
347 36 : c = *p++;
348 : }
349 : }
350 :
351 9199 : if (number == 0)
352 9199 : return true;
353 :
354 : // Only allow a limited number of arguments.
355 0 : MOZ_RELEASE_ASSERT(number <= 20);
356 :
357 0 : if (!nas.growByUninitialized(number))
358 0 : return false;
359 :
360 0 : for (i = 0; i < number; i++)
361 0 : nas[i].type = TYPE_UNKNOWN;
362 :
363 :
364 : // Second pass:
365 : // Set nas[].type.
366 :
367 0 : p = fmt;
368 0 : while ((c = *p++) != 0) {
369 0 : if (c != '%')
370 0 : continue;
371 0 : c = *p++;
372 0 : if (c == '%')
373 0 : continue;
374 :
375 0 : cn = 0;
376 0 : while (c && c != '$') { // should improve error check later
377 0 : cn = cn*10 + c - '0';
378 0 : c = *p++;
379 : }
380 :
381 0 : if (!c || cn < 1 || cn > number)
382 0 : MOZ_CRASH("Bad format string");
383 :
384 : // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
385 0 : cn--;
386 0 : if (nas[cn].type != TYPE_UNKNOWN)
387 0 : continue;
388 :
389 0 : c = *p++;
390 :
391 : // flags
392 0 : while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
393 0 : c = *p++;
394 : }
395 :
396 : // width
397 0 : if (c == '*') {
398 : // not supported feature, for the argument is not numbered
399 0 : MOZ_CRASH("Bad format string");
400 : }
401 :
402 0 : while ((c >= '0') && (c <= '9')) {
403 0 : c = *p++;
404 : }
405 :
406 : // precision
407 0 : if (c == '.') {
408 0 : c = *p++;
409 0 : if (c == '*') {
410 : // not supported feature, for the argument is not numbered
411 0 : MOZ_CRASH("Bad format string");
412 : }
413 :
414 0 : while ((c >= '0') && (c <= '9')) {
415 0 : c = *p++;
416 : }
417 : }
418 :
419 : // size
420 0 : nas[cn].type = TYPE_INTN;
421 0 : if (c == 'h') {
422 0 : nas[cn].type = TYPE_SHORT;
423 0 : c = *p++;
424 0 : } else if (c == 'L') {
425 0 : nas[cn].type = TYPE_LONGLONG;
426 0 : c = *p++;
427 0 : } else if (c == 'l') {
428 0 : nas[cn].type = TYPE_LONG;
429 0 : c = *p++;
430 0 : if (c == 'l') {
431 0 : nas[cn].type = TYPE_LONGLONG;
432 0 : c = *p++;
433 : }
434 0 : } else if (c == 'z' || c == 'I') {
435 : static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
436 : sizeof(size_t) == sizeof(long long),
437 : "size_t is not one of the expected sizes");
438 0 : nas[cn].type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
439 : sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
440 0 : c = *p++;
441 : }
442 :
443 : // format
444 0 : switch (c) {
445 : case 'd':
446 : case 'c':
447 : case 'i':
448 0 : break;
449 :
450 : case 'o':
451 : case 'u':
452 : case 'x':
453 : case 'X':
454 : // Mark as unsigned type.
455 0 : nas[cn].type |= 1;
456 0 : break;
457 :
458 : case 'e':
459 : case 'f':
460 : case 'g':
461 0 : nas[cn].type = TYPE_DOUBLE;
462 0 : break;
463 :
464 : case 'p':
465 0 : nas[cn].type = TYPE_POINTER;
466 0 : break;
467 :
468 : case 'S':
469 : #if defined(XP_WIN)
470 : nas[cn].type = TYPE_WSTRING;
471 : #else
472 0 : MOZ_ASSERT(0);
473 : nas[cn].type = TYPE_UNKNOWN;
474 : #endif
475 : break;
476 :
477 : case 's':
478 : #if defined(XP_WIN)
479 : if (nas[cn].type == TYPE_LONG) {
480 : nas[cn].type = TYPE_WSTRING;
481 : break;
482 : }
483 : #endif
484 : // Other type sizes are not supported here.
485 0 : MOZ_ASSERT (nas[cn].type == TYPE_INTN);
486 0 : nas[cn].type = TYPE_STRING;
487 0 : break;
488 :
489 : case 'n':
490 0 : nas[cn].type = TYPE_INTSTR;
491 0 : break;
492 :
493 : default:
494 0 : MOZ_ASSERT(0);
495 : nas[cn].type = TYPE_UNKNOWN;
496 : break;
497 : }
498 :
499 : // get a legal para.
500 0 : if (nas[cn].type == TYPE_UNKNOWN)
501 0 : MOZ_CRASH("Bad format string");
502 : }
503 :
504 :
505 : // Third pass:
506 : // Fill nas[].ap.
507 :
508 0 : cn = 0;
509 0 : while (cn < number) {
510 : // A TYPE_UNKNOWN here means that the format asked for a
511 : // positional argument without specifying the meaning of some
512 : // earlier argument.
513 0 : MOZ_ASSERT (nas[cn].type != TYPE_UNKNOWN);
514 :
515 0 : VARARGS_ASSIGN(nas[cn].ap, ap);
516 :
517 0 : switch (nas[cn].type) {
518 : case TYPE_SHORT:
519 : case TYPE_USHORT:
520 : case TYPE_INTN:
521 0 : case TYPE_UINTN: (void) va_arg(ap, int); break;
522 0 : case TYPE_LONG: (void) va_arg(ap, long); break;
523 0 : case TYPE_ULONG: (void) va_arg(ap, unsigned long); break;
524 0 : case TYPE_LONGLONG: (void) va_arg(ap, long long); break;
525 0 : case TYPE_ULONGLONG: (void) va_arg(ap, unsigned long long); break;
526 0 : case TYPE_STRING: (void) va_arg(ap, char*); break;
527 0 : case TYPE_INTSTR: (void) va_arg(ap, int*); break;
528 0 : case TYPE_DOUBLE: (void) va_arg(ap, double); break;
529 0 : case TYPE_POINTER: (void) va_arg(ap, void*); break;
530 : #if defined(XP_WIN)
531 : case TYPE_WSTRING: (void) va_arg(ap, wchar_t*); break;
532 : #endif
533 :
534 0 : default: MOZ_CRASH();
535 : }
536 :
537 0 : cn++;
538 : }
539 :
540 0 : return true;
541 : }
542 :
543 9146 : mozilla::PrintfTarget::PrintfTarget()
544 9146 : : mEmitted(0)
545 : {
546 9146 : }
547 :
548 : bool
549 9198 : mozilla::PrintfTarget::vprint(const char* fmt, va_list ap)
550 : {
551 : char c;
552 : int flags, width, prec, radix, type;
553 : union {
554 : char ch;
555 : int i;
556 : long l;
557 : long long ll;
558 : double d;
559 : const char* s;
560 : int* ip;
561 : void* p;
562 : #if defined(XP_WIN)
563 : const wchar_t* ws;
564 : #endif
565 : } u;
566 : const char* fmt0;
567 : static const char hex[] = "0123456789abcdef";
568 : static const char HEX[] = "0123456789ABCDEF";
569 : const char* hexp;
570 : int i;
571 : char pattern[20];
572 9198 : const char* dolPt = nullptr; // in "%4$.2f", dolPt will point to '.'
573 :
574 : // Build an argument array, IF the fmt is numbered argument
575 : // list style, to contain the Numbered Argument list pointers.
576 :
577 18397 : NumArgStateVector nas;
578 9198 : if (!BuildArgArray(fmt, ap, nas)) {
579 : // the fmt contains error Numbered Argument format, jliu@netscape.com
580 0 : MOZ_CRASH("Bad format string");
581 : }
582 :
583 136630 : while ((c = *fmt++) != 0) {
584 63716 : if (c != '%') {
585 40083 : if (!emit(fmt - 1, 1))
586 0 : return false;
587 :
588 40081 : continue;
589 : }
590 23633 : fmt0 = fmt - 1;
591 :
592 : // Gobble up the % format string. Hopefully we have handled all
593 : // of the strange cases!
594 23633 : flags = 0;
595 23633 : c = *fmt++;
596 23633 : if (c == '%') {
597 : // quoting a % with %%
598 0 : if (!emit(fmt - 1, 1))
599 0 : return false;
600 :
601 0 : continue;
602 : }
603 :
604 23633 : if (!nas.empty()) {
605 : // the fmt contains the Numbered Arguments feature
606 0 : i = 0;
607 0 : while (c && c != '$') { // should improve error check later
608 0 : i = (i * 10) + (c - '0');
609 0 : c = *fmt++;
610 : }
611 :
612 0 : if (nas[i - 1].type == TYPE_UNKNOWN)
613 0 : MOZ_CRASH("Bad format string");
614 :
615 0 : ap = nas[i - 1].ap;
616 0 : dolPt = fmt;
617 0 : c = *fmt++;
618 : }
619 :
620 : // Examine optional flags. Note that we do not implement the
621 : // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
622 : // somewhat ambiguous and not ideal, which is perhaps why
623 : // the various sprintf() implementations are inconsistent
624 : // on this feature.
625 23669 : while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
626 18 : if (c == '-') flags |= FLAG_LEFT;
627 18 : if (c == '+') flags |= FLAG_SIGNED;
628 18 : if (c == ' ') flags |= FLAG_SPACED;
629 18 : if (c == '0') flags |= FLAG_ZEROS;
630 18 : c = *fmt++;
631 : }
632 23633 : if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
633 23633 : if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
634 :
635 : // width
636 23633 : if (c == '*') {
637 0 : c = *fmt++;
638 0 : width = va_arg(ap, int);
639 0 : if (width < 0) {
640 0 : width = -width;
641 0 : flags |= FLAG_LEFT;
642 0 : flags &= ~FLAG_ZEROS;
643 : }
644 : } else {
645 23633 : width = 0;
646 23669 : while ((c >= '0') && (c <= '9')) {
647 18 : width = (width * 10) + (c - '0');
648 18 : c = *fmt++;
649 : }
650 : }
651 :
652 : // precision
653 23633 : prec = -1;
654 23633 : if (c == '.') {
655 1 : c = *fmt++;
656 1 : if (c == '*') {
657 0 : c = *fmt++;
658 0 : prec = va_arg(ap, int);
659 : } else {
660 1 : prec = 0;
661 5 : while ((c >= '0') && (c <= '9')) {
662 2 : prec = (prec * 10) + (c - '0');
663 2 : c = *fmt++;
664 : }
665 : }
666 : }
667 :
668 : // size
669 23633 : type = TYPE_INTN;
670 23633 : if (c == 'h') {
671 0 : type = TYPE_SHORT;
672 0 : c = *fmt++;
673 23633 : } else if (c == 'L') {
674 0 : type = TYPE_LONGLONG;
675 0 : c = *fmt++;
676 23633 : } else if (c == 'l') {
677 22 : type = TYPE_LONG;
678 22 : c = *fmt++;
679 22 : if (c == 'l') {
680 0 : type = TYPE_LONGLONG;
681 0 : c = *fmt++;
682 : }
683 23611 : } else if (c == 'z' || c == 'I') {
684 : static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
685 : sizeof(size_t) == sizeof(long long),
686 : "size_t is not one of the expected sizes");
687 0 : type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
688 : sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
689 0 : c = *fmt++;
690 : }
691 :
692 : // format
693 23633 : hexp = hex;
694 23633 : switch (c) {
695 : case 'd': case 'i': // decimal/integer
696 733 : radix = 10;
697 733 : goto fetch_and_convert;
698 :
699 : case 'o': // octal
700 0 : radix = 8;
701 0 : type |= 1;
702 0 : goto fetch_and_convert;
703 :
704 : case 'u': // unsigned decimal
705 12385 : radix = 10;
706 12385 : type |= 1;
707 12385 : goto fetch_and_convert;
708 :
709 : case 'x': // unsigned hex
710 1997 : radix = 16;
711 1997 : type |= 1;
712 1997 : goto fetch_and_convert;
713 :
714 : case 'X': // unsigned HEX
715 1 : radix = 16;
716 1 : hexp = HEX;
717 1 : type |= 1;
718 1 : goto fetch_and_convert;
719 :
720 : fetch_and_convert:
721 15184 : switch (type) {
722 : case TYPE_SHORT:
723 0 : u.l = va_arg(ap, int);
724 0 : if (u.l < 0) {
725 0 : u.l = -u.l;
726 0 : flags |= FLAG_NEG;
727 : }
728 0 : goto do_long;
729 : case TYPE_USHORT:
730 0 : u.l = (unsigned short) va_arg(ap, unsigned int);
731 0 : goto do_long;
732 : case TYPE_INTN:
733 723 : u.l = va_arg(ap, int);
734 723 : if (u.l < 0) {
735 13 : u.l = -u.l;
736 13 : flags |= FLAG_NEG;
737 : }
738 723 : goto do_long;
739 : case TYPE_UINTN:
740 14371 : u.l = (long)va_arg(ap, unsigned int);
741 14371 : goto do_long;
742 :
743 : case TYPE_LONG:
744 10 : u.l = va_arg(ap, long);
745 10 : if (u.l < 0) {
746 0 : u.l = -u.l;
747 0 : flags |= FLAG_NEG;
748 : }
749 10 : goto do_long;
750 : case TYPE_ULONG:
751 12 : u.l = (long)va_arg(ap, unsigned long);
752 : do_long:
753 15116 : if (!cvt_l(u.l, width, prec, radix, type, flags, hexp))
754 0 : return false;
755 :
756 15116 : break;
757 :
758 : case TYPE_LONGLONG:
759 0 : u.ll = va_arg(ap, long long);
760 0 : if (u.ll < 0) {
761 0 : u.ll = -u.ll;
762 0 : flags |= FLAG_NEG;
763 : }
764 0 : goto do_longlong;
765 : case TYPE_POINTER:
766 68 : u.ll = (uintptr_t)va_arg(ap, void*);
767 68 : goto do_longlong;
768 : case TYPE_ULONGLONG:
769 0 : u.ll = va_arg(ap, unsigned long long);
770 : do_longlong:
771 68 : if (!cvt_ll(u.ll, width, prec, radix, type, flags, hexp))
772 0 : return false;
773 :
774 68 : break;
775 : }
776 15184 : break;
777 :
778 : case 'e':
779 : case 'E':
780 : case 'f':
781 : case 'g':
782 0 : u.d = va_arg(ap, double);
783 0 : if (!nas.empty()) {
784 0 : i = fmt - dolPt;
785 0 : if (i < int(sizeof(pattern))) {
786 0 : pattern[0] = '%';
787 0 : memcpy(&pattern[1], dolPt, size_t(i));
788 0 : if (!cvt_f(u.d, pattern, &pattern[i + 1]))
789 0 : return false;
790 : }
791 : } else {
792 0 : if (!cvt_f(u.d, fmt0, fmt))
793 0 : return false;
794 : }
795 :
796 0 : break;
797 :
798 : case 'c':
799 14 : if ((flags & FLAG_LEFT) == 0) {
800 14 : while (width-- > 1) {
801 0 : if (!emit(" ", 1))
802 0 : return false;
803 : }
804 : }
805 14 : switch (type) {
806 : case TYPE_SHORT:
807 : case TYPE_INTN:
808 14 : u.ch = va_arg(ap, int);
809 14 : if (!emit(&u.ch, 1))
810 0 : return false;
811 14 : break;
812 : }
813 14 : if (flags & FLAG_LEFT) {
814 0 : while (width-- > 1) {
815 0 : if (!emit(" ", 1))
816 0 : return false;
817 : }
818 : }
819 14 : break;
820 :
821 : case 'p':
822 68 : type = TYPE_POINTER;
823 68 : radix = 16;
824 68 : goto fetch_and_convert;
825 :
826 : case 's':
827 8436 : if (type == TYPE_INTN) {
828 8436 : u.s = va_arg(ap, const char*);
829 8436 : if (!cvt_s(u.s, width, prec, flags))
830 0 : return false;
831 8436 : break;
832 : }
833 0 : MOZ_ASSERT(type == TYPE_LONG);
834 : MOZ_FALLTHROUGH;
835 : case 'S':
836 : #if defined(XP_WIN)
837 : {
838 : u.ws = va_arg(ap, const wchar_t*);
839 :
840 : int rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL);
841 : if (rv == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
842 : if (!cvt_s("<unicode errors in string>", width, prec, flags)) {
843 : return false;
844 : }
845 : } else {
846 : if (rv == 0) {
847 : rv = 1;
848 : }
849 : UniqueFreePtr<char[]> buf((char*)malloc(rv));
850 : WideCharToMultiByte(CP_ACP, 0, u.ws, -1, buf.get(), rv, NULL, NULL);
851 : buf[rv - 1] = '\0';
852 :
853 : if (!cvt_s(buf.get(), width, prec, flags)) {
854 : return false;
855 : }
856 : }
857 : }
858 : #else
859 : // Not supported here.
860 0 : MOZ_ASSERT(0);
861 : #endif
862 : break;
863 :
864 : case 'n':
865 0 : u.ip = va_arg(ap, int*);
866 0 : if (u.ip) {
867 0 : *u.ip = mEmitted;
868 : }
869 0 : break;
870 :
871 : default:
872 : // Not a % token after all... skip it
873 0 : if (!emit("%", 1))
874 0 : return false;
875 0 : if (!emit(fmt - 1, 1))
876 0 : return false;
877 : }
878 : }
879 :
880 9199 : return true;
881 : }
882 :
883 : /************************************************************************/
884 :
885 : bool
886 74 : mozilla::PrintfTarget::print(const char* format, ...)
887 : {
888 : va_list ap;
889 :
890 74 : va_start(ap, format);
891 74 : bool result = vprint(format, ap);
892 74 : va_end(ap);
893 74 : return result;
894 : }
895 :
896 : #undef TYPE_SHORT
897 : #undef TYPE_USHORT
898 : #undef TYPE_INTN
899 : #undef TYPE_UINTN
900 : #undef TYPE_LONG
901 : #undef TYPE_ULONG
902 : #undef TYPE_LONGLONG
903 : #undef TYPE_ULONGLONG
904 : #undef TYPE_STRING
905 : #undef TYPE_DOUBLE
906 : #undef TYPE_INTSTR
907 : #undef TYPE_POINTER
908 : #undef TYPE_WSTRING
909 : #undef TYPE_UNKNOWN
910 :
911 : #undef FLAG_LEFT
912 : #undef FLAG_SIGNED
913 : #undef FLAG_SPACED
914 : #undef FLAG_ZEROS
915 : #undef FLAG_NEG
|