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