nspr: import 3.0 RC1 cutoff from CVS
[mozilla-nspr.git] / nsprpub / pr / src / io / prprf.c
blobfa444e2cdfb19ef35236bed3a4f4fa6afce2ad95
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 the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 ** Portable safe sprintf code.
41 ** Author: Kipp E.B. Hickman
43 #include <stdarg.h>
44 #include <stddef.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include "primpl.h"
48 #include "prprf.h"
49 #include "prlong.h"
50 #include "prlog.h"
51 #include "prmem.h"
54 ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
58 ** XXX This needs to be internationalized!
61 typedef struct SprintfStateStr SprintfState;
63 struct SprintfStateStr {
64 int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len);
66 char *base;
67 char *cur;
68 PRUint32 maxlen;
70 int (*func)(void *arg, const char *sp, PRUint32 len);
71 void *arg;
75 ** Numbered Argument
77 struct NumArg {
78 int type; /* type of the numbered argument */
79 union { /* the numbered argument */
80 int i;
81 unsigned int ui;
82 PRInt32 i32;
83 PRUint32 ui32;
84 PRInt64 ll;
85 PRUint64 ull;
86 double d;
87 const char *s;
88 int *ip;
89 } u;
92 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
95 #define TYPE_INT16 0
96 #define TYPE_UINT16 1
97 #define TYPE_INTN 2
98 #define TYPE_UINTN 3
99 #define TYPE_INT32 4
100 #define TYPE_UINT32 5
101 #define TYPE_INT64 6
102 #define TYPE_UINT64 7
103 #define TYPE_STRING 8
104 #define TYPE_DOUBLE 9
105 #define TYPE_INTSTR 10
106 #define TYPE_UNKNOWN 20
108 #define FLAG_LEFT 0x1
109 #define FLAG_SIGNED 0x2
110 #define FLAG_SPACED 0x4
111 #define FLAG_ZEROS 0x8
112 #define FLAG_NEG 0x10
115 ** Fill into the buffer using the data in src
117 static int fill2(SprintfState *ss, const char *src, int srclen, int width,
118 int flags)
120 char space = ' ';
121 int rv;
123 width -= srclen;
124 if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
125 if (flags & FLAG_ZEROS) {
126 space = '0';
128 while (--width >= 0) {
129 rv = (*ss->stuff)(ss, &space, 1);
130 if (rv < 0) {
131 return rv;
136 /* Copy out the source data */
137 rv = (*ss->stuff)(ss, src, srclen);
138 if (rv < 0) {
139 return rv;
142 if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
143 while (--width >= 0) {
144 rv = (*ss->stuff)(ss, &space, 1);
145 if (rv < 0) {
146 return rv;
150 return 0;
154 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
156 static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
157 int prec, int type, int flags)
159 int zerowidth = 0;
160 int precwidth = 0;
161 int signwidth = 0;
162 int leftspaces = 0;
163 int rightspaces = 0;
164 int cvtwidth;
165 int rv;
166 char sign;
168 if ((type & 1) == 0) {
169 if (flags & FLAG_NEG) {
170 sign = '-';
171 signwidth = 1;
172 } else if (flags & FLAG_SIGNED) {
173 sign = '+';
174 signwidth = 1;
175 } else if (flags & FLAG_SPACED) {
176 sign = ' ';
177 signwidth = 1;
180 cvtwidth = signwidth + srclen;
182 if (prec > 0) {
183 if (prec > srclen) {
184 precwidth = prec - srclen; /* Need zero filling */
185 cvtwidth += precwidth;
189 if ((flags & FLAG_ZEROS) && (prec < 0)) {
190 if (width > cvtwidth) {
191 zerowidth = width - cvtwidth; /* Zero filling */
192 cvtwidth += zerowidth;
196 if (flags & FLAG_LEFT) {
197 if (width > cvtwidth) {
198 /* Space filling on the right (i.e. left adjusting) */
199 rightspaces = width - cvtwidth;
201 } else {
202 if (width > cvtwidth) {
203 /* Space filling on the left (i.e. right adjusting) */
204 leftspaces = width - cvtwidth;
207 while (--leftspaces >= 0) {
208 rv = (*ss->stuff)(ss, " ", 1);
209 if (rv < 0) {
210 return rv;
213 if (signwidth) {
214 rv = (*ss->stuff)(ss, &sign, 1);
215 if (rv < 0) {
216 return rv;
219 while (--precwidth >= 0) {
220 rv = (*ss->stuff)(ss, "0", 1);
221 if (rv < 0) {
222 return rv;
225 while (--zerowidth >= 0) {
226 rv = (*ss->stuff)(ss, "0", 1);
227 if (rv < 0) {
228 return rv;
231 rv = (*ss->stuff)(ss, src, srclen);
232 if (rv < 0) {
233 return rv;
235 while (--rightspaces >= 0) {
236 rv = (*ss->stuff)(ss, " ", 1);
237 if (rv < 0) {
238 return rv;
241 return 0;
245 ** Convert a long into its printable form
247 static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
248 int type, int flags, const char *hexp)
250 char cvtbuf[100];
251 char *cvt;
252 int digits;
254 /* according to the man page this needs to happen */
255 if ((prec == 0) && (num == 0)) {
256 return 0;
260 ** Converting decimal is a little tricky. In the unsigned case we
261 ** need to stop when we hit 10 digits. In the signed case, we can
262 ** stop when the number is zero.
264 cvt = cvtbuf + sizeof(cvtbuf);
265 digits = 0;
266 while (num) {
267 int digit = (((unsigned long)num) % radix) & 0xF;
268 *--cvt = hexp[digit];
269 digits++;
270 num = (long)(((unsigned long)num) / radix);
272 if (digits == 0) {
273 *--cvt = '0';
274 digits++;
278 ** Now that we have the number converted without its sign, deal with
279 ** the sign and zero padding.
281 return fill_n(ss, cvt, digits, width, prec, type, flags);
285 ** Convert a 64-bit integer into its printable form
287 static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix,
288 int type, int flags, const char *hexp)
290 char cvtbuf[100];
291 char *cvt;
292 int digits;
293 PRInt64 rad;
295 /* according to the man page this needs to happen */
296 if ((prec == 0) && (LL_IS_ZERO(num))) {
297 return 0;
301 ** Converting decimal is a little tricky. In the unsigned case we
302 ** need to stop when we hit 10 digits. In the signed case, we can
303 ** stop when the number is zero.
305 LL_I2L(rad, radix);
306 cvt = cvtbuf + sizeof(cvtbuf);
307 digits = 0;
308 while (!LL_IS_ZERO(num)) {
309 PRInt32 digit;
310 PRInt64 quot, rem;
311 LL_UDIVMOD(&quot, &rem, num, rad);
312 LL_L2I(digit, rem);
313 *--cvt = hexp[digit & 0xf];
314 digits++;
315 num = quot;
317 if (digits == 0) {
318 *--cvt = '0';
319 digits++;
323 ** Now that we have the number converted without its sign, deal with
324 ** the sign and zero padding.
326 return fill_n(ss, cvt, digits, width, prec, type, flags);
330 ** Convert a double precision floating point number into its printable
331 ** form.
333 ** XXX stop using sprintf to convert floating point
335 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
337 char fin[20];
338 char fout[300];
339 int amount = fmt1 - fmt0;
341 PR_ASSERT((amount > 0) && (amount < sizeof(fin)));
342 if (amount >= sizeof(fin)) {
343 /* Totally bogus % command to sprintf. Just ignore it */
344 return 0;
346 memcpy(fin, fmt0, amount);
347 fin[amount] = 0;
349 /* Convert floating point using the native sprintf code */
350 #ifdef DEBUG
352 const char *p = fin;
353 while (*p) {
354 PR_ASSERT(*p != 'L');
355 p++;
358 #endif
359 sprintf(fout, fin, d);
362 ** This assert will catch overflow's of fout, when building with
363 ** debugging on. At least this way we can track down the evil piece
364 ** of calling code and fix it!
366 PR_ASSERT(strlen(fout) < sizeof(fout));
368 return (*ss->stuff)(ss, fout, strlen(fout));
372 ** Convert a string into its printable form. "width" is the output
373 ** width. "prec" is the maximum number of characters of "s" to output,
374 ** where -1 means until NUL.
376 static int cvt_s(SprintfState *ss, const char *str, int width, int prec,
377 int flags)
379 int slen;
381 if (prec == 0)
382 return 0;
384 /* Limit string length by precision value */
385 if (!str) {
386 str = "(null)";
388 if (prec > 0) {
389 /* this is: slen = strnlen(str, prec); */
390 register const char *s;
392 for(s = str; prec && *s; s++, prec-- )
394 slen = s - str;
395 } else {
396 slen = strlen(str);
399 /* and away we go */
400 return fill2(ss, str, slen, width, flags);
404 ** BuildArgArray stands for Numbered Argument list Sprintf
405 ** for example,
406 ** fmp = "%4$i, %2$d, %3s, %1d";
407 ** the number must start from 1, and no gap among them
410 static struct NumArg* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArg* nasArray )
412 int number = 0, cn = 0, i;
413 const char* p;
414 char c;
415 struct NumArg* nas;
419 ** first pass:
420 ** determine how many legal % I have got, then allocate space
423 p = fmt;
424 *rv = 0;
425 i = 0;
426 while( ( c = *p++ ) != 0 ){
427 if( c != '%' )
428 continue;
429 if( ( c = *p++ ) == '%' ) /* skip %% case */
430 continue;
432 while( c != 0 ){
433 if( c > '9' || c < '0' ){
434 if( c == '$' ){ /* numbered argument case */
435 if( i > 0 ){
436 *rv = -1;
437 return NULL;
439 number++;
440 } else{ /* non-numbered argument case */
441 if( number > 0 ){
442 *rv = -1;
443 return NULL;
445 i = 1;
447 break;
450 c = *p++;
454 if( number == 0 ){
455 return NULL;
459 if( number > NAS_DEFAULT_NUM ){
460 nas = (struct NumArg*)PR_MALLOC( number * sizeof( struct NumArg ) );
461 if( !nas ){
462 *rv = -1;
463 return NULL;
465 } else {
466 nas = nasArray;
469 for( i = 0; i < number; i++ ){
470 nas[i].type = TYPE_UNKNOWN;
475 ** second pass:
476 ** set nas[].type
479 p = fmt;
480 while( ( c = *p++ ) != 0 ){
481 if( c != '%' ) continue;
482 c = *p++;
483 if( c == '%' ) continue;
485 cn = 0;
486 while( c && c != '$' ){ /* should imporve error check later */
487 cn = cn*10 + c - '0';
488 c = *p++;
491 if( !c || cn < 1 || cn > number ){
492 *rv = -1;
493 break;
496 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
497 cn--;
498 if( nas[cn].type != TYPE_UNKNOWN )
499 continue;
501 c = *p++;
503 /* width */
504 if (c == '*') {
505 /* not supported feature, for the argument is not numbered */
506 *rv = -1;
507 break;
510 while ((c >= '0') && (c <= '9')) {
511 c = *p++;
514 /* precision */
515 if (c == '.') {
516 c = *p++;
517 if (c == '*') {
518 /* not supported feature, for the argument is not numbered */
519 *rv = -1;
520 break;
523 while ((c >= '0') && (c <= '9')) {
524 c = *p++;
528 /* size */
529 nas[cn].type = TYPE_INTN;
530 if (c == 'h') {
531 nas[cn].type = TYPE_INT16;
532 c = *p++;
533 } else if (c == 'L') {
534 /* XXX not quite sure here */
535 nas[cn].type = TYPE_INT64;
536 c = *p++;
537 } else if (c == 'l') {
538 nas[cn].type = TYPE_INT32;
539 c = *p++;
540 if (c == 'l') {
541 nas[cn].type = TYPE_INT64;
542 c = *p++;
546 /* format */
547 switch (c) {
548 case 'd':
549 case 'c':
550 case 'i':
551 case 'o':
552 case 'u':
553 case 'x':
554 case 'X':
555 break;
557 case 'e':
558 case 'f':
559 case 'g':
560 nas[ cn ].type = TYPE_DOUBLE;
561 break;
563 case 'p':
564 /* XXX should use cpp */
565 if (sizeof(void *) == sizeof(PRInt32)) {
566 nas[ cn ].type = TYPE_UINT32;
567 } else if (sizeof(void *) == sizeof(PRInt64)) {
568 nas[ cn ].type = TYPE_UINT64;
569 } else if (sizeof(void *) == sizeof(PRIntn)) {
570 nas[ cn ].type = TYPE_UINTN;
571 } else {
572 nas[ cn ].type = TYPE_UNKNOWN;
574 break;
576 case 'C':
577 case 'S':
578 case 'E':
579 case 'G':
580 /* XXX not supported I suppose */
581 PR_ASSERT(0);
582 nas[ cn ].type = TYPE_UNKNOWN;
583 break;
585 case 's':
586 nas[ cn ].type = TYPE_STRING;
587 break;
589 case 'n':
590 nas[ cn ].type = TYPE_INTSTR;
591 break;
593 default:
594 PR_ASSERT(0);
595 nas[ cn ].type = TYPE_UNKNOWN;
596 break;
599 /* get a legal para. */
600 if( nas[ cn ].type == TYPE_UNKNOWN ){
601 *rv = -1;
602 break;
608 ** third pass
609 ** fill the nas[cn].ap
612 if( *rv < 0 ){
613 if( nas != nasArray )
614 PR_DELETE( nas );
615 return NULL;
618 cn = 0;
619 while( cn < number ){
620 if( nas[cn].type == TYPE_UNKNOWN ){
621 cn++;
622 continue;
625 switch( nas[cn].type ){
626 case TYPE_INT16:
627 case TYPE_UINT16:
628 case TYPE_INTN:
629 nas[cn].u.i = va_arg( ap, int );
630 break;
632 case TYPE_UINTN:
633 nas[cn].u.ui = va_arg( ap, unsigned int );
634 break;
636 case TYPE_INT32:
637 nas[cn].u.i32 = va_arg( ap, PRInt32 );
638 break;
640 case TYPE_UINT32:
641 nas[cn].u.ui32 = va_arg( ap, PRUint32 );
642 break;
644 case TYPE_INT64:
645 nas[cn].u.ll = va_arg( ap, PRInt64 );
646 break;
648 case TYPE_UINT64:
649 nas[cn].u.ull = va_arg( ap, PRUint64 );
650 break;
652 case TYPE_STRING:
653 nas[cn].u.s = va_arg( ap, char* );
654 break;
656 case TYPE_INTSTR:
657 nas[cn].u.ip = va_arg( ap, int* );
658 break;
660 case TYPE_DOUBLE:
661 nas[cn].u.d = va_arg( ap, double );
662 break;
664 default:
665 if( nas != nasArray )
666 PR_DELETE( nas );
667 *rv = -1;
668 return NULL;
671 cn++;
675 return nas;
679 ** The workhorse sprintf code.
681 static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
683 char c;
684 int flags, width, prec, radix, type;
685 union {
686 char ch;
687 int i;
688 long l;
689 PRInt64 ll;
690 double d;
691 const char *s;
692 int *ip;
693 } u;
694 const char *fmt0;
695 static char *hex = "0123456789abcdef";
696 static char *HEX = "0123456789ABCDEF";
697 char *hexp;
698 int rv, i;
699 struct NumArg* nas = NULL;
700 struct NumArg* nap;
701 struct NumArg nasArray[ NAS_DEFAULT_NUM ];
702 char pattern[20];
703 const char* dolPt = NULL; /* in "%4$.2f", dolPt will point to . */
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 imporve 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 );
754 return -1;
757 nap = &nas[i-1];
758 dolPt = fmt;
759 c = *fmt++;
763 * Examine optional flags. Note that we do not implement the
764 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
765 * somewhat ambiguous and not ideal, which is perhaps why
766 * the various sprintf() implementations are inconsistent
767 * on this feature.
769 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
770 if (c == '-') flags |= FLAG_LEFT;
771 if (c == '+') flags |= FLAG_SIGNED;
772 if (c == ' ') flags |= FLAG_SPACED;
773 if (c == '0') flags |= FLAG_ZEROS;
774 c = *fmt++;
776 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
777 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
779 /* width */
780 if (c == '*') {
781 c = *fmt++;
782 width = va_arg(ap, int);
783 } else {
784 width = 0;
785 while ((c >= '0') && (c <= '9')) {
786 width = (width * 10) + (c - '0');
787 c = *fmt++;
791 /* precision */
792 prec = -1;
793 if (c == '.') {
794 c = *fmt++;
795 if (c == '*') {
796 c = *fmt++;
797 prec = va_arg(ap, int);
798 } else {
799 prec = 0;
800 while ((c >= '0') && (c <= '9')) {
801 prec = (prec * 10) + (c - '0');
802 c = *fmt++;
807 /* size */
808 type = TYPE_INTN;
809 if (c == 'h') {
810 type = TYPE_INT16;
811 c = *fmt++;
812 } else if (c == 'L') {
813 /* XXX not quite sure here */
814 type = TYPE_INT64;
815 c = *fmt++;
816 } else if (c == 'l') {
817 type = TYPE_INT32;
818 c = *fmt++;
819 if (c == 'l') {
820 type = TYPE_INT64;
821 c = *fmt++;
825 /* format */
826 hexp = hex;
827 switch (c) {
828 case 'd': case 'i': /* decimal/integer */
829 radix = 10;
830 goto fetch_and_convert;
832 case 'o': /* octal */
833 radix = 8;
834 type |= 1;
835 goto fetch_and_convert;
837 case 'u': /* unsigned decimal */
838 radix = 10;
839 type |= 1;
840 goto fetch_and_convert;
842 case 'x': /* unsigned hex */
843 radix = 16;
844 type |= 1;
845 goto fetch_and_convert;
847 case 'X': /* unsigned HEX */
848 radix = 16;
849 hexp = HEX;
850 type |= 1;
851 goto fetch_and_convert;
853 fetch_and_convert:
854 switch (type) {
855 case TYPE_INT16:
856 u.l = nas ? nap->u.i : va_arg(ap, int);
857 if (u.l < 0) {
858 u.l = -u.l;
859 flags |= FLAG_NEG;
861 goto do_long;
862 case TYPE_UINT16:
863 u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff;
864 goto do_long;
865 case TYPE_INTN:
866 u.l = nas ? nap->u.i : va_arg(ap, int);
867 if (u.l < 0) {
868 u.l = -u.l;
869 flags |= FLAG_NEG;
871 goto do_long;
872 case TYPE_UINTN:
873 u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int));
874 goto do_long;
876 case TYPE_INT32:
877 u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32);
878 if (u.l < 0) {
879 u.l = -u.l;
880 flags |= FLAG_NEG;
882 goto do_long;
883 case TYPE_UINT32:
884 u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32));
885 do_long:
886 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
887 if (rv < 0) {
888 return rv;
890 break;
892 case TYPE_INT64:
893 u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64);
894 if (!LL_GE_ZERO(u.ll)) {
895 LL_NEG(u.ll, u.ll);
896 flags |= FLAG_NEG;
898 goto do_longlong;
899 case TYPE_UINT64:
900 u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64);
901 do_longlong:
902 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
903 if (rv < 0) {
904 return rv;
906 break;
908 break;
910 case 'e':
911 case 'E':
912 case 'f':
913 case 'g':
914 u.d = nas ? nap->u.d : va_arg(ap, double);
915 if( nas != NULL ){
916 i = fmt - dolPt;
917 if( i < sizeof( pattern ) ){
918 pattern[0] = '%';
919 memcpy( &pattern[1], dolPt, i );
920 rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
922 } else
923 rv = cvt_f(ss, u.d, fmt0, fmt);
925 if (rv < 0) {
926 return rv;
928 break;
930 case 'c':
931 u.ch = nas ? nap->u.i : va_arg(ap, int);
932 if ((flags & FLAG_LEFT) == 0) {
933 while (width-- > 1) {
934 rv = (*ss->stuff)(ss, " ", 1);
935 if (rv < 0) {
936 return rv;
940 rv = (*ss->stuff)(ss, &u.ch, 1);
941 if (rv < 0) {
942 return rv;
944 if (flags & FLAG_LEFT) {
945 while (width-- > 1) {
946 rv = (*ss->stuff)(ss, " ", 1);
947 if (rv < 0) {
948 return rv;
952 break;
954 case 'p':
955 if (sizeof(void *) == sizeof(PRInt32)) {
956 type = TYPE_UINT32;
957 } else if (sizeof(void *) == sizeof(PRInt64)) {
958 type = TYPE_UINT64;
959 } else if (sizeof(void *) == sizeof(int)) {
960 type = TYPE_UINTN;
961 } else {
962 PR_ASSERT(0);
963 break;
965 radix = 16;
966 goto fetch_and_convert;
968 #if 0
969 case 'C':
970 case 'S':
971 case 'E':
972 case 'G':
973 /* XXX not supported I suppose */
974 PR_ASSERT(0);
975 break;
976 #endif
978 case 's':
979 u.s = nas ? nap->u.s : va_arg(ap, const char*);
980 rv = cvt_s(ss, u.s, width, prec, flags);
981 if (rv < 0) {
982 return rv;
984 break;
986 case 'n':
987 u.ip = nas ? nap->u.ip : va_arg(ap, int*);
988 if (u.ip) {
989 *u.ip = ss->cur - ss->base;
991 break;
993 default:
994 /* Not a % token after all... skip it */
995 #if 0
996 PR_ASSERT(0);
997 #endif
998 rv = (*ss->stuff)(ss, "%", 1);
999 if (rv < 0) {
1000 return rv;
1002 rv = (*ss->stuff)(ss, fmt - 1, 1);
1003 if (rv < 0) {
1004 return rv;
1009 /* Stuff trailing NUL */
1010 rv = (*ss->stuff)(ss, "\0", 1);
1012 if( nas && ( nas != nasArray ) ){
1013 PR_DELETE( nas );
1016 return rv;
1019 /************************************************************************/
1021 static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len)
1023 int rv;
1025 rv = (*ss->func)(ss->arg, sp, len);
1026 if (rv < 0) {
1027 return rv;
1029 ss->maxlen += len;
1030 return 0;
1033 PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg,
1034 const char *fmt, ...)
1036 va_list ap;
1037 PRUint32 rv;
1039 va_start(ap, fmt);
1040 rv = PR_vsxprintf(func, arg, fmt, ap);
1041 va_end(ap);
1042 return rv;
1045 PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg,
1046 const char *fmt, va_list ap)
1048 SprintfState ss;
1049 int rv;
1051 ss.stuff = FuncStuff;
1052 ss.func = func;
1053 ss.arg = arg;
1054 ss.maxlen = 0;
1055 rv = dosprintf(&ss, fmt, ap);
1056 return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
1060 ** Stuff routine that automatically grows the malloc'd output buffer
1061 ** before it overflows.
1063 static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len)
1065 ptrdiff_t off;
1066 char *newbase;
1067 PRUint32 newlen;
1069 off = ss->cur - ss->base;
1070 if (off + len >= ss->maxlen) {
1071 /* Grow the buffer */
1072 newlen = ss->maxlen + ((len > 32) ? len : 32);
1073 if (ss->base) {
1074 newbase = (char*) PR_REALLOC(ss->base, newlen);
1075 } else {
1076 newbase = (char*) PR_MALLOC(newlen);
1078 if (!newbase) {
1079 /* Ran out of memory */
1080 return -1;
1082 ss->base = newbase;
1083 ss->maxlen = newlen;
1084 ss->cur = ss->base + off;
1087 /* Copy data */
1088 while (len) {
1089 --len;
1090 *ss->cur++ = *sp++;
1092 PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
1093 return 0;
1097 ** sprintf into a malloc'd buffer
1099 PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...)
1101 va_list ap;
1102 char *rv;
1104 va_start(ap, fmt);
1105 rv = PR_vsmprintf(fmt, ap);
1106 va_end(ap);
1107 return rv;
1111 ** Free memory allocated, for the caller, by PR_smprintf
1113 PR_IMPLEMENT(void) PR_smprintf_free(char *mem)
1115 PR_DELETE(mem);
1118 PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap)
1120 SprintfState ss;
1121 int rv;
1123 ss.stuff = GrowStuff;
1124 ss.base = 0;
1125 ss.cur = 0;
1126 ss.maxlen = 0;
1127 rv = dosprintf(&ss, fmt, ap);
1128 if (rv < 0) {
1129 if (ss.base) {
1130 PR_DELETE(ss.base);
1132 return 0;
1134 return ss.base;
1138 ** Stuff routine that discards overflow data
1140 static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len)
1142 PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
1144 if (len > limit) {
1145 len = limit;
1147 while (len) {
1148 --len;
1149 *ss->cur++ = *sp++;
1151 return 0;
1155 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1156 ** when finished.
1158 PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...)
1160 va_list ap;
1161 PRUint32 rv;
1163 PR_ASSERT((PRInt32)outlen > 0);
1164 if ((PRInt32)outlen <= 0) {
1165 return 0;
1168 va_start(ap, fmt);
1169 rv = PR_vsnprintf(out, outlen, fmt, ap);
1170 va_end(ap);
1171 return rv;
1174 PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt,
1175 va_list ap)
1177 SprintfState ss;
1178 PRUint32 n;
1180 PR_ASSERT((PRInt32)outlen > 0);
1181 if ((PRInt32)outlen <= 0) {
1182 return 0;
1185 ss.stuff = LimitStuff;
1186 ss.base = out;
1187 ss.cur = out;
1188 ss.maxlen = outlen;
1189 (void) dosprintf(&ss, fmt, ap);
1191 /* If we added chars, and we didn't append a null, do it now. */
1192 if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
1193 *(ss.cur - 1) = '\0';
1195 n = ss.cur - ss.base;
1196 return n ? n - 1 : n;
1199 PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...)
1201 va_list ap;
1202 char *rv;
1204 va_start(ap, fmt);
1205 rv = PR_vsprintf_append(last, fmt, ap);
1206 va_end(ap);
1207 return rv;
1210 PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap)
1212 SprintfState ss;
1213 int rv;
1215 ss.stuff = GrowStuff;
1216 if (last) {
1217 int lastlen = strlen(last);
1218 ss.base = last;
1219 ss.cur = last + lastlen;
1220 ss.maxlen = lastlen;
1221 } else {
1222 ss.base = 0;
1223 ss.cur = 0;
1224 ss.maxlen = 0;
1226 rv = dosprintf(&ss, fmt, ap);
1227 if (rv < 0) {
1228 if (ss.base) {
1229 PR_DELETE(ss.base);
1231 return 0;
1233 return ss.base;