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