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