On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / glue / nsTextFormatter.cpp
blob923b1c40fed2913be5cd3d3ca246a3064f21147b
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
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/
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.
15 * The Original Code is mozilla.org Code.
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.
22 * Contributor(s):
23 * Prasad <prasad@medhas.org>
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.
37 * ***** END LICENSE BLOCK ***** */
39 /*
40 * Portable safe sprintf code.
42 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
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>
51 * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
52 * Changed to use nsMemory and Frozen linkage
53 * -- Prasad <prasad@medhas.org>
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"
70 ** Note: on some platforms va_list is defined as an array,
71 ** and requires array notation.
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
82 typedef struct SprintfStateStr SprintfState;
84 struct SprintfStateStr {
85 int (*stuff)(SprintfState *ss, const PRUnichar *sp, PRUint32 len);
87 PRUnichar *base;
88 PRUnichar *cur;
89 PRUint32 maxlen;
91 void *stuffclosure;
95 ** Numbered Arguement State
97 struct NumArgState{
98 int type; /* type of the current ap */
99 va_list ap; /* point to the corresponding position on ap */
102 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
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
118 #define _LEFT 0x1
119 #define _SIGNED 0x2
120 #define _SPACED 0x4
121 #define _ZEROS 0x8
122 #define _NEG 0x10
124 #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
127 ** Fill into the buffer using the data in src
129 static int fill2(SprintfState *ss, const PRUnichar *src, int srclen,
130 int width, int flags)
132 PRUnichar space = ' ';
133 int rv;
135 width -= srclen;
136 /* Right adjusting */
137 if ((width > 0) && ((flags & _LEFT) == 0)) {
138 if (flags & _ZEROS) {
139 space = '0';
141 while (--width >= 0) {
142 rv = (*ss->stuff)(ss, &space, 1);
143 if (rv < 0) {
144 return rv;
149 /* Copy out the source data */
150 rv = (*ss->stuff)(ss, src, srclen);
151 if (rv < 0) {
152 return rv;
155 /* Left adjusting */
156 if ((width > 0) && ((flags & _LEFT) != 0)) {
157 while (--width >= 0) {
158 rv = (*ss->stuff)(ss, &space, 1);
159 if (rv < 0) {
160 return rv;
164 return 0;
168 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
170 static int fill_n(SprintfState *ss, const PRUnichar *src, int srclen,
171 int width, int prec, int type, int flags)
173 int zerowidth = 0;
174 int precwidth = 0;
175 int signwidth = 0;
176 int leftspaces = 0;
177 int rightspaces = 0;
178 int cvtwidth;
179 int rv;
180 PRUnichar sign;
181 PRUnichar space = ' ';
182 PRUnichar zero = '0';
184 if ((type & 1) == 0) {
185 if (flags & _NEG) {
186 sign = '-';
187 signwidth = 1;
188 } else if (flags & _SIGNED) {
189 sign = '+';
190 signwidth = 1;
191 } else if (flags & _SPACED) {
192 sign = ' ';
193 signwidth = 1;
196 cvtwidth = signwidth + srclen;
198 if (prec > 0) {
199 if (prec > srclen) {
200 /* Need zero filling */
201 precwidth = prec - srclen;
202 cvtwidth += precwidth;
206 if ((flags & _ZEROS) && (prec < 0)) {
207 if (width > cvtwidth) {
208 /* Zero filling */
209 zerowidth = width - cvtwidth;
210 cvtwidth += zerowidth;
214 if (flags & _LEFT) {
215 if (width > cvtwidth) {
216 /* Space filling on the right (i.e. left adjusting) */
217 rightspaces = width - cvtwidth;
219 } else {
220 if (width > cvtwidth) {
221 /* Space filling on the left (i.e. right adjusting) */
222 leftspaces = width - cvtwidth;
225 while (--leftspaces >= 0) {
226 rv = (*ss->stuff)(ss, &space, 1);
227 if (rv < 0) {
228 return rv;
231 if (signwidth) {
232 rv = (*ss->stuff)(ss, &sign, 1);
233 if (rv < 0) {
234 return rv;
237 while (--precwidth >= 0) {
238 rv = (*ss->stuff)(ss, &space, 1);
239 if (rv < 0) {
240 return rv;
243 while (--zerowidth >= 0) {
244 rv = (*ss->stuff)(ss, &zero, 1);
245 if (rv < 0) {
246 return rv;
249 rv = (*ss->stuff)(ss, src, srclen);
250 if (rv < 0) {
251 return rv;
253 while (--rightspaces >= 0) {
254 rv = (*ss->stuff)(ss, &space, 1);
255 if (rv < 0) {
256 return rv;
259 return 0;
263 ** Convert a long into its printable form
265 static int cvt_l(SprintfState *ss, long num, int width, int prec,
266 int radix, int type, int flags, const PRUnichar *hexp)
268 PRUnichar cvtbuf[100];
269 PRUnichar *cvt;
270 int digits;
272 /* according to the man page this needs to happen */
273 if ((prec == 0) && (num == 0)) {
274 return 0;
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.
282 cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
283 digits = 0;
284 while (num) {
285 int digit = (((unsigned long)num) % radix) & 0xF;
286 *--cvt = hexp[digit];
287 digits++;
288 num = (long)(((unsigned long)num) / radix);
290 if (digits == 0) {
291 *--cvt = '0';
292 digits++;
296 ** Now that we have the number converted without its sign, deal with
297 ** the sign and zero padding.
299 return fill_n(ss, cvt, digits, width, prec, type, flags);
303 ** Convert a 64-bit integer into its printable form
305 static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec,
306 int radix, int type, int flags, const PRUnichar *hexp)
308 PRUnichar cvtbuf[100];
309 PRUnichar *cvt;
310 int digits;
311 PRInt64 rad;
313 /* according to the man page this needs to happen */
314 if ((prec == 0) && (LL_IS_ZERO(num))) {
315 return 0;
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.
323 LL_I2L(rad, radix);
324 cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
325 digits = 0;
326 while (!LL_IS_ZERO(num)) {
327 PRInt32 digit;
328 PRInt64 quot, rem;
329 LL_UDIVMOD(&quot, &rem, num, rad);
330 LL_L2I(digit, rem);
331 *--cvt = hexp[digit & 0xf];
332 digits++;
333 num = quot;
335 if (digits == 0) {
336 *--cvt = '0';
337 digits++;
341 ** Now that we have the number converted without its sign, deal with
342 ** the sign and zero padding.
344 return fill_n(ss, cvt, digits, width, prec, type, flags);
348 ** Convert a double precision floating point number into its printable
349 ** form.
351 static int cvt_f(SprintfState *ss, double d, int width, int prec,
352 const PRUnichar type, int flags)
354 int mode = 2;
355 int decpt;
356 int sign;
357 char buf[256];
358 char * bufp = buf;
359 int bufsz = 256;
360 char num[256];
361 char * nump;
362 char * endnum;
363 int numdigits = 0;
364 char exp = 'e';
366 if (prec == -1) {
367 prec = 6;
368 } else if (prec > 50) {
369 // limit precision to avoid PR_dtoa bug 108335
370 // and to prevent buffers overflows
371 prec = 50;
374 switch (type) {
375 case 'f':
376 numdigits = prec;
377 mode = 3;
378 break;
379 case 'E':
380 exp = 'E';
381 // no break
382 case 'e':
383 numdigits = prec + 1;
384 mode = 2;
385 break;
386 case 'G':
387 exp = 'E';
388 // no break
389 case 'g':
390 if (prec == 0) {
391 prec = 1;
393 numdigits = prec;
394 mode = 2;
395 break;
396 default:
397 NS_ERROR("invalid type passed to cvt_f");
400 if (PR_dtoa(d, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) {
401 buf[0] = '\0';
402 return -1;
404 numdigits = endnum - num;
405 nump = num;
407 if (sign) {
408 *bufp++ = '-';
409 } else if (flags & _SIGNED) {
410 *bufp++ = '+';
413 if (decpt == 9999) {
414 while ((*bufp++ = *nump++)) { }
415 } else {
417 switch (type) {
419 case 'E':
420 case 'e':
422 *bufp++ = *nump++;
423 if (prec > 0) {
424 *bufp++ = '.';
425 while (*nump) {
426 *bufp++ = *nump++;
427 prec--;
429 while (prec-- > 0) {
430 *bufp++ = '0';
433 *bufp++ = exp;
434 PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
435 break;
437 case 'f':
439 if (decpt < 1) {
440 *bufp++ = '0';
441 if (prec > 0) {
442 *bufp++ = '.';
443 while (decpt++ && prec-- > 0) {
444 *bufp++ = '0';
446 while (*nump && prec-- > 0) {
447 *bufp++ = *nump++;
449 while (prec-- > 0) {
450 *bufp++ = '0';
453 } else {
454 while (*nump && decpt-- > 0) {
455 *bufp++ = *nump++;
457 while (decpt-- > 0) {
458 *bufp++ = '0';
460 if (prec > 0) {
461 *bufp++ = '.';
462 while (*nump && prec-- > 0) {
463 *bufp++ = *nump++;
465 while (prec-- > 0) {
466 *bufp++ = '0';
470 *bufp = '\0';
471 break;
473 case 'G':
474 case 'g':
476 if ((decpt < -3) || ((decpt - 1) >= prec)) {
477 *bufp++ = *nump++;
478 numdigits--;
479 if (numdigits > 0) {
480 *bufp++ = '.';
481 while (*nump) {
482 *bufp++ = *nump++;
485 *bufp++ = exp;
486 PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1);
487 } else {
488 if (decpt < 1) {
489 *bufp++ = '0';
490 if (prec > 0) {
491 *bufp++ = '.';
492 while (decpt++) {
493 *bufp++ = '0';
495 while (*nump) {
496 *bufp++ = *nump++;
499 } else {
500 while (*nump && decpt-- > 0) {
501 *bufp++ = *nump++;
502 numdigits--;
504 while (decpt-- > 0) {
505 *bufp++ = '0';
507 if (numdigits > 0) {
508 *bufp++ = '.';
509 while (*nump) {
510 *bufp++ = *nump++;
514 *bufp = '\0';
519 PRUnichar rbuf[256];
520 PRUnichar *rbufp = rbuf;
521 bufp = buf;
522 // cast to PRUnichar
523 while ((*rbufp++ = *bufp++)) { }
524 *rbufp = '\0';
526 return fill2(ss, rbuf, NS_strlen(rbuf), width, flags);
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.
534 static int cvt_S(SprintfState *ss, const PRUnichar *s, int width,
535 int prec, int flags)
537 int slen;
539 if (prec == 0) {
540 return 0;
543 /* Limit string length by precision value */
544 slen = s ? NS_strlen(s) : 6;
545 if (prec > 0) {
546 if (prec < slen) {
547 slen = prec;
551 /* and away we go */
552 NS_NAMED_LITERAL_STRING(nullstr, "(null)");
554 return fill2(ss, s ? s : nullstr.get(), slen, width, flags);
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.
562 static int cvt_s(SprintfState *ss, const char *s, int width,
563 int prec, int flags)
565 NS_ConvertUTF8toUTF16 utf16Val(s);
566 return cvt_S(ss, utf16Val.get(), width, prec, flags);
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
576 static struct NumArgState* BuildArgArray(const PRUnichar *fmt,
577 va_list ap, int * rv,
578 struct NumArgState * nasArray)
580 int number = 0, cn = 0, i;
581 const PRUnichar* p;
582 PRUnichar c;
583 struct NumArgState* nas;
586 ** first pass:
587 ** detemine how many legal % I have got, then allocate space
589 p = fmt;
590 *rv = 0;
591 i = 0;
592 while ((c = *p++) != 0) {
593 if (c != '%') {
594 continue;
596 /* skip %% case */
597 if ((c = *p++) == '%') {
598 continue;
601 while( c != 0 ){
602 if (c > '9' || c < '0') {
603 /* numbered argument csae */
604 if (c == '$') {
605 if (i > 0) {
606 *rv = -1;
607 return NULL;
609 number++;
610 break;
612 } else {
613 /* non-numbered argument case */
614 if (number > 0) {
615 *rv = -1;
616 return NULL;
618 i = 1;
619 break;
622 c = *p++;
626 if (number == 0) {
627 return NULL;
630 if (number > NAS_DEFAULT_NUM) {
631 nas = (struct NumArgState*)nsMemory::Alloc(number * sizeof(struct NumArgState));
632 if (!nas) {
633 *rv = -1;
634 return NULL;
636 } else {
637 nas = nasArray;
640 for (i = 0; i < number; i++) {
641 nas[i].type = TYPE_UNKNOWN;
645 ** second pass:
646 ** set nas[].type
648 p = fmt;
649 while ((c = *p++) != 0) {
650 if (c != '%') {
651 continue;
653 c = *p++;
654 if (c == '%') {
655 continue;
657 cn = 0;
658 /* should imporve error check later */
659 while (c && c != '$') {
660 cn = cn*10 + c - '0';
661 c = *p++;
664 if (!c || cn < 1 || cn > number) {
665 *rv = -1;
666 break;
669 /* nas[cn] starts from 0, and make sure
670 nas[cn].type is not assigned */
671 cn--;
672 if (nas[cn].type != TYPE_UNKNOWN) {
673 continue;
676 c = *p++;
678 /* width */
679 if (c == '*') {
680 /* not supported feature, for the argument is not numbered */
681 *rv = -1;
682 break;
683 } else {
684 while ((c >= '0') && (c <= '9')) {
685 c = *p++;
689 /* precision */
690 if (c == '.') {
691 c = *p++;
692 if (c == '*') {
693 /* not supported feature, for the argument is not numbered */
694 *rv = -1;
695 break;
696 } else {
697 while ((c >= '0') && (c <= '9')) {
698 c = *p++;
703 /* size */
704 nas[cn].type = TYPE_INTN;
705 if (c == 'h') {
706 nas[cn].type = TYPE_INT16;
707 c = *p++;
708 } else if (c == 'L') {
709 /* XXX not quite sure here */
710 nas[cn].type = TYPE_INT64;
711 c = *p++;
712 } else if (c == 'l') {
713 nas[cn].type = TYPE_INT32;
714 c = *p++;
715 if (c == 'l') {
716 nas[cn].type = TYPE_INT64;
717 c = *p++;
721 /* format */
722 switch (c) {
723 case 'd':
724 case 'c':
725 case 'i':
726 case 'o':
727 case 'u':
728 case 'x':
729 case 'X':
730 break;
732 case 'e':
733 case 'f':
734 case 'g':
735 nas[cn].type = TYPE_DOUBLE;
736 break;
738 case 'p':
739 /* XXX should use cpp */
740 if (sizeof(void *) == sizeof(PRInt32)) {
741 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;
749 break;
751 case 'C':
752 /* XXX not supported I suppose */
753 PR_ASSERT(0);
754 nas[cn].type = TYPE_UNKNOWN;
755 break;
757 case 'S':
758 nas[cn].type = TYPE_UNISTRING;
759 break;
761 case 's':
762 nas[cn].type = TYPE_STRING;
763 break;
765 case 'n':
766 nas[cn].type = TYPE_INTSTR;
767 break;
769 default:
770 PR_ASSERT(0);
771 nas[cn].type = TYPE_UNKNOWN;
772 break;
775 /* get a legal para. */
776 if (nas[cn].type == TYPE_UNKNOWN) {
777 *rv = -1;
778 break;
784 ** third pass
785 ** fill the nas[cn].ap
787 if (*rv < 0) {
788 if( nas != nasArray ) {
789 PR_DELETE(nas);
791 return NULL;
794 cn = 0;
795 while (cn < number) {
796 if (nas[cn].type == TYPE_UNKNOWN) {
797 cn++;
798 continue;
801 VARARGS_ASSIGN(nas[cn].ap, ap);
803 switch (nas[cn].type) {
804 case TYPE_INT16:
805 case TYPE_UINT16:
806 case TYPE_INTN:
807 case TYPE_UINTN: (void)va_arg(ap, PRIntn); break;
809 case TYPE_INT32: (void)va_arg(ap, PRInt32); break;
811 case TYPE_UINT32: (void)va_arg(ap, PRUint32); break;
813 case TYPE_INT64: (void)va_arg(ap, PRInt64); break;
815 case TYPE_UINT64: (void)va_arg(ap, PRUint64); break;
817 case TYPE_STRING: (void)va_arg(ap, char*); break;
819 case TYPE_INTSTR: (void)va_arg(ap, PRIntn*); break;
821 case TYPE_DOUBLE: (void)va_arg(ap, double); break;
823 case TYPE_UNISTRING: (void)va_arg(ap, PRUnichar*); break;
825 default:
826 if( nas != nasArray ) {
827 PR_DELETE( nas );
829 *rv = -1;
830 return NULL;
832 cn++;
834 return nas;
838 ** The workhorse sprintf code.
840 static int dosprintf(SprintfState *ss, const PRUnichar *fmt, va_list ap)
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 PRUnichar space = ' ';
855 const PRUnichar *fmt0;
857 nsAutoString hex;
858 hex.AssignLiteral("0123456789abcdef");
860 nsAutoString HEX;
861 HEX.AssignLiteral("0123456789ABCDEF");
863 const PRUnichar *hexp;
864 int rv, i;
865 struct NumArgState* nas = NULL;
866 struct NumArgState nasArray[NAS_DEFAULT_NUM];
867 /* in "%4$.2f" dolPt will point to . */
868 const PRUnichar* dolPt = NULL;
872 ** build an argument array, IF the fmt is numbered argument
873 ** list style, to contain the Numbered Argument list pointers
875 nas = BuildArgArray (fmt, ap, &rv, nasArray);
876 if (rv < 0) {
877 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
878 PR_ASSERT(0);
879 return rv;
882 while ((c = *fmt++) != 0) {
883 if (c != '%') {
884 rv = (*ss->stuff)(ss, fmt - 1, 1);
885 if (rv < 0) {
886 return rv;
888 continue;
890 fmt0 = fmt - 1;
893 ** Gobble up the % format string. Hopefully we have handled all
894 ** of the strange cases!
896 flags = 0;
897 c = *fmt++;
898 if (c == '%') {
899 /* quoting a % with %% */
900 rv = (*ss->stuff)(ss, fmt - 1, 1);
901 if (rv < 0) {
902 return rv;
904 continue;
907 if (nas != NULL) {
908 /* the fmt contains the Numbered Arguments feature */
909 i = 0;
910 /* should imporve error check later */
911 while (c && c != '$') {
912 i = (i * 10) + (c - '0');
913 c = *fmt++;
916 if (nas[i-1].type == TYPE_UNKNOWN) {
917 if (nas && (nas != nasArray)) {
918 PR_DELETE(nas);
920 return -1;
923 VARARGS_ASSIGN(ap, nas[i-1].ap);
924 dolPt = fmt;
925 c = *fmt++;
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.
935 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
936 if (c == '-') flags |= _LEFT;
937 if (c == '+') flags |= _SIGNED;
938 if (c == ' ') flags |= _SPACED;
939 if (c == '0') flags |= _ZEROS;
940 c = *fmt++;
942 if (flags & _SIGNED) flags &= ~_SPACED;
943 if (flags & _LEFT) flags &= ~_ZEROS;
945 /* width */
946 if (c == '*') {
947 c = *fmt++;
948 width = va_arg(ap, int);
949 } else {
950 width = 0;
951 while ((c >= '0') && (c <= '9')) {
952 width = (width * 10) + (c - '0');
953 c = *fmt++;
957 /* precision */
958 prec = -1;
959 if (c == '.') {
960 c = *fmt++;
961 if (c == '*') {
962 c = *fmt++;
963 prec = va_arg(ap, int);
964 } else {
965 prec = 0;
966 while ((c >= '0') && (c <= '9')) {
967 prec = (prec * 10) + (c - '0');
968 c = *fmt++;
973 /* size */
974 type = TYPE_INTN;
975 if (c == 'h') {
976 type = TYPE_INT16;
977 c = *fmt++;
978 } else if (c == 'L') {
979 /* XXX not quite sure here */
980 type = TYPE_INT64;
981 c = *fmt++;
982 } else if (c == 'l') {
983 type = TYPE_INT32;
984 c = *fmt++;
985 if (c == 'l') {
986 type = TYPE_INT64;
987 c = *fmt++;
991 /* format */
992 hexp = hex.get();
993 switch (c) {
994 case 'd':
995 case 'i': /* decimal/integer */
996 radix = 10;
997 goto fetch_and_convert;
999 case 'o': /* octal */
1000 radix = 8;
1001 type |= 1;
1002 goto fetch_and_convert;
1004 case 'u': /* unsigned decimal */
1005 radix = 10;
1006 type |= 1;
1007 goto fetch_and_convert;
1009 case 'x': /* unsigned hex */
1010 radix = 16;
1011 type |= 1;
1012 goto fetch_and_convert;
1014 case 'X': /* unsigned HEX */
1015 radix = 16;
1016 hexp = HEX.get();
1017 type |= 1;
1018 goto fetch_and_convert;
1020 fetch_and_convert:
1021 switch (type) {
1022 case TYPE_INT16:
1023 u.l = va_arg(ap, int);
1024 if (u.l < 0) {
1025 u.l = -u.l;
1026 flags |= _NEG;
1028 goto do_long;
1029 case TYPE_UINT16:
1030 u.l = va_arg(ap, int) & 0xffff;
1031 goto do_long;
1032 case TYPE_INTN:
1033 u.l = va_arg(ap, int);
1034 if (u.l < 0) {
1035 u.l = -u.l;
1036 flags |= _NEG;
1038 goto do_long;
1039 case TYPE_UINTN:
1040 u.l = (long)va_arg(ap, unsigned int);
1041 goto do_long;
1043 case TYPE_INT32:
1044 u.l = va_arg(ap, PRInt32);
1045 if (u.l < 0) {
1046 u.l = -u.l;
1047 flags |= _NEG;
1049 goto do_long;
1050 case TYPE_UINT32:
1051 u.l = (long)va_arg(ap, PRUint32);
1052 do_long:
1053 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
1054 if (rv < 0) {
1055 return rv;
1057 break;
1059 case TYPE_INT64:
1060 u.ll = va_arg(ap, PRInt64);
1061 if (!LL_GE_ZERO(u.ll)) {
1062 LL_NEG(u.ll, u.ll);
1063 flags |= _NEG;
1065 goto do_longlong;
1066 case TYPE_UINT64:
1067 u.ll = va_arg(ap, PRUint64);
1068 do_longlong:
1069 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
1070 if (rv < 0) {
1071 return rv;
1073 break;
1075 break;
1077 case 'e':
1078 case 'E':
1079 case 'f':
1080 case 'g':
1081 case 'G':
1082 u.d = va_arg(ap, double);
1083 rv = cvt_f(ss, u.d, width, prec, c, flags);
1084 if (rv < 0) {
1085 return rv;
1087 break;
1089 case 'c':
1090 u.ch = va_arg(ap, int);
1091 if ((flags & _LEFT) == 0) {
1092 while (width-- > 1) {
1093 rv = (*ss->stuff)(ss, &space, 1);
1094 if (rv < 0) {
1095 return rv;
1099 rv = (*ss->stuff)(ss, &u.ch, 1);
1100 if (rv < 0) {
1101 return rv;
1103 if (flags & _LEFT) {
1104 while (width-- > 1) {
1105 rv = (*ss->stuff)(ss, &space, 1);
1106 if (rv < 0) {
1107 return rv;
1111 break;
1113 case 'p':
1114 if (sizeof(void *) == sizeof(PRInt32)) {
1115 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;
1124 radix = 16;
1125 goto fetch_and_convert;
1127 #if 0
1128 case 'C':
1129 /* XXX not supported I suppose */
1130 PR_ASSERT(0);
1131 break;
1132 #endif
1134 case 'S':
1135 u.S = va_arg(ap, const PRUnichar*);
1136 rv = cvt_S(ss, u.S, width, prec, flags);
1137 if (rv < 0) {
1138 return rv;
1140 break;
1142 case 's':
1143 u.s = va_arg(ap, const char*);
1144 rv = cvt_s(ss, u.s, width, prec, flags);
1145 if (rv < 0) {
1146 return rv;
1148 break;
1150 case 'n':
1151 u.ip = va_arg(ap, int*);
1152 if (u.ip) {
1153 *u.ip = ss->cur - ss->base;
1155 break;
1157 default:
1158 /* Not a % token after all... skip it */
1159 #if 0
1160 PR_ASSERT(0);
1161 #endif
1162 PRUnichar perct = '%';
1163 rv = (*ss->stuff)(ss, &perct, 1);
1164 if (rv < 0) {
1165 return rv;
1167 rv = (*ss->stuff)(ss, fmt - 1, 1);
1168 if (rv < 0) {
1169 return rv;
1174 /* Stuff trailing NUL */
1175 PRUnichar null = '\0';
1177 rv = (*ss->stuff)(ss, &null, 1);
1179 if( nas && ( nas != nasArray ) ){
1180 PR_DELETE( nas );
1183 return rv;
1186 /************************************************************************/
1188 static int
1189 StringStuff(SprintfState* ss, const PRUnichar* sp, PRUint32 len)
1191 ptrdiff_t off = ss->cur - ss->base;
1193 nsAString* str = static_cast<nsAString*>(ss->stuffclosure);
1194 str->Append(sp, len);
1196 ss->base = str->BeginWriting();
1197 ss->cur = ss->base + off;
1199 return 0;
1203 ** Stuff routine that automatically grows the malloc'd output buffer
1204 ** before it overflows.
1206 static int GrowStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
1208 ptrdiff_t off;
1209 PRUnichar *newbase;
1210 PRUint32 newlen;
1212 off = ss->cur - ss->base;
1213 if (off + len >= ss->maxlen) {
1214 /* Grow the buffer */
1215 newlen = ss->maxlen + ((len > 32) ? len : 32);
1216 if (ss->base) {
1217 newbase = (PRUnichar*) nsMemory::Realloc(ss->base, newlen*sizeof(PRUnichar));
1218 } else {
1219 newbase = (PRUnichar*) nsMemory::Alloc(newlen*sizeof(PRUnichar));
1221 if (!newbase) {
1222 /* Ran out of memory */
1223 return -1;
1225 ss->base = newbase;
1226 ss->maxlen = newlen;
1227 ss->cur = ss->base + off;
1230 /* Copy data */
1231 while (len) {
1232 --len;
1233 *ss->cur++ = *sp++;
1235 PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
1236 return 0;
1240 ** sprintf into a malloc'd buffer
1242 PRUnichar * nsTextFormatter::smprintf(const PRUnichar *fmt, ...)
1244 va_list ap;
1245 PRUnichar *rv;
1247 va_start(ap, fmt);
1248 rv = nsTextFormatter::vsmprintf(fmt, ap);
1249 va_end(ap);
1250 return rv;
1253 PRUint32 nsTextFormatter::ssprintf(nsAString& out, const PRUnichar* fmt, ...)
1255 va_list ap;
1256 PRUint32 rv;
1258 va_start(ap, fmt);
1259 rv = nsTextFormatter::vssprintf(out, fmt, ap);
1260 va_end(ap);
1261 return rv;
1264 PRUint32 nsTextFormatter::vssprintf(nsAString& out, const PRUnichar* fmt, va_list ap)
1266 SprintfState ss;
1267 ss.stuff = StringStuff;
1268 ss.base = 0;
1269 ss.cur = 0;
1270 ss.maxlen = 0;
1271 ss.stuffclosure = &out;
1273 out.Truncate();
1274 int n = dosprintf(&ss, fmt, ap);
1275 return n ? n - 1 : n;
1278 PRUnichar * nsTextFormatter::vsmprintf(const PRUnichar *fmt, va_list ap)
1280 SprintfState ss;
1281 int rv;
1283 ss.stuff = GrowStuff;
1284 ss.base = 0;
1285 ss.cur = 0;
1286 ss.maxlen = 0;
1287 rv = dosprintf(&ss, fmt, ap);
1288 if (rv < 0) {
1289 if (ss.base) {
1290 PR_DELETE(ss.base);
1292 return 0;
1294 return ss.base;
1298 ** Stuff routine that discards overflow data
1300 static int LimitStuff(SprintfState *ss, const PRUnichar *sp, PRUint32 len)
1302 PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
1304 if (len > limit) {
1305 len = limit;
1307 while (len) {
1308 --len;
1309 *ss->cur++ = *sp++;
1311 return 0;
1315 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1316 ** when finished.
1318 PRUint32 nsTextFormatter::snprintf(PRUnichar *out, PRUint32 outlen, const PRUnichar *fmt, ...)
1320 va_list ap;
1321 PRUint32 rv;
1323 PR_ASSERT((PRInt32)outlen > 0);
1324 if ((PRInt32)outlen <= 0) {
1325 return 0;
1328 va_start(ap, fmt);
1329 rv = nsTextFormatter::vsnprintf(out, outlen, fmt, ap);
1330 va_end(ap);
1331 return rv;
1334 PRUint32 nsTextFormatter::vsnprintf(PRUnichar *out, PRUint32 outlen,const PRUnichar *fmt,
1335 va_list ap)
1337 SprintfState ss;
1338 PRUint32 n;
1340 PR_ASSERT((PRInt32)outlen > 0);
1341 if ((PRInt32)outlen <= 0) {
1342 return 0;
1345 ss.stuff = LimitStuff;
1346 ss.base = out;
1347 ss.cur = out;
1348 ss.maxlen = outlen;
1349 (void) dosprintf(&ss, fmt, ap);
1351 /* If we added chars, and we didn't append a null, do it now. */
1352 if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
1353 *(--ss.cur) = '\0';
1355 n = ss.cur - ss.base;
1356 return n ? n - 1 : n;
1360 * Free memory allocated, for the caller, by smprintf
1362 void nsTextFormatter::smprintf_free(PRUnichar *mem)
1364 nsMemory::Free(mem);