Line data Source code
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 : * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
11 : *
12 : * Contributor(s):
13 : * Kipp E.B. Hickman <kipp@netscape.com> (original author)
14 : * Frank Yung-Fong Tang <ftang@netscape.com>
15 : * Daniele Nicolodi <daniele@grinta.net>
16 : */
17 :
18 : /*
19 : * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
20 : * Changed to use nsMemory and Frozen linkage
21 : * -- Prasad <prasad@medhas.org>
22 : */
23 :
24 : #include <stdarg.h>
25 : #include <stddef.h>
26 : #include <stdio.h>
27 : #include <string.h>
28 : #include "prdtoa.h"
29 : #include "mozilla/Logging.h"
30 : #include "mozilla/Sprintf.h"
31 : #include "nsCRTGlue.h"
32 : #include "nsTextFormatter.h"
33 : #include "nsMemory.h"
34 :
35 : /*
36 : ** Note: on some platforms va_list is defined as an array,
37 : ** and requires array notation.
38 : */
39 :
40 : #ifdef HAVE_VA_COPY
41 : #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
42 : #elif defined(HAVE_VA_LIST_AS_ARRAY)
43 : #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
44 : #else
45 : #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
46 : #endif
47 :
48 : struct SprintfStateStr
49 : {
50 : int (*stuff)(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen);
51 :
52 : char16_t* base;
53 : char16_t* cur;
54 : uint32_t maxlen;
55 :
56 : void* stuffclosure;
57 : };
58 :
59 : /*
60 : ** Numbered Arguement State
61 : */
62 : struct NumArgState
63 : {
64 : int type; /* type of the current ap */
65 : va_list ap; /* point to the corresponding position on ap */
66 :
67 : enum Type
68 : {
69 : INT16,
70 : UINT16,
71 : INTN,
72 : UINTN,
73 : INT32,
74 : UINT32,
75 : INT64,
76 : UINT64,
77 : STRING,
78 : DOUBLE,
79 : INTSTR,
80 : UNISTRING,
81 : UNKNOWN
82 : };
83 : };
84 :
85 : #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
86 :
87 : #define _LEFT 0x1
88 : #define _SIGNED 0x2
89 : #define _SPACED 0x4
90 : #define _ZEROS 0x8
91 : #define _NEG 0x10
92 :
93 : #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
94 :
95 : #define FREE_IF_NECESSARY(nas) if (nas && (nas != nasArray)) { free(nas); }
96 :
97 : /*
98 : ** Fill into the buffer using the data in src
99 : */
100 : static int
101 79 : fill2(SprintfStateStr* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
102 : int aFlags)
103 : {
104 79 : char16_t space = ' ';
105 : int rv;
106 :
107 79 : aWidth -= aSrcLen;
108 : /* Right adjusting */
109 79 : if ((aWidth > 0) && ((aFlags & _LEFT) == 0)) {
110 0 : if (aFlags & _ZEROS) {
111 0 : space = '0';
112 : }
113 0 : while (--aWidth >= 0) {
114 0 : rv = (*aState->stuff)(aState, &space, 1);
115 0 : if (rv < 0) {
116 0 : return rv;
117 : }
118 : }
119 : }
120 :
121 : /* Copy out the source data */
122 79 : rv = (*aState->stuff)(aState, aSrc, aSrcLen);
123 79 : if (rv < 0) {
124 0 : return rv;
125 : }
126 :
127 : /* Left adjusting */
128 79 : if ((aWidth > 0) && ((aFlags & _LEFT) != 0)) {
129 0 : while (--aWidth >= 0) {
130 0 : rv = (*aState->stuff)(aState, &space, 1);
131 0 : if (rv < 0) {
132 0 : return rv;
133 : }
134 : }
135 : }
136 79 : return 0;
137 : }
138 :
139 : /*
140 : ** Fill a number. The order is: optional-sign zero-filling conversion-digits
141 : */
142 : static int
143 622 : fill_n(SprintfStateStr* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
144 : int aPrec, int aType, int aFlags)
145 : {
146 622 : int zerowidth = 0;
147 622 : int precwidth = 0;
148 622 : int signwidth = 0;
149 622 : int leftspaces = 0;
150 622 : int rightspaces = 0;
151 : int cvtwidth;
152 : int rv;
153 : char16_t sign;
154 622 : char16_t space = ' ';
155 622 : char16_t zero = '0';
156 :
157 622 : if ((aType & 1) == 0) {
158 622 : if (aFlags & _NEG) {
159 0 : sign = '-';
160 0 : signwidth = 1;
161 622 : } else if (aFlags & _SIGNED) {
162 0 : sign = '+';
163 0 : signwidth = 1;
164 622 : } else if (aFlags & _SPACED) {
165 0 : sign = ' ';
166 0 : signwidth = 1;
167 : }
168 : }
169 622 : cvtwidth = signwidth + aSrcLen;
170 :
171 622 : if (aPrec > 0) {
172 0 : if (aPrec > aSrcLen) {
173 : /* Need zero filling */
174 0 : precwidth = aPrec - aSrcLen;
175 0 : cvtwidth += precwidth;
176 : }
177 : }
178 :
179 622 : if ((aFlags & _ZEROS) && (aPrec < 0)) {
180 0 : if (aWidth > cvtwidth) {
181 : /* Zero filling */
182 0 : zerowidth = aWidth - cvtwidth;
183 0 : cvtwidth += zerowidth;
184 : }
185 : }
186 :
187 622 : if (aFlags & _LEFT) {
188 0 : if (aWidth > cvtwidth) {
189 : /* Space filling on the right (i.e. left adjusting) */
190 0 : rightspaces = aWidth - cvtwidth;
191 : }
192 : } else {
193 622 : if (aWidth > cvtwidth) {
194 : /* Space filling on the left (i.e. right adjusting) */
195 0 : leftspaces = aWidth - cvtwidth;
196 : }
197 : }
198 622 : while (--leftspaces >= 0) {
199 0 : rv = (*aState->stuff)(aState, &space, 1);
200 0 : if (rv < 0) {
201 0 : return rv;
202 : }
203 : }
204 622 : if (signwidth) {
205 0 : rv = (*aState->stuff)(aState, &sign, 1);
206 0 : if (rv < 0) {
207 0 : return rv;
208 : }
209 : }
210 622 : while (--precwidth >= 0) {
211 0 : rv = (*aState->stuff)(aState, &space, 1);
212 0 : if (rv < 0) {
213 0 : return rv;
214 : }
215 : }
216 622 : while (--zerowidth >= 0) {
217 0 : rv = (*aState->stuff)(aState, &zero, 1);
218 0 : if (rv < 0) {
219 0 : return rv;
220 : }
221 : }
222 622 : rv = (*aState->stuff)(aState, aSrc, aSrcLen);
223 622 : if (rv < 0) {
224 0 : return rv;
225 : }
226 622 : while (--rightspaces >= 0) {
227 0 : rv = (*aState->stuff)(aState, &space, 1);
228 0 : if (rv < 0) {
229 0 : return rv;
230 : }
231 : }
232 622 : return 0;
233 : }
234 :
235 : /*
236 : ** Convert a long into its printable form
237 : */
238 : static int
239 622 : cvt_l(SprintfStateStr* aState, long aNum, int aWidth, int aPrec, int aRadix,
240 : int aType, int aFlags, const char16_t* aHexStr)
241 : {
242 : char16_t cvtbuf[100];
243 : char16_t* cvt;
244 : int digits;
245 :
246 : /* according to the man page this needs to happen */
247 622 : if ((aPrec == 0) && (aNum == 0)) {
248 0 : return 0;
249 : }
250 :
251 : /*
252 : ** Converting decimal is a little tricky. In the unsigned case we
253 : ** need to stop when we hit 10 digits. In the signed case, we can
254 : ** stop when the number is zero.
255 : */
256 622 : cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
257 622 : digits = 0;
258 2488 : while (aNum) {
259 933 : int digit = (((unsigned long)aNum) % aRadix) & 0xF;
260 933 : *--cvt = aHexStr[digit];
261 933 : digits++;
262 933 : aNum = (long)(((unsigned long)aNum) / aRadix);
263 : }
264 622 : if (digits == 0) {
265 0 : *--cvt = '0';
266 0 : digits++;
267 : }
268 :
269 : /*
270 : ** Now that we have the number converted without its sign, deal with
271 : ** the sign and zero padding.
272 : */
273 622 : return fill_n(aState, cvt, digits, aWidth, aPrec, aType, aFlags);
274 : }
275 :
276 : /*
277 : ** Convert a 64-bit integer into its printable form
278 : */
279 : static int
280 0 : cvt_ll(SprintfStateStr* aState, int64_t aNum, int aWidth, int aPrec, int aRadix,
281 : int aType, int aFlags, const char16_t* aHexStr)
282 : {
283 : char16_t cvtbuf[100];
284 : char16_t* cvt;
285 : int digits;
286 : int64_t rad;
287 :
288 : /* according to the man page this needs to happen */
289 0 : if (aPrec == 0 && aNum == 0) {
290 0 : return 0;
291 : }
292 :
293 : /*
294 : ** Converting decimal is a little tricky. In the unsigned case we
295 : ** need to stop when we hit 10 digits. In the signed case, we can
296 : ** stop when the number is zero.
297 : */
298 0 : rad = aRadix;
299 0 : cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
300 0 : digits = 0;
301 0 : while (aNum != 0) {
302 0 : *--cvt = aHexStr[int32_t(aNum % rad) & 0xf];
303 0 : digits++;
304 0 : aNum /= rad;
305 : }
306 0 : if (digits == 0) {
307 0 : *--cvt = '0';
308 0 : digits++;
309 : }
310 :
311 : /*
312 : ** Now that we have the number converted without its sign, deal with
313 : ** the sign and zero padding.
314 : */
315 0 : return fill_n(aState, cvt, digits, aWidth, aPrec, aType, aFlags);
316 : }
317 :
318 : /*
319 : ** Convert a double precision floating point number into its printable
320 : ** form.
321 : */
322 : static int
323 0 : cvt_f(SprintfStateStr* aState, double aDouble, int aWidth, int aPrec,
324 : const char16_t aType, int aFlags)
325 : {
326 0 : int mode = 2;
327 : int decpt;
328 : int sign;
329 : char buf[256];
330 0 : char* bufp = buf;
331 0 : int bufsz = 256;
332 : char num[256];
333 : char* nump;
334 : char* endnum;
335 0 : int numdigits = 0;
336 0 : char exp = 'e';
337 :
338 0 : if (aPrec == -1) {
339 0 : aPrec = 6;
340 0 : } else if (aPrec > 50) {
341 : // limit precision to avoid PR_dtoa bug 108335
342 : // and to prevent buffers overflows
343 0 : aPrec = 50;
344 : }
345 :
346 0 : switch (aType) {
347 : case 'f':
348 0 : numdigits = aPrec;
349 0 : mode = 3;
350 0 : break;
351 : case 'E':
352 0 : exp = 'E';
353 : MOZ_FALLTHROUGH;
354 : case 'e':
355 0 : numdigits = aPrec + 1;
356 0 : mode = 2;
357 0 : break;
358 : case 'G':
359 0 : exp = 'E';
360 : MOZ_FALLTHROUGH;
361 : case 'g':
362 0 : if (aPrec == 0) {
363 0 : aPrec = 1;
364 : }
365 0 : numdigits = aPrec;
366 0 : mode = 2;
367 0 : break;
368 : default:
369 0 : NS_ERROR("invalid aType passed to cvt_f");
370 : }
371 :
372 0 : if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign,
373 : &endnum, num, bufsz) == PR_FAILURE) {
374 0 : buf[0] = '\0';
375 0 : return -1;
376 : }
377 0 : numdigits = endnum - num;
378 0 : nump = num;
379 :
380 0 : if (sign) {
381 0 : *bufp++ = '-';
382 0 : } else if (aFlags & _SIGNED) {
383 0 : *bufp++ = '+';
384 : }
385 :
386 0 : if (decpt == 9999) {
387 0 : while ((*bufp++ = *nump++)) {
388 : }
389 : } else {
390 :
391 0 : switch (aType) {
392 :
393 : case 'E':
394 : case 'e':
395 :
396 0 : *bufp++ = *nump++;
397 0 : if (aPrec > 0) {
398 0 : *bufp++ = '.';
399 0 : while (*nump) {
400 0 : *bufp++ = *nump++;
401 0 : aPrec--;
402 : }
403 0 : while (aPrec-- > 0) {
404 0 : *bufp++ = '0';
405 : }
406 : }
407 0 : *bufp++ = exp;
408 :
409 0 : snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
410 0 : break;
411 :
412 : case 'f':
413 :
414 0 : if (decpt < 1) {
415 0 : *bufp++ = '0';
416 0 : if (aPrec > 0) {
417 0 : *bufp++ = '.';
418 0 : while (decpt++ && aPrec-- > 0) {
419 0 : *bufp++ = '0';
420 : }
421 0 : while (*nump && aPrec-- > 0) {
422 0 : *bufp++ = *nump++;
423 : }
424 0 : while (aPrec-- > 0) {
425 0 : *bufp++ = '0';
426 : }
427 : }
428 : } else {
429 0 : while (*nump && decpt-- > 0) {
430 0 : *bufp++ = *nump++;
431 : }
432 0 : while (decpt-- > 0) {
433 0 : *bufp++ = '0';
434 : }
435 0 : if (aPrec > 0) {
436 0 : *bufp++ = '.';
437 0 : while (*nump && aPrec-- > 0) {
438 0 : *bufp++ = *nump++;
439 : }
440 0 : while (aPrec-- > 0) {
441 0 : *bufp++ = '0';
442 : }
443 : }
444 : }
445 0 : *bufp = '\0';
446 0 : break;
447 :
448 : case 'G':
449 : case 'g':
450 :
451 0 : if ((decpt < -3) || ((decpt - 1) >= aPrec)) {
452 0 : *bufp++ = *nump++;
453 0 : numdigits--;
454 0 : if (numdigits > 0) {
455 0 : *bufp++ = '.';
456 0 : while (*nump) {
457 0 : *bufp++ = *nump++;
458 : }
459 : }
460 0 : *bufp++ = exp;
461 0 : snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
462 : } else {
463 0 : if (decpt < 1) {
464 0 : *bufp++ = '0';
465 0 : if (aPrec > 0) {
466 0 : *bufp++ = '.';
467 0 : while (decpt++) {
468 0 : *bufp++ = '0';
469 : }
470 0 : while (*nump) {
471 0 : *bufp++ = *nump++;
472 : }
473 : }
474 : } else {
475 0 : while (*nump && decpt-- > 0) {
476 0 : *bufp++ = *nump++;
477 0 : numdigits--;
478 : }
479 0 : while (decpt-- > 0) {
480 0 : *bufp++ = '0';
481 : }
482 0 : if (numdigits > 0) {
483 0 : *bufp++ = '.';
484 0 : while (*nump) {
485 0 : *bufp++ = *nump++;
486 : }
487 : }
488 : }
489 0 : *bufp = '\0';
490 : }
491 : }
492 : }
493 :
494 : char16_t rbuf[256];
495 0 : char16_t* rbufp = rbuf;
496 0 : bufp = buf;
497 : // cast to char16_t
498 0 : while ((*rbufp++ = *bufp++)) {
499 : }
500 0 : *rbufp = '\0';
501 :
502 0 : return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags);
503 : }
504 :
505 : /*
506 : ** Convert a string into its printable form. |aWidth| is the output
507 : ** width. |aPrec| is the maximum number of characters of |aStr| to output,
508 : ** where -1 means until NUL.
509 : */
510 : static int
511 79 : cvt_S(SprintfStateStr* aState, const char16_t* aStr, int aWidth, int aPrec,
512 : int aFlags)
513 : {
514 : int slen;
515 :
516 79 : if (aPrec == 0) {
517 0 : return 0;
518 : }
519 :
520 : /* Limit string length by precision value */
521 79 : slen = aStr ? NS_strlen(aStr) : 6;
522 79 : if (aPrec > 0) {
523 0 : if (aPrec < slen) {
524 0 : slen = aPrec;
525 : }
526 : }
527 :
528 : /* and away we go */
529 79 : return fill2(aState, aStr ? aStr : u"(null)", slen, aWidth, aFlags);
530 : }
531 :
532 : /*
533 : ** Convert a string into its printable form. |aWidth| is the output
534 : ** width. |aPrec| is the maximum number of characters of |aStr| to output,
535 : ** where -1 means until NUL.
536 : */
537 : static int
538 0 : cvt_s(SprintfStateStr* aState, const char* aStr, int aWidth, int aPrec, int aFlags)
539 : {
540 0 : NS_ConvertUTF8toUTF16 utf16Val(aStr);
541 0 : return cvt_S(aState, utf16Val.get(), aWidth, aPrec, aFlags);
542 : }
543 :
544 : /*
545 : ** BuildArgArray stands for Numbered Argument list Sprintf
546 : ** for example,
547 : ** fmp = "%4$i, %2$d, %3s, %1d";
548 : ** the number must start from 1, and no gap among them
549 : */
550 :
551 : static struct NumArgState*
552 390 : BuildArgArray(const char16_t* aFmt, va_list aAp, int* aRv,
553 : struct NumArgState* aNasArray)
554 : {
555 390 : int number = 0, cn = 0, i;
556 : const char16_t* p;
557 : char16_t c;
558 : struct NumArgState* nas;
559 :
560 : /*
561 : ** first pass:
562 : ** detemine how many legal % I have got, then allocate space
563 : */
564 390 : p = aFmt;
565 390 : *aRv = 0;
566 390 : i = 0;
567 8376 : while ((c = *p++) != 0) {
568 3993 : if (c != '%') {
569 3289 : continue;
570 : }
571 : /* skip %% case */
572 704 : if ((c = *p++) == '%') {
573 3 : continue;
574 : }
575 :
576 851 : while (c != 0) {
577 776 : if (c > '9' || c < '0') {
578 : /* numbered argument csae */
579 701 : if (c == '$') {
580 75 : if (i > 0) {
581 0 : *aRv = -1;
582 0 : return nullptr;
583 : }
584 75 : number++;
585 75 : break;
586 :
587 : } else {
588 : /* non-numbered argument case */
589 626 : if (number > 0) {
590 0 : *aRv = -1;
591 0 : return nullptr;
592 : }
593 626 : i = 1;
594 626 : break;
595 : }
596 : }
597 75 : c = *p++;
598 : }
599 : }
600 :
601 390 : if (number == 0) {
602 315 : return nullptr;
603 : }
604 :
605 75 : if (number > NAS_DEFAULT_NUM) {
606 0 : nas = (struct NumArgState*)moz_xmalloc(number * sizeof(struct NumArgState));
607 0 : if (!nas) {
608 0 : *aRv = -1;
609 0 : return nullptr;
610 : }
611 : } else {
612 75 : nas = aNasArray;
613 : }
614 :
615 150 : for (i = 0; i < number; i++) {
616 75 : nas[i].type = NumArgState::UNKNOWN;
617 : }
618 :
619 : /*
620 : ** second pass:
621 : ** set nas[].type
622 : */
623 75 : p = aFmt;
624 4745 : while ((c = *p++) != 0) {
625 2335 : if (c != '%') {
626 2260 : continue;
627 : }
628 75 : c = *p++;
629 75 : if (c == '%') {
630 0 : continue;
631 : }
632 75 : cn = 0;
633 : /* should improve error check later */
634 225 : while (c && c != '$') {
635 75 : cn = cn * 10 + c - '0';
636 75 : c = *p++;
637 : }
638 :
639 75 : if (!c || cn < 1 || cn > number) {
640 0 : *aRv = -1;
641 0 : break;
642 : }
643 :
644 : /* nas[cn] starts from 0, and make sure
645 : nas[cn].type is not assigned */
646 75 : cn--;
647 75 : if (nas[cn].type != NumArgState::UNKNOWN) {
648 0 : continue;
649 : }
650 :
651 75 : c = *p++;
652 :
653 : /* width */
654 75 : if (c == '*') {
655 : /* not supported feature, for the argument is not numbered */
656 0 : *aRv = -1;
657 0 : break;
658 : } else {
659 75 : while ((c >= '0') && (c <= '9')) {
660 0 : c = *p++;
661 : }
662 : }
663 :
664 : /* precision */
665 75 : if (c == '.') {
666 0 : c = *p++;
667 0 : if (c == '*') {
668 : /* not supported feature, for the argument is not numbered */
669 0 : *aRv = -1;
670 0 : break;
671 : } else {
672 0 : while ((c >= '0') && (c <= '9')) {
673 0 : c = *p++;
674 : }
675 : }
676 : }
677 :
678 : /* size */
679 75 : nas[cn].type = NumArgState::INTN;
680 75 : if (c == 'h') {
681 0 : nas[cn].type = NumArgState::INT16;
682 0 : c = *p++;
683 75 : } else if (c == 'L') {
684 : /* XXX not quite sure here */
685 0 : nas[cn].type = NumArgState::INT64;
686 0 : c = *p++;
687 75 : } else if (c == 'l') {
688 0 : nas[cn].type = NumArgState::INT32;
689 0 : c = *p++;
690 0 : if (c == 'l') {
691 0 : nas[cn].type = NumArgState::INT64;
692 0 : c = *p++;
693 : }
694 : }
695 :
696 : /* format */
697 75 : switch (c) {
698 : case 'd':
699 : case 'c':
700 : case 'i':
701 : case 'o':
702 : case 'u':
703 : case 'x':
704 : case 'X':
705 0 : break;
706 :
707 : case 'e':
708 : case 'f':
709 : case 'g':
710 0 : nas[cn].type = NumArgState::DOUBLE;
711 0 : break;
712 :
713 : case 'p':
714 : /* XXX should use cpp */
715 : if (sizeof(void*) == sizeof(int32_t)) {
716 : nas[cn].type = NumArgState::UINT32;
717 : } else if (sizeof(void*) == sizeof(int64_t)) {
718 0 : nas[cn].type = NumArgState::UINT64;
719 : } else if (sizeof(void*) == sizeof(int)) {
720 : nas[cn].type = NumArgState::UINTN;
721 : } else {
722 : nas[cn].type = NumArgState::UNKNOWN;
723 : }
724 0 : break;
725 :
726 : case 'C':
727 : /* XXX not supported I suppose */
728 0 : MOZ_ASSERT(0);
729 : nas[cn].type = NumArgState::UNKNOWN;
730 : break;
731 :
732 : case 'S':
733 75 : nas[cn].type = NumArgState::UNISTRING;
734 75 : break;
735 :
736 : case 's':
737 0 : nas[cn].type = NumArgState::STRING;
738 0 : break;
739 :
740 : case 'n':
741 0 : nas[cn].type = NumArgState::INTSTR;
742 0 : break;
743 :
744 : default:
745 0 : MOZ_ASSERT(0);
746 : nas[cn].type = NumArgState::UNKNOWN;
747 : break;
748 : }
749 :
750 : /* get a legal para. */
751 75 : if (nas[cn].type == NumArgState::UNKNOWN) {
752 0 : *aRv = -1;
753 0 : break;
754 : }
755 : }
756 :
757 :
758 : /*
759 : ** third pass
760 : ** fill the nas[cn].ap
761 : */
762 75 : if (*aRv < 0) {
763 0 : if (nas != aNasArray) {
764 0 : free(nas);
765 : }
766 0 : return nullptr;
767 : }
768 :
769 75 : cn = 0;
770 225 : while (cn < number) {
771 75 : if (nas[cn].type == NumArgState::UNKNOWN) {
772 0 : cn++;
773 0 : continue;
774 : }
775 :
776 75 : VARARGS_ASSIGN(nas[cn].ap, aAp);
777 :
778 75 : switch (nas[cn].type) {
779 : case NumArgState::INT16:
780 : case NumArgState::UINT16:
781 : case NumArgState::INTN:
782 0 : case NumArgState::UINTN: (void)va_arg(aAp, int); break;
783 :
784 0 : case NumArgState::INT32: (void)va_arg(aAp, int32_t); break;
785 :
786 0 : case NumArgState::UINT32: (void)va_arg(aAp, uint32_t); break;
787 :
788 0 : case NumArgState::INT64: (void)va_arg(aAp, int64_t); break;
789 :
790 0 : case NumArgState::UINT64: (void)va_arg(aAp, uint64_t); break;
791 :
792 0 : case NumArgState::STRING: (void)va_arg(aAp, char*); break;
793 :
794 0 : case NumArgState::INTSTR: (void)va_arg(aAp, int*); break;
795 :
796 0 : case NumArgState::DOUBLE: (void)va_arg(aAp, double); break;
797 :
798 75 : case NumArgState::UNISTRING: (void)va_arg(aAp, char16_t*); break;
799 :
800 : default:
801 0 : if (nas != aNasArray) {
802 0 : free(nas);
803 : }
804 0 : *aRv = -1;
805 0 : va_end(aAp);
806 0 : return nullptr;
807 : }
808 75 : cn++;
809 : }
810 75 : va_end(aAp);
811 75 : return nas;
812 : }
813 :
814 :
815 : /*
816 : ** The workhorse sprintf code.
817 : */
818 : static int
819 390 : dosprintf(SprintfStateStr* aState, const char16_t* aFmt, va_list aAp)
820 : {
821 : char16_t c;
822 : int flags, width, prec, radix, type;
823 : union
824 : {
825 : char16_t ch;
826 : int i;
827 : long l;
828 : int64_t ll;
829 : double d;
830 : const char* s;
831 : const char16_t* S;
832 : int* ip;
833 : } u;
834 390 : char16_t space = ' ';
835 :
836 780 : nsAutoString hex;
837 390 : hex.AssignLiteral("0123456789abcdef");
838 :
839 780 : nsAutoString HEX;
840 390 : HEX.AssignLiteral("0123456789ABCDEF");
841 :
842 : const char16_t* hexp;
843 : int rv, i;
844 390 : struct NumArgState* nas = nullptr;
845 : struct NumArgState nasArray[NAS_DEFAULT_NUM];
846 :
847 :
848 : /*
849 : ** build an argument array, IF the aFmt is numbered argument
850 : ** list style, to contain the Numbered Argument list pointers
851 : */
852 390 : nas = BuildArgArray(aFmt, aAp, &rv, nasArray);
853 390 : if (rv < 0) {
854 : /* the aFmt contains error Numbered Argument format, jliu@netscape.com */
855 0 : MOZ_ASSERT(0);
856 : return rv;
857 : }
858 :
859 6982 : while ((c = *aFmt++) != 0) {
860 3296 : if (c != '%') {
861 2592 : rv = (*aState->stuff)(aState, aFmt - 1, 1);
862 2592 : if (rv < 0) {
863 0 : va_end(aAp);
864 0 : FREE_IF_NECESSARY(nas);
865 0 : return rv;
866 : }
867 2592 : continue;
868 : }
869 :
870 : /*
871 : ** Gobble up the % format string. Hopefully we have handled all
872 : ** of the strange cases!
873 : */
874 704 : flags = 0;
875 704 : c = *aFmt++;
876 704 : if (c == '%') {
877 : /* quoting a % with %% */
878 3 : rv = (*aState->stuff)(aState, aFmt - 1, 1);
879 3 : if (rv < 0) {
880 0 : va_end(aAp);
881 0 : FREE_IF_NECESSARY(nas);
882 0 : return rv;
883 : }
884 3 : continue;
885 : }
886 :
887 701 : if (nas) {
888 : /* the aFmt contains the Numbered Arguments feature */
889 75 : i = 0;
890 : /* should improve error check later */
891 225 : while (c && c != '$') {
892 75 : i = (i * 10) + (c - '0');
893 75 : c = *aFmt++;
894 : }
895 :
896 75 : if (nas[i - 1].type == NumArgState::UNKNOWN) {
897 0 : if (nas != nasArray) {
898 0 : free(nas);
899 : }
900 0 : va_end(aAp);
901 0 : return -1;
902 : }
903 :
904 75 : VARARGS_ASSIGN(aAp, nas[i - 1].ap);
905 75 : c = *aFmt++;
906 : }
907 :
908 : /*
909 : * Examine optional flags. Note that we do not implement the
910 : * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
911 : * somewhat ambiguous and not ideal, which is perhaps why
912 : * the various sprintf() implementations are inconsistent
913 : * on this feature.
914 : */
915 701 : while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
916 0 : if (c == '-') {
917 0 : flags |= _LEFT;
918 : }
919 0 : if (c == '+') {
920 0 : flags |= _SIGNED;
921 : }
922 0 : if (c == ' ') {
923 0 : flags |= _SPACED;
924 : }
925 0 : if (c == '0') {
926 0 : flags |= _ZEROS;
927 : }
928 0 : c = *aFmt++;
929 : }
930 701 : if (flags & _SIGNED) {
931 0 : flags &= ~_SPACED;
932 : }
933 701 : if (flags & _LEFT) {
934 0 : flags &= ~_ZEROS;
935 : }
936 :
937 : /* width */
938 701 : if (c == '*') {
939 0 : c = *aFmt++;
940 0 : width = va_arg(aAp, int);
941 : } else {
942 701 : width = 0;
943 701 : while ((c >= '0') && (c <= '9')) {
944 0 : width = (width * 10) + (c - '0');
945 0 : c = *aFmt++;
946 : }
947 : }
948 :
949 : /* precision */
950 701 : prec = -1;
951 701 : if (c == '.') {
952 0 : c = *aFmt++;
953 0 : if (c == '*') {
954 0 : c = *aFmt++;
955 0 : prec = va_arg(aAp, int);
956 : } else {
957 0 : prec = 0;
958 0 : while ((c >= '0') && (c <= '9')) {
959 0 : prec = (prec * 10) + (c - '0');
960 0 : c = *aFmt++;
961 : }
962 : }
963 : }
964 :
965 : /* size */
966 701 : type = NumArgState::INTN;
967 701 : if (c == 'h') {
968 0 : type = NumArgState::INT16;
969 0 : c = *aFmt++;
970 701 : } else if (c == 'L') {
971 : /* XXX not quite sure here */
972 0 : type = NumArgState::INT64;
973 0 : c = *aFmt++;
974 701 : } else if (c == 'l') {
975 622 : type = NumArgState::INT32;
976 622 : c = *aFmt++;
977 622 : if (c == 'l') {
978 0 : type = NumArgState::INT64;
979 0 : c = *aFmt++;
980 : }
981 : }
982 :
983 : /* format */
984 701 : hexp = hex.get();
985 701 : switch (c) {
986 : case 'd':
987 : case 'i': /* decimal/integer */
988 622 : radix = 10;
989 622 : goto fetch_and_convert;
990 :
991 : case 'o': /* octal */
992 0 : radix = 8;
993 0 : type |= 1;
994 0 : goto fetch_and_convert;
995 :
996 : case 'u': /* unsigned decimal */
997 0 : radix = 10;
998 0 : type |= 1;
999 0 : goto fetch_and_convert;
1000 :
1001 : case 'x': /* unsigned hex */
1002 0 : radix = 16;
1003 0 : type |= 1;
1004 0 : goto fetch_and_convert;
1005 :
1006 : case 'X': /* unsigned HEX */
1007 0 : radix = 16;
1008 0 : hexp = HEX.get();
1009 0 : type |= 1;
1010 0 : goto fetch_and_convert;
1011 :
1012 : fetch_and_convert:
1013 622 : switch (type) {
1014 : case NumArgState::INT16:
1015 0 : u.l = va_arg(aAp, int);
1016 0 : if (u.l < 0) {
1017 0 : u.l = -u.l;
1018 0 : flags |= _NEG;
1019 : }
1020 0 : goto do_long;
1021 : case NumArgState::UINT16:
1022 0 : u.l = va_arg(aAp, int) & 0xffff;
1023 0 : goto do_long;
1024 : case NumArgState::INTN:
1025 0 : u.l = va_arg(aAp, int);
1026 0 : if (u.l < 0) {
1027 0 : u.l = -u.l;
1028 0 : flags |= _NEG;
1029 : }
1030 0 : goto do_long;
1031 : case NumArgState::UINTN:
1032 0 : u.l = (long)va_arg(aAp, unsigned int);
1033 0 : goto do_long;
1034 :
1035 : case NumArgState::INT32:
1036 622 : u.l = va_arg(aAp, int32_t);
1037 622 : if (u.l < 0) {
1038 0 : u.l = -u.l;
1039 0 : flags |= _NEG;
1040 : }
1041 622 : goto do_long;
1042 : case NumArgState::UINT32:
1043 0 : u.l = (long)va_arg(aAp, uint32_t);
1044 : do_long:
1045 622 : rv = cvt_l(aState, u.l, width, prec, radix, type, flags, hexp);
1046 622 : if (rv < 0) {
1047 0 : va_end(aAp);
1048 0 : FREE_IF_NECESSARY(nas);
1049 0 : return rv;
1050 : }
1051 622 : break;
1052 :
1053 : case NumArgState::INT64:
1054 0 : u.ll = va_arg(aAp, int64_t);
1055 0 : if (u.ll < 0) {
1056 0 : u.ll = -u.ll;
1057 0 : flags |= _NEG;
1058 : }
1059 0 : goto do_longlong;
1060 : case NumArgState::UINT64:
1061 0 : u.ll = va_arg(aAp, uint64_t);
1062 : do_longlong:
1063 0 : rv = cvt_ll(aState, u.ll, width, prec, radix, type, flags, hexp);
1064 0 : if (rv < 0) {
1065 0 : va_end(aAp);
1066 0 : FREE_IF_NECESSARY(nas);
1067 0 : return rv;
1068 : }
1069 0 : break;
1070 : }
1071 1323 : break;
1072 :
1073 : case 'e':
1074 : case 'E':
1075 : case 'f':
1076 : case 'g':
1077 : case 'G':
1078 0 : u.d = va_arg(aAp, double);
1079 0 : rv = cvt_f(aState, u.d, width, prec, c, flags);
1080 0 : if (rv < 0) {
1081 0 : return rv;
1082 : }
1083 0 : break;
1084 :
1085 : case 'c':
1086 0 : u.ch = va_arg(aAp, int);
1087 0 : if ((flags & _LEFT) == 0) {
1088 0 : while (width-- > 1) {
1089 0 : rv = (*aState->stuff)(aState, &space, 1);
1090 0 : if (rv < 0) {
1091 0 : va_end(aAp);
1092 0 : FREE_IF_NECESSARY(nas);
1093 0 : return rv;
1094 : }
1095 : }
1096 : }
1097 0 : rv = (*aState->stuff)(aState, &u.ch, 1);
1098 0 : if (rv < 0) {
1099 0 : va_end(aAp);
1100 0 : FREE_IF_NECESSARY(nas);
1101 0 : return rv;
1102 : }
1103 0 : if (flags & _LEFT) {
1104 0 : while (width-- > 1) {
1105 0 : rv = (*aState->stuff)(aState, &space, 1);
1106 0 : if (rv < 0) {
1107 0 : va_end(aAp);
1108 0 : FREE_IF_NECESSARY(nas);
1109 0 : return rv;
1110 : }
1111 : }
1112 : }
1113 0 : break;
1114 :
1115 : case 'p':
1116 : if (sizeof(void*) == sizeof(int32_t)) {
1117 : type = NumArgState::UINT32;
1118 : } else if (sizeof(void*) == sizeof(int64_t)) {
1119 0 : type = NumArgState::UINT64;
1120 : } else if (sizeof(void*) == sizeof(int)) {
1121 : type = NumArgState::UINTN;
1122 : } else {
1123 : MOZ_ASSERT(0);
1124 : break;
1125 : }
1126 0 : radix = 16;
1127 0 : goto fetch_and_convert;
1128 :
1129 : #if 0
1130 : case 'C':
1131 : /* XXX not supported I suppose */
1132 : MOZ_ASSERT(0);
1133 : break;
1134 : #endif
1135 :
1136 : case 'S':
1137 79 : u.S = va_arg(aAp, const char16_t*);
1138 79 : rv = cvt_S(aState, u.S, width, prec, flags);
1139 79 : if (rv < 0) {
1140 0 : va_end(aAp);
1141 0 : FREE_IF_NECESSARY(nas);
1142 0 : return rv;
1143 : }
1144 79 : break;
1145 :
1146 : case 's':
1147 0 : u.s = va_arg(aAp, const char*);
1148 0 : rv = cvt_s(aState, u.s, width, prec, flags);
1149 0 : if (rv < 0) {
1150 0 : va_end(aAp);
1151 0 : FREE_IF_NECESSARY(nas);
1152 0 : return rv;
1153 : }
1154 0 : break;
1155 :
1156 : case 'n':
1157 0 : u.ip = va_arg(aAp, int*);
1158 0 : if (u.ip) {
1159 0 : *u.ip = aState->cur - aState->base;
1160 : }
1161 0 : break;
1162 :
1163 : default:
1164 : /* Not a % token after all... skip it */
1165 : #if 0
1166 : MOZ_ASSERT(0);
1167 : #endif
1168 0 : char16_t perct = '%';
1169 0 : rv = (*aState->stuff)(aState, &perct, 1);
1170 0 : if (rv < 0) {
1171 0 : va_end(aAp);
1172 0 : FREE_IF_NECESSARY(nas);
1173 0 : return rv;
1174 : }
1175 0 : rv = (*aState->stuff)(aState, aFmt - 1, 1);
1176 0 : if (rv < 0) {
1177 0 : va_end(aAp);
1178 0 : FREE_IF_NECESSARY(nas);
1179 0 : return rv;
1180 : }
1181 : }
1182 : }
1183 :
1184 : /* Stuff trailing NUL */
1185 390 : char16_t null = '\0';
1186 :
1187 390 : rv = (*aState->stuff)(aState, &null, 1);
1188 :
1189 390 : va_end(aAp);
1190 390 : FREE_IF_NECESSARY(nas);
1191 :
1192 390 : return rv;
1193 : }
1194 :
1195 : /************************************************************************/
1196 :
1197 : static int
1198 1244 : StringStuff(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen)
1199 : {
1200 1244 : if (*aStr == '\0') {
1201 311 : return 0;
1202 : }
1203 :
1204 933 : ptrdiff_t off = aState->cur - aState->base;
1205 :
1206 933 : nsAString* str = static_cast<nsAString*>(aState->stuffclosure);
1207 933 : str->Append(aStr, aLen);
1208 :
1209 933 : aState->base = str->BeginWriting();
1210 933 : aState->cur = aState->base + off;
1211 :
1212 933 : return 0;
1213 : }
1214 :
1215 : /*
1216 : ** Stuff routine that automatically grows the malloc'd output buffer
1217 : ** before it overflows.
1218 : */
1219 : static int
1220 2442 : GrowStuff(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen)
1221 : {
1222 : ptrdiff_t off;
1223 : char16_t* newbase;
1224 : uint32_t newlen;
1225 :
1226 2442 : off = aState->cur - aState->base;
1227 2442 : if (off + aLen >= aState->maxlen) {
1228 : /* Grow the buffer */
1229 146 : newlen = aState->maxlen + ((aLen > 32) ? aLen : 32);
1230 146 : if (aState->base) {
1231 67 : newbase = (char16_t*)moz_xrealloc(aState->base,
1232 67 : newlen * sizeof(char16_t));
1233 : } else {
1234 79 : newbase = (char16_t*)moz_xmalloc(newlen * sizeof(char16_t));
1235 : }
1236 146 : if (!newbase) {
1237 : /* Ran out of memory */
1238 0 : return -1;
1239 : }
1240 146 : aState->base = newbase;
1241 146 : aState->maxlen = newlen;
1242 146 : aState->cur = aState->base + off;
1243 : }
1244 :
1245 : /* Copy data */
1246 7964 : while (aLen) {
1247 2761 : --aLen;
1248 2761 : *aState->cur++ = *aStr++;
1249 : }
1250 2442 : MOZ_ASSERT((uint32_t)(aState->cur - aState->base) <= aState->maxlen);
1251 2442 : return 0;
1252 : }
1253 :
1254 : /*
1255 : ** sprintf into a malloc'd buffer
1256 : */
1257 : char16_t*
1258 79 : nsTextFormatter::smprintf(const char16_t* aFmt, ...)
1259 : {
1260 : va_list ap;
1261 : char16_t* rv;
1262 :
1263 79 : va_start(ap, aFmt);
1264 79 : rv = nsTextFormatter::vsmprintf(aFmt, ap);
1265 79 : va_end(ap);
1266 79 : return rv;
1267 : }
1268 :
1269 : uint32_t
1270 311 : nsTextFormatter::ssprintf(nsAString& aOut, const char16_t* aFmt, ...)
1271 : {
1272 : va_list ap;
1273 : uint32_t rv;
1274 :
1275 311 : va_start(ap, aFmt);
1276 311 : rv = nsTextFormatter::vssprintf(aOut, aFmt, ap);
1277 311 : va_end(ap);
1278 311 : return rv;
1279 : }
1280 :
1281 : uint32_t
1282 311 : nsTextFormatter::vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp)
1283 : {
1284 : SprintfStateStr ss;
1285 311 : ss.stuff = StringStuff;
1286 311 : ss.base = 0;
1287 311 : ss.cur = 0;
1288 311 : ss.maxlen = 0;
1289 311 : ss.stuffclosure = &aOut;
1290 :
1291 311 : aOut.Truncate();
1292 311 : int n = dosprintf(&ss, aFmt, aAp);
1293 311 : return n ? n - 1 : n;
1294 : }
1295 :
1296 : char16_t*
1297 79 : nsTextFormatter::vsmprintf(const char16_t* aFmt, va_list aAp)
1298 : {
1299 : SprintfStateStr ss;
1300 : int rv;
1301 :
1302 79 : ss.stuff = GrowStuff;
1303 79 : ss.base = 0;
1304 79 : ss.cur = 0;
1305 79 : ss.maxlen = 0;
1306 79 : rv = dosprintf(&ss, aFmt, aAp);
1307 79 : if (rv < 0) {
1308 0 : if (ss.base) {
1309 0 : free(ss.base);
1310 : }
1311 0 : return 0;
1312 : }
1313 79 : return ss.base;
1314 : }
1315 :
1316 : /*
1317 : ** Stuff routine that discards overflow data
1318 : */
1319 : static int
1320 0 : LimitStuff(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen)
1321 : {
1322 0 : uint32_t limit = aState->maxlen - (aState->cur - aState->base);
1323 :
1324 0 : if (aLen > limit) {
1325 0 : aLen = limit;
1326 : }
1327 0 : while (aLen) {
1328 0 : --aLen;
1329 0 : *aState->cur++ = *aStr++;
1330 : }
1331 0 : return 0;
1332 : }
1333 :
1334 : /*
1335 : ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1336 : ** when finished.
1337 : */
1338 : uint32_t
1339 0 : nsTextFormatter::snprintf(char16_t* aOut, uint32_t aOutLen,
1340 : const char16_t* aFmt, ...)
1341 : {
1342 : va_list ap;
1343 : uint32_t rv;
1344 :
1345 0 : MOZ_ASSERT((int32_t)aOutLen > 0);
1346 0 : if ((int32_t)aOutLen <= 0) {
1347 0 : return 0;
1348 : }
1349 :
1350 0 : va_start(ap, aFmt);
1351 0 : rv = nsTextFormatter::vsnprintf(aOut, aOutLen, aFmt, ap);
1352 0 : va_end(ap);
1353 0 : return rv;
1354 : }
1355 :
1356 : uint32_t
1357 0 : nsTextFormatter::vsnprintf(char16_t* aOut, uint32_t aOutLen,
1358 : const char16_t* aFmt, va_list aAp)
1359 : {
1360 : SprintfStateStr ss;
1361 : uint32_t n;
1362 :
1363 0 : MOZ_ASSERT((int32_t)aOutLen > 0);
1364 0 : if ((int32_t)aOutLen <= 0) {
1365 0 : return 0;
1366 : }
1367 :
1368 0 : ss.stuff = LimitStuff;
1369 0 : ss.base = aOut;
1370 0 : ss.cur = aOut;
1371 0 : ss.maxlen = aOutLen;
1372 0 : (void) dosprintf(&ss, aFmt, aAp);
1373 :
1374 : /* If we added chars, and we didn't append a null, do it now. */
1375 0 : if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) {
1376 0 : *(--ss.cur) = '\0';
1377 : }
1378 :
1379 0 : n = ss.cur - ss.base;
1380 0 : return n ? n - 1 : n;
1381 : }
1382 :
1383 : /*
1384 : * Free memory allocated, for the caller, by smprintf
1385 : */
1386 : void
1387 79 : nsTextFormatter::smprintf_free(char16_t* aMem)
1388 : {
1389 79 : free(aMem);
1390 88 : }
1391 :
|