Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / nsprpub / pr / src / io / prprf.c
bloba9bcc6b19386c8fec5e6629b0a01d969107205a1
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/. */
6 /*
7 ** Portable safe sprintf code.
8 **
9 ** Author: Kipp E.B. Hickman
11 #include <stdarg.h>
12 #include <stddef.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include "primpl.h"
16 #include "prprf.h"
17 #include "prlong.h"
18 #include "prlog.h"
19 #include "prmem.h"
21 #if defined(_MSC_VER) && _MSC_VER < 1900
22 # define snprintf _snprintf
23 #endif
26 ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
30 ** XXX This needs to be internationalized!
33 typedef struct SprintfStateStr SprintfState;
35 struct SprintfStateStr {
36 int (*stuff)(SprintfState* ss, const char* sp, PRUint32 len);
38 char* base;
39 char* cur;
40 PRUint32 maxlen; /* Must not exceed PR_INT32_MAX. */
42 int (*func)(void* arg, const char* sp, PRUint32 len);
43 void* arg;
47 ** Numbered Argument
49 struct NumArg {
50 int type; /* type of the numbered argument */
51 union { /* the numbered argument */
52 int i;
53 unsigned int ui;
54 PRInt32 i32;
55 PRUint32 ui32;
56 PRInt64 ll;
57 PRUint64 ull;
58 double d;
59 const char* s;
60 int* ip;
61 #ifdef WIN32
62 const WCHAR* ws;
63 #endif
64 } u;
67 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
70 ** For numeric types, the signed versions must have even values,
71 ** and their corresponding unsigned versions must have the subsequent
72 ** odd value.
74 #define TYPE_INT16 0
75 #define TYPE_UINT16 1
76 #define TYPE_INTN 2
77 #define TYPE_UINTN 3
78 #define TYPE_INT32 4
79 #define TYPE_UINT32 5
80 #define TYPE_INT64 6
81 #define TYPE_UINT64 7
82 #define TYPE_STRING 8
83 #define TYPE_DOUBLE 9
84 #define TYPE_INTSTR 10
85 #ifdef WIN32
86 # define TYPE_WSTRING 11
87 #endif
88 #define TYPE_UNKNOWN 20
90 #define FLAG_LEFT 0x1
91 #define FLAG_SIGNED 0x2
92 #define FLAG_SPACED 0x4
93 #define FLAG_ZEROS 0x8
94 #define FLAG_NEG 0x10
97 ** Fill into the buffer using the data in src
99 static int fill2(SprintfState* ss, const char* src, int srclen, int width,
100 int flags) {
101 char space = ' ';
102 int rv;
104 width -= srclen;
105 if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
106 if (flags & FLAG_ZEROS) {
107 space = '0';
109 while (--width >= 0) {
110 rv = (*ss->stuff)(ss, &space, 1);
111 if (rv < 0) {
112 return rv;
117 /* Copy out the source data */
118 rv = (*ss->stuff)(ss, src, srclen);
119 if (rv < 0) {
120 return rv;
123 if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
124 while (--width >= 0) {
125 rv = (*ss->stuff)(ss, &space, 1);
126 if (rv < 0) {
127 return rv;
131 return 0;
135 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
137 static int fill_n(SprintfState* ss, const char* src, int srclen, int width,
138 int prec, int type, int flags) {
139 int zerowidth = 0;
140 int precwidth = 0;
141 int signwidth = 0;
142 int leftspaces = 0;
143 int rightspaces = 0;
144 int cvtwidth;
145 int rv;
146 char sign;
148 if ((type & 1) == 0) {
149 if (flags & FLAG_NEG) {
150 sign = '-';
151 signwidth = 1;
152 } else if (flags & FLAG_SIGNED) {
153 sign = '+';
154 signwidth = 1;
155 } else if (flags & FLAG_SPACED) {
156 sign = ' ';
157 signwidth = 1;
160 cvtwidth = signwidth + srclen;
162 if (prec > 0) {
163 if (prec > srclen) {
164 precwidth = prec - srclen; /* Need zero filling */
165 cvtwidth += precwidth;
169 if ((flags & FLAG_ZEROS) && (prec < 0)) {
170 if (width > cvtwidth) {
171 zerowidth = width - cvtwidth; /* Zero filling */
172 cvtwidth += zerowidth;
176 if (flags & FLAG_LEFT) {
177 if (width > cvtwidth) {
178 /* Space filling on the right (i.e. left adjusting) */
179 rightspaces = width - cvtwidth;
181 } else {
182 if (width > cvtwidth) {
183 /* Space filling on the left (i.e. right adjusting) */
184 leftspaces = width - cvtwidth;
187 while (--leftspaces >= 0) {
188 rv = (*ss->stuff)(ss, " ", 1);
189 if (rv < 0) {
190 return rv;
193 if (signwidth) {
194 rv = (*ss->stuff)(ss, &sign, 1);
195 if (rv < 0) {
196 return rv;
199 while (--precwidth >= 0) {
200 rv = (*ss->stuff)(ss, "0", 1);
201 if (rv < 0) {
202 return rv;
205 while (--zerowidth >= 0) {
206 rv = (*ss->stuff)(ss, "0", 1);
207 if (rv < 0) {
208 return rv;
211 rv = (*ss->stuff)(ss, src, srclen);
212 if (rv < 0) {
213 return rv;
215 while (--rightspaces >= 0) {
216 rv = (*ss->stuff)(ss, " ", 1);
217 if (rv < 0) {
218 return rv;
221 return 0;
225 ** Convert a long into its printable form
227 static int cvt_l(SprintfState* ss, long num, int width, int prec, int radix,
228 int type, int flags, const char* hexp) {
229 char cvtbuf[100];
230 char* cvt;
231 int digits;
233 /* according to the man page this needs to happen */
234 if ((prec == 0) && (num == 0)) {
235 return 0;
239 ** Converting decimal is a little tricky. In the unsigned case we
240 ** need to stop when we hit 10 digits. In the signed case, we can
241 ** stop when the number is zero.
243 cvt = cvtbuf + sizeof(cvtbuf);
244 digits = 0;
245 while (num) {
246 int digit = (((unsigned long)num) % radix) & 0xF;
247 *--cvt = hexp[digit];
248 digits++;
249 num = (long)(((unsigned long)num) / radix);
251 if (digits == 0) {
252 *--cvt = '0';
253 digits++;
257 ** Now that we have the number converted without its sign, deal with
258 ** the sign and zero padding.
260 return fill_n(ss, cvt, digits, width, prec, type, flags);
264 ** Convert a 64-bit integer into its printable form
266 static int cvt_ll(SprintfState* ss, PRInt64 num, int width, int prec, int radix,
267 int type, int flags, const char* hexp) {
268 char cvtbuf[100];
269 char* cvt;
270 int digits;
271 PRInt64 rad;
273 /* according to the man page this needs to happen */
274 if ((prec == 0) && (LL_IS_ZERO(num))) {
275 return 0;
279 ** Converting decimal is a little tricky. In the unsigned case we
280 ** need to stop when we hit 10 digits. In the signed case, we can
281 ** stop when the number is zero.
283 LL_I2L(rad, radix);
284 cvt = cvtbuf + sizeof(cvtbuf);
285 digits = 0;
286 while (!LL_IS_ZERO(num)) {
287 PRInt32 digit;
288 PRInt64 quot, rem;
289 LL_UDIVMOD(&quot, &rem, num, rad);
290 LL_L2I(digit, rem);
291 *--cvt = hexp[digit & 0xf];
292 digits++;
293 num = quot;
295 if (digits == 0) {
296 *--cvt = '0';
297 digits++;
301 ** Now that we have the number converted without its sign, deal with
302 ** the sign and zero padding.
304 return fill_n(ss, cvt, digits, width, prec, type, flags);
308 ** Convert a double precision floating point number into its printable
309 ** form.
311 ** XXX stop using snprintf to convert floating point
313 static int cvt_f(SprintfState* ss, double d, const char* fmt0,
314 const char* fmt1) {
315 char fin[20];
316 char fout[300];
317 int amount = fmt1 - fmt0;
319 if (amount <= 0 || amount >= sizeof(fin)) {
320 /* Totally bogus % command to snprintf. Just ignore it */
321 return 0;
323 memcpy(fin, fmt0, amount);
324 fin[amount] = 0;
326 /* Convert floating point using the native snprintf code */
327 #ifdef DEBUG
329 const char* p = fin;
330 while (*p) {
331 PR_ASSERT(*p != 'L');
332 p++;
335 #endif
336 memset(fout, 0, sizeof(fout));
337 snprintf(fout, sizeof(fout), fin, d);
338 /* Explicitly null-terminate fout because on Windows snprintf doesn't
339 * append a null-terminator if the buffer is too small. */
340 fout[sizeof(fout) - 1] = '\0';
342 return (*ss->stuff)(ss, fout, strlen(fout));
346 ** Convert a string into its printable form. "width" is the output
347 ** width. "prec" is the maximum number of characters of "s" to output,
348 ** where -1 means until NUL.
350 static int cvt_s(SprintfState* ss, const char* str, int width, int prec,
351 int flags) {
352 int slen;
354 if (prec == 0) {
355 return 0;
358 /* Limit string length by precision value */
359 if (!str) {
360 str = "(null)";
362 if (prec > 0) {
363 /* this is: slen = strnlen(str, prec); */
364 register const char* s;
366 for (s = str; prec && *s; s++, prec--);
367 slen = s - str;
368 } else {
369 slen = strlen(str);
372 /* and away we go */
373 return fill2(ss, str, slen, width, flags);
377 ** BuildArgArray stands for Numbered Argument list Sprintf
378 ** for example,
379 ** fmt = "%4$i, %2$d, %3s, %1d";
380 ** the number must start from 1, and no gap among them
383 static struct NumArg* BuildArgArray(const char* fmt, va_list ap, int* rv,
384 struct NumArg* nasArray) {
385 int number = 0, cn = 0, i;
386 const char* p;
387 char c;
388 struct NumArg* nas;
391 ** first pass:
392 ** determine how many legal % I have got, then allocate space
395 p = fmt;
396 *rv = 0;
397 i = 0;
398 while ((c = *p++) != 0) {
399 if (c != '%') {
400 continue;
402 if ((c = *p++) == '%') { /* skip %% case */
403 continue;
406 while (c != 0) {
407 if (c > '9' || c < '0') {
408 if (c == '$') { /* numbered argument case */
409 if (i > 0) {
410 *rv = -1;
411 return NULL;
413 number++;
414 } else { /* non-numbered argument case */
415 if (number > 0) {
416 *rv = -1;
417 return NULL;
419 i = 1;
421 break;
424 c = *p++;
428 if (number == 0) {
429 return NULL;
432 if (number > NAS_DEFAULT_NUM) {
433 nas = (struct NumArg*)PR_MALLOC(number * sizeof(struct NumArg));
434 if (!nas) {
435 *rv = -1;
436 return NULL;
438 } else {
439 nas = nasArray;
442 for (i = 0; i < number; i++) {
443 nas[i].type = TYPE_UNKNOWN;
447 ** second pass:
448 ** set nas[].type
451 p = fmt;
452 while ((c = *p++) != 0) {
453 if (c != '%') {
454 continue;
456 c = *p++;
457 if (c == '%') {
458 continue;
461 cn = 0;
462 while (c && c != '$') { /* should improve error check later */
463 cn = cn * 10 + c - '0';
464 c = *p++;
467 if (!c || cn < 1 || cn > number) {
468 *rv = -1;
469 break;
472 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
473 cn--;
474 if (nas[cn].type != TYPE_UNKNOWN) {
475 continue;
478 c = *p++;
480 /* width */
481 if (c == '*') {
482 /* not supported feature, for the argument is not numbered */
483 *rv = -1;
484 break;
487 while ((c >= '0') && (c <= '9')) {
488 c = *p++;
491 /* precision */
492 if (c == '.') {
493 c = *p++;
494 if (c == '*') {
495 /* not supported feature, for the argument is not numbered */
496 *rv = -1;
497 break;
500 while ((c >= '0') && (c <= '9')) {
501 c = *p++;
505 /* size */
506 nas[cn].type = TYPE_INTN;
507 if (c == 'h') {
508 nas[cn].type = TYPE_INT16;
509 c = *p++;
510 } else if (c == 'L') {
511 /* XXX not quite sure here */
512 nas[cn].type = TYPE_INT64;
513 c = *p++;
514 } else if (c == 'l') {
515 nas[cn].type = TYPE_INT32;
516 c = *p++;
517 if (c == 'l') {
518 nas[cn].type = TYPE_INT64;
519 c = *p++;
521 } else if (c == 'z') {
522 if (sizeof(size_t) == sizeof(PRInt32)) {
523 nas[cn].type = TYPE_INT32;
524 } else if (sizeof(size_t) == sizeof(PRInt64)) {
525 nas[cn].type = TYPE_INT64;
526 } else {
527 nas[cn].type = TYPE_UNKNOWN;
529 c = *p++;
532 /* format */
533 switch (c) {
534 case 'd':
535 case 'c':
536 case 'i':
537 case 'o':
538 case 'u':
539 case 'x':
540 case 'X':
541 break;
543 case 'e':
544 case 'f':
545 case 'g':
546 nas[cn].type = TYPE_DOUBLE;
547 break;
549 case 'p':
550 /* XXX should use cpp */
551 if (sizeof(void*) == sizeof(PRInt32)) {
552 nas[cn].type = TYPE_UINT32;
553 } else if (sizeof(void*) == sizeof(PRInt64)) {
554 nas[cn].type = TYPE_UINT64;
555 } else if (sizeof(void*) == sizeof(PRIntn)) {
556 nas[cn].type = TYPE_UINTN;
557 } else {
558 nas[cn].type = TYPE_UNKNOWN;
560 break;
562 case 'S':
563 #ifdef WIN32
564 nas[cn].type = TYPE_WSTRING;
565 break;
566 #endif
567 case 'C':
568 case 'E':
569 case 'G':
570 /* XXX not supported I suppose */
571 PR_ASSERT(0);
572 nas[cn].type = TYPE_UNKNOWN;
573 break;
575 case 's':
576 nas[cn].type = TYPE_STRING;
577 break;
579 case 'n':
580 nas[cn].type = TYPE_INTSTR;
581 break;
583 default:
584 PR_ASSERT(0);
585 nas[cn].type = TYPE_UNKNOWN;
586 break;
589 /* get a legal para. */
590 if (nas[cn].type == TYPE_UNKNOWN) {
591 *rv = -1;
592 break;
597 ** third pass
598 ** fill the nas[cn].ap
601 if (*rv < 0) {
602 if (nas != nasArray) {
603 PR_DELETE(nas);
605 return NULL;
608 cn = 0;
609 while (cn < number) {
610 if (nas[cn].type == TYPE_UNKNOWN) {
611 cn++;
612 continue;
615 switch (nas[cn].type) {
616 case TYPE_INT16:
617 case TYPE_UINT16:
618 case TYPE_INTN:
619 nas[cn].u.i = va_arg(ap, int);
620 break;
622 case TYPE_UINTN:
623 nas[cn].u.ui = va_arg(ap, unsigned int);
624 break;
626 case TYPE_INT32:
627 nas[cn].u.i32 = va_arg(ap, PRInt32);
628 break;
630 case TYPE_UINT32:
631 nas[cn].u.ui32 = va_arg(ap, PRUint32);
632 break;
634 case TYPE_INT64:
635 nas[cn].u.ll = va_arg(ap, PRInt64);
636 break;
638 case TYPE_UINT64:
639 nas[cn].u.ull = va_arg(ap, PRUint64);
640 break;
642 case TYPE_STRING:
643 nas[cn].u.s = va_arg(ap, char*);
644 break;
646 #ifdef WIN32
647 case TYPE_WSTRING:
648 nas[cn].u.ws = va_arg(ap, WCHAR*);
649 break;
650 #endif
652 case TYPE_INTSTR:
653 nas[cn].u.ip = va_arg(ap, int*);
654 break;
656 case TYPE_DOUBLE:
657 nas[cn].u.d = va_arg(ap, double);
658 break;
660 default:
661 if (nas != nasArray) {
662 PR_DELETE(nas);
664 *rv = -1;
665 return NULL;
668 cn++;
671 return nas;
675 ** The workhorse sprintf code.
677 static int dosprintf(SprintfState* ss, const char* fmt, va_list ap) {
678 char c;
679 int flags, width, prec, radix, type;
680 union {
681 char ch;
682 int i;
683 long l;
684 PRInt64 ll;
685 double d;
686 const char* s;
687 int* ip;
688 #ifdef WIN32
689 const WCHAR* ws;
690 #endif
691 } u;
692 const char* fmt0;
693 static char* hex = "0123456789abcdef";
694 static char* HEX = "0123456789ABCDEF";
695 char* hexp;
696 int rv, i;
697 struct NumArg* nas = NULL;
698 struct NumArg* nap = NULL;
699 struct NumArg nasArray[NAS_DEFAULT_NUM];
700 char pattern[20];
701 const char* dolPt = NULL; /* in "%4$.2f", dolPt will point to . */
702 #ifdef WIN32
703 char* pBuf = NULL;
704 #endif
707 ** build an argument array, IF the fmt is numbered argument
708 ** list style, to contain the Numbered Argument list pointers
711 nas = BuildArgArray(fmt, ap, &rv, nasArray);
712 if (rv < 0) {
713 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
714 PR_ASSERT(0);
715 return rv;
718 while ((c = *fmt++) != 0) {
719 if (c != '%') {
720 rv = (*ss->stuff)(ss, fmt - 1, 1);
721 if (rv < 0) {
722 return rv;
724 continue;
726 fmt0 = fmt - 1;
729 ** Gobble up the % format string. Hopefully we have handled all
730 ** of the strange cases!
732 flags = 0;
733 c = *fmt++;
734 if (c == '%') {
735 /* quoting a % with %% */
736 rv = (*ss->stuff)(ss, fmt - 1, 1);
737 if (rv < 0) {
738 return rv;
740 continue;
743 if (nas != NULL) {
744 /* the fmt contains the Numbered Arguments feature */
745 i = 0;
746 while (c && c != '$') { /* should improve error check later */
747 i = (i * 10) + (c - '0');
748 c = *fmt++;
751 if (nas[i - 1].type == TYPE_UNKNOWN) {
752 if (nas && (nas != nasArray)) {
753 PR_DELETE(nas);
755 return -1;
758 nap = &nas[i - 1];
759 dolPt = fmt;
760 c = *fmt++;
764 * Examine optional flags. Note that we do not implement the
765 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
766 * somewhat ambiguous and not ideal, which is perhaps why
767 * the various sprintf() implementations are inconsistent
768 * on this feature.
770 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
771 if (c == '-') {
772 flags |= FLAG_LEFT;
774 if (c == '+') {
775 flags |= FLAG_SIGNED;
777 if (c == ' ') {
778 flags |= FLAG_SPACED;
780 if (c == '0') {
781 flags |= FLAG_ZEROS;
783 c = *fmt++;
785 if (flags & FLAG_SIGNED) {
786 flags &= ~FLAG_SPACED;
788 if (flags & FLAG_LEFT) {
789 flags &= ~FLAG_ZEROS;
792 /* width */
793 if (c == '*') {
794 c = *fmt++;
795 width = va_arg(ap, int);
796 } else {
797 width = 0;
798 while ((c >= '0') && (c <= '9')) {
799 width = (width * 10) + (c - '0');
800 c = *fmt++;
804 /* precision */
805 prec = -1;
806 if (c == '.') {
807 c = *fmt++;
808 if (c == '*') {
809 c = *fmt++;
810 prec = va_arg(ap, int);
811 } else {
812 prec = 0;
813 while ((c >= '0') && (c <= '9')) {
814 prec = (prec * 10) + (c - '0');
815 c = *fmt++;
820 /* size */
821 type = TYPE_INTN;
822 if (c == 'h') {
823 type = TYPE_INT16;
824 c = *fmt++;
825 } else if (c == 'L') {
826 /* XXX not quite sure here */
827 type = TYPE_INT64;
828 c = *fmt++;
829 } else if (c == 'l') {
830 type = TYPE_INT32;
831 c = *fmt++;
832 if (c == 'l') {
833 type = TYPE_INT64;
834 c = *fmt++;
836 } else if (c == 'z') {
837 if (sizeof(size_t) == sizeof(PRInt32)) {
838 type = TYPE_INT32;
839 } else if (sizeof(size_t) == sizeof(PRInt64)) {
840 type = TYPE_INT64;
842 c = *fmt++;
845 /* format */
846 hexp = hex;
847 switch (c) {
848 case 'd':
849 case 'i': /* decimal/integer */
850 radix = 10;
851 goto fetch_and_convert;
853 case 'o': /* octal */
854 radix = 8;
855 type |= 1;
856 goto fetch_and_convert;
858 case 'u': /* unsigned decimal */
859 radix = 10;
860 type |= 1;
861 goto fetch_and_convert;
863 case 'x': /* unsigned hex */
864 radix = 16;
865 type |= 1;
866 goto fetch_and_convert;
868 case 'X': /* unsigned HEX */
869 radix = 16;
870 hexp = HEX;
871 type |= 1;
872 goto fetch_and_convert;
874 fetch_and_convert:
875 switch (type) {
876 case TYPE_INT16:
877 u.l = nas ? nap->u.i : va_arg(ap, int);
878 if (u.l < 0) {
879 u.l = -u.l;
880 flags |= FLAG_NEG;
882 goto do_long;
883 case TYPE_UINT16:
884 u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff;
885 goto do_long;
886 case TYPE_INTN:
887 u.l = nas ? nap->u.i : va_arg(ap, int);
888 if (u.l < 0) {
889 u.l = -u.l;
890 flags |= FLAG_NEG;
892 goto do_long;
893 case TYPE_UINTN:
894 u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int));
895 goto do_long;
897 case TYPE_INT32:
898 u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32);
899 if (u.l < 0) {
900 u.l = -u.l;
901 flags |= FLAG_NEG;
903 goto do_long;
904 case TYPE_UINT32:
905 u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32));
906 do_long:
907 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
908 if (rv < 0) {
909 return rv;
911 break;
913 case TYPE_INT64:
914 u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64);
915 if (!LL_GE_ZERO(u.ll)) {
916 LL_NEG(u.ll, u.ll);
917 flags |= FLAG_NEG;
919 goto do_longlong;
920 case TYPE_UINT64:
921 u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64);
922 do_longlong:
923 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
924 if (rv < 0) {
925 return rv;
927 break;
929 break;
931 case 'e':
932 case 'E':
933 case 'f':
934 case 'g':
935 u.d = nas ? nap->u.d : va_arg(ap, double);
936 if (nas != NULL) {
937 i = fmt - dolPt;
938 if (i < sizeof(pattern)) {
939 pattern[0] = '%';
940 memcpy(&pattern[1], dolPt, i);
941 rv = cvt_f(ss, u.d, pattern, &pattern[i + 1]);
943 } else {
944 rv = cvt_f(ss, u.d, fmt0, fmt);
947 if (rv < 0) {
948 return rv;
950 break;
952 case 'c':
953 u.ch = nas ? nap->u.i : va_arg(ap, int);
954 if ((flags & FLAG_LEFT) == 0) {
955 while (width-- > 1) {
956 rv = (*ss->stuff)(ss, " ", 1);
957 if (rv < 0) {
958 return rv;
962 rv = (*ss->stuff)(ss, &u.ch, 1);
963 if (rv < 0) {
964 return rv;
966 if (flags & FLAG_LEFT) {
967 while (width-- > 1) {
968 rv = (*ss->stuff)(ss, " ", 1);
969 if (rv < 0) {
970 return rv;
974 break;
976 case 'p':
977 if (sizeof(void*) == sizeof(PRInt32)) {
978 type = TYPE_UINT32;
979 } else if (sizeof(void*) == sizeof(PRInt64)) {
980 type = TYPE_UINT64;
981 } else if (sizeof(void*) == sizeof(int)) {
982 type = TYPE_UINTN;
983 } else {
984 PR_ASSERT(0);
985 break;
987 radix = 16;
988 goto fetch_and_convert;
990 #ifndef WIN32
991 case 'S':
992 /* XXX not supported I suppose */
993 PR_ASSERT(0);
994 break;
995 #endif
997 #if 0
998 case 'C':
999 case 'E':
1000 case 'G':
1001 /* XXX not supported I suppose */
1002 PR_ASSERT(0);
1003 break;
1004 #endif
1006 #ifdef WIN32
1007 case 'S':
1008 u.ws = nas ? nap->u.ws : va_arg(ap, const WCHAR*);
1010 /* Get the required size in rv */
1011 rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL);
1012 if (rv == 0) {
1013 rv = 1;
1015 pBuf = PR_MALLOC(rv);
1016 WideCharToMultiByte(CP_ACP, 0, u.ws, -1, pBuf, (int)rv, NULL, NULL);
1017 pBuf[rv - 1] = '\0';
1019 rv = cvt_s(ss, pBuf, width, prec, flags);
1021 /* We don't need the allocated buffer anymore */
1022 PR_Free(pBuf);
1023 if (rv < 0) {
1024 return rv;
1026 break;
1028 #endif
1030 case 's':
1031 u.s = nas ? nap->u.s : va_arg(ap, const char*);
1032 rv = cvt_s(ss, u.s, width, prec, flags);
1033 if (rv < 0) {
1034 return rv;
1036 break;
1038 case 'n':
1039 u.ip = nas ? nap->u.ip : va_arg(ap, int*);
1040 if (u.ip) {
1041 *u.ip = ss->cur - ss->base;
1043 break;
1045 default:
1046 /* Not a % token after all... skip it */
1047 #if 0
1048 PR_ASSERT(0);
1049 #endif
1050 rv = (*ss->stuff)(ss, "%", 1);
1051 if (rv < 0) {
1052 return rv;
1054 rv = (*ss->stuff)(ss, fmt - 1, 1);
1055 if (rv < 0) {
1056 return rv;
1061 /* Stuff trailing NUL */
1062 rv = (*ss->stuff)(ss, "\0", 1);
1064 if (nas && (nas != nasArray)) {
1065 PR_DELETE(nas);
1068 return rv;
1071 /************************************************************************/
1073 static int FuncStuff(SprintfState* ss, const char* sp, PRUint32 len) {
1074 int rv;
1077 ** We will add len to ss->maxlen at the end of the function. First check
1078 ** if ss->maxlen + len would overflow or be greater than PR_INT32_MAX.
1080 if (PR_UINT32_MAX - ss->maxlen < len || ss->maxlen + len > PR_INT32_MAX) {
1081 return -1;
1083 rv = (*ss->func)(ss->arg, sp, len);
1084 if (rv < 0) {
1085 return rv;
1087 ss->maxlen += len;
1088 return 0;
1091 PR_IMPLEMENT(PRUint32)
1092 PR_sxprintf(PRStuffFunc func, void* arg, const char* fmt, ...) {
1093 va_list ap;
1094 PRUint32 rv;
1096 va_start(ap, fmt);
1097 rv = PR_vsxprintf(func, arg, fmt, ap);
1098 va_end(ap);
1099 return rv;
1102 PR_IMPLEMENT(PRUint32)
1103 PR_vsxprintf(PRStuffFunc func, void* arg, const char* fmt, va_list ap) {
1104 SprintfState ss;
1105 int rv;
1107 ss.stuff = FuncStuff;
1108 ss.func = func;
1109 ss.arg = arg;
1110 ss.maxlen = 0;
1111 rv = dosprintf(&ss, fmt, ap);
1112 return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
1116 ** Stuff routine that automatically grows the malloc'd output buffer
1117 ** before it overflows.
1119 static int GrowStuff(SprintfState* ss, const char* sp, PRUint32 len) {
1120 ptrdiff_t off;
1121 char* newbase;
1122 PRUint32 newlen;
1124 off = ss->cur - ss->base;
1125 if (PR_UINT32_MAX - len < off) {
1126 /* off + len would be too big. */
1127 return -1;
1129 if (off + len >= ss->maxlen) {
1130 /* Grow the buffer */
1131 PRUint32 increment = (len > 32) ? len : 32;
1132 if (PR_UINT32_MAX - ss->maxlen < increment) {
1133 /* ss->maxlen + increment would overflow. */
1134 return -1;
1136 newlen = ss->maxlen + increment;
1137 if (newlen > PR_INT32_MAX) {
1138 return -1;
1140 if (ss->base) {
1141 newbase = (char*)PR_REALLOC(ss->base, newlen);
1142 } else {
1143 newbase = (char*)PR_MALLOC(newlen);
1145 if (!newbase) {
1146 /* Ran out of memory */
1147 return -1;
1149 ss->base = newbase;
1150 ss->maxlen = newlen;
1151 ss->cur = ss->base + off;
1154 /* Copy data */
1155 while (len) {
1156 --len;
1157 *ss->cur++ = *sp++;
1159 PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
1160 return 0;
1164 ** sprintf into a malloc'd buffer
1166 PR_IMPLEMENT(char*) PR_smprintf(const char* fmt, ...) {
1167 va_list ap;
1168 char* rv;
1170 va_start(ap, fmt);
1171 rv = PR_vsmprintf(fmt, ap);
1172 va_end(ap);
1173 return rv;
1177 ** Free memory allocated, for the caller, by PR_smprintf
1179 PR_IMPLEMENT(void) PR_smprintf_free(char* mem) { PR_DELETE(mem); }
1181 PR_IMPLEMENT(char*) PR_vsmprintf(const char* fmt, va_list ap) {
1182 SprintfState ss;
1183 int rv;
1185 ss.stuff = GrowStuff;
1186 ss.base = 0;
1187 ss.cur = 0;
1188 ss.maxlen = 0;
1189 rv = dosprintf(&ss, fmt, ap);
1190 if (rv < 0) {
1191 if (ss.base) {
1192 PR_DELETE(ss.base);
1194 return 0;
1196 return ss.base;
1200 ** Stuff routine that discards overflow data
1202 static int LimitStuff(SprintfState* ss, const char* sp, PRUint32 len) {
1203 PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
1205 if (len > limit) {
1206 len = limit;
1208 while (len) {
1209 --len;
1210 *ss->cur++ = *sp++;
1212 return 0;
1216 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1217 ** when finished.
1219 PR_IMPLEMENT(PRUint32)
1220 PR_snprintf(char* out, PRUint32 outlen, const char* fmt, ...) {
1221 va_list ap;
1222 PRUint32 rv;
1224 va_start(ap, fmt);
1225 rv = PR_vsnprintf(out, outlen, fmt, ap);
1226 va_end(ap);
1227 return rv;
1230 PR_IMPLEMENT(PRUint32)
1231 PR_vsnprintf(char* out, PRUint32 outlen, const char* fmt, va_list ap) {
1232 SprintfState ss;
1233 PRUint32 n;
1235 PR_ASSERT(outlen != 0 && outlen <= PR_INT32_MAX);
1236 if (outlen == 0 || outlen > PR_INT32_MAX) {
1237 return 0;
1240 ss.stuff = LimitStuff;
1241 ss.base = out;
1242 ss.cur = out;
1243 ss.maxlen = outlen;
1244 (void)dosprintf(&ss, fmt, ap);
1246 /* If we added chars, and we didn't append a null, do it now. */
1247 if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) {
1248 *(ss.cur - 1) = '\0';
1251 n = ss.cur - ss.base;
1252 return n ? n - 1 : n;
1255 PR_IMPLEMENT(char*) PR_sprintf_append(char* last, const char* fmt, ...) {
1256 va_list ap;
1257 char* rv;
1259 va_start(ap, fmt);
1260 rv = PR_vsprintf_append(last, fmt, ap);
1261 va_end(ap);
1262 return rv;
1265 PR_IMPLEMENT(char*)
1266 PR_vsprintf_append(char* last, const char* fmt, va_list ap) {
1267 SprintfState ss;
1268 int rv;
1270 ss.stuff = GrowStuff;
1271 if (last) {
1272 size_t lastlen = strlen(last);
1273 if (lastlen > PR_INT32_MAX) {
1274 return 0;
1276 ss.base = last;
1277 ss.cur = last + lastlen;
1278 ss.maxlen = lastlen;
1279 } else {
1280 ss.base = 0;
1281 ss.cur = 0;
1282 ss.maxlen = 0;
1284 rv = dosprintf(&ss, fmt, ap);
1285 if (rv < 0) {
1286 if (ss.base) {
1287 PR_DELETE(ss.base);
1289 return 0;
1291 return ss.base;