OLD | NEW |
| (Empty) |
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 static int fill2(SprintfState *ss, const char *src, int srclen, int width, | |
100 int flags) | |
101 { | |
102 char space = ' '; | |
103 int rv; | |
104 | |
105 width -= srclen; | |
106 if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ | |
107 if (flags & FLAG_ZEROS) { | |
108 space = '0'; | |
109 } | |
110 while (--width >= 0) { | |
111 rv = (*ss->stuff)(ss, &space, 1); | |
112 if (rv < 0) { | |
113 return rv; | |
114 } | |
115 } | |
116 } | |
117 | |
118 /* Copy out the source data */ | |
119 rv = (*ss->stuff)(ss, src, srclen); | |
120 if (rv < 0) { | |
121 return rv; | |
122 } | |
123 | |
124 if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ | |
125 while (--width >= 0) { | |
126 rv = (*ss->stuff)(ss, &space, 1); | |
127 if (rv < 0) { | |
128 return rv; | |
129 } | |
130 } | |
131 } | |
132 return 0; | |
133 } | |
134 | |
135 /* | |
136 ** Fill a number. The order is: optional-sign zero-filling conversion-digits | |
137 */ | |
138 static int fill_n(SprintfState *ss, const char *src, int srclen, int width, | |
139 int prec, int type, int flags) | |
140 { | |
141 int zerowidth = 0; | |
142 int precwidth = 0; | |
143 int signwidth = 0; | |
144 int leftspaces = 0; | |
145 int rightspaces = 0; | |
146 int cvtwidth; | |
147 int rv; | |
148 char sign; | |
149 | |
150 if ((type & 1) == 0) { | |
151 if (flags & FLAG_NEG) { | |
152 sign = '-'; | |
153 signwidth = 1; | |
154 } else if (flags & FLAG_SIGNED) { | |
155 sign = '+'; | |
156 signwidth = 1; | |
157 } else if (flags & FLAG_SPACED) { | |
158 sign = ' '; | |
159 signwidth = 1; | |
160 } | |
161 } | |
162 cvtwidth = signwidth + srclen; | |
163 | |
164 if (prec > 0) { | |
165 if (prec > srclen) { | |
166 precwidth = prec - srclen; /* Need zero filling */ | |
167 cvtwidth += precwidth; | |
168 } | |
169 } | |
170 | |
171 if ((flags & FLAG_ZEROS) && (prec < 0)) { | |
172 if (width > cvtwidth) { | |
173 zerowidth = width - cvtwidth; /* Zero filling */ | |
174 cvtwidth += zerowidth; | |
175 } | |
176 } | |
177 | |
178 if (flags & FLAG_LEFT) { | |
179 if (width > cvtwidth) { | |
180 /* Space filling on the right (i.e. left adjusting) */ | |
181 rightspaces = width - cvtwidth; | |
182 } | |
183 } else { | |
184 if (width > cvtwidth) { | |
185 /* Space filling on the left (i.e. right adjusting) */ | |
186 leftspaces = width - cvtwidth; | |
187 } | |
188 } | |
189 while (--leftspaces >= 0) { | |
190 rv = (*ss->stuff)(ss, " ", 1); | |
191 if (rv < 0) { | |
192 return rv; | |
193 } | |
194 } | |
195 if (signwidth) { | |
196 rv = (*ss->stuff)(ss, &sign, 1); | |
197 if (rv < 0) { | |
198 return rv; | |
199 } | |
200 } | |
201 while (--precwidth >= 0) { | |
202 rv = (*ss->stuff)(ss, "0", 1); | |
203 if (rv < 0) { | |
204 return rv; | |
205 } | |
206 } | |
207 while (--zerowidth >= 0) { | |
208 rv = (*ss->stuff)(ss, "0", 1); | |
209 if (rv < 0) { | |
210 return rv; | |
211 } | |
212 } | |
213 rv = (*ss->stuff)(ss, src, srclen); | |
214 if (rv < 0) { | |
215 return rv; | |
216 } | |
217 while (--rightspaces >= 0) { | |
218 rv = (*ss->stuff)(ss, " ", 1); | |
219 if (rv < 0) { | |
220 return rv; | |
221 } | |
222 } | |
223 return 0; | |
224 } | |
225 | |
226 /* | |
227 ** Convert a long into its printable form | |
228 */ | |
229 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 if ((prec == 0) && (num == 0)) { | |
238 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 cvt = cvtbuf + sizeof(cvtbuf); | |
247 digits = 0; | |
248 while (num) { | |
249 int digit = (((unsigned long)num) % radix) & 0xF; | |
250 *--cvt = hexp[digit]; | |
251 digits++; | |
252 num = (long)(((unsigned long)num) / radix); | |
253 } | |
254 if (digits == 0) { | |
255 *--cvt = '0'; | |
256 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 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 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 if ((prec == 0) && (LL_IS_ZERO(num))) { | |
279 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 LL_I2L(rad, radix); | |
288 cvt = cvtbuf + sizeof(cvtbuf); | |
289 digits = 0; | |
290 while (!LL_IS_ZERO(num)) { | |
291 PRInt32 digit; | |
292 PRInt64 quot, rem; | |
293 LL_UDIVMOD(", &rem, num, rad); | |
294 LL_L2I(digit, rem); | |
295 *--cvt = hexp[digit & 0xf]; | |
296 digits++; | |
297 num = quot; | |
298 } | |
299 if (digits == 0) { | |
300 *--cvt = '0'; | |
301 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 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 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) | |
318 { | |
319 char fin[20]; | |
320 char fout[300]; | |
321 int amount = fmt1 - fmt0; | |
322 | |
323 if (amount <= 0 || amount >= sizeof(fin)) { | |
324 /* Totally bogus % command to snprintf. Just ignore it */ | |
325 return 0; | |
326 } | |
327 memcpy(fin, fmt0, amount); | |
328 fin[amount] = 0; | |
329 | |
330 /* Convert floating point using the native snprintf code */ | |
331 #ifdef DEBUG | |
332 { | |
333 const char *p = fin; | |
334 while (*p) { | |
335 PR_ASSERT(*p != 'L'); | |
336 p++; | |
337 } | |
338 } | |
339 #endif | |
340 memset(fout, 0, sizeof(fout)); | |
341 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 fout[sizeof(fout) - 1] = '\0'; | |
345 | |
346 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 static int cvt_s(SprintfState *ss, const char *str, int width, int prec, | |
355 int flags) | |
356 { | |
357 int slen; | |
358 | |
359 if (prec == 0) | |
360 return 0; | |
361 | |
362 /* Limit string length by precision value */ | |
363 if (!str) { | |
364 str = "(null)"; | |
365 } | |
366 if (prec > 0) { | |
367 /* this is: slen = strnlen(str, prec); */ | |
368 register const char *s; | |
369 | |
370 for(s = str; prec && *s; s++, prec-- ) | |
371 ; | |
372 slen = s - str; | |
373 } else { | |
374 slen = strlen(str); | |
375 } | |
376 | |
377 /* and away we go */ | |
378 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 static struct NumArg* BuildArgArray( const char *fmt, va_list ap, int* rv, struc
t NumArg* nasArray ) | |
389 { | |
390 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 p = fmt; | |
402 *rv = 0; | |
403 i = 0; | |
404 while( ( c = *p++ ) != 0 ){ | |
405 if( c != '%' ) | |
406 continue; | |
407 if( ( c = *p++ ) == '%' ) /* skip %% case */ | |
408 continue; | |
409 | |
410 while( c != 0 ){ | |
411 if( c > '9' || c < '0' ){ | |
412 if( c == '$' ){ /* numbered argument case */ | |
413 if( i > 0 ){ | |
414 *rv = -1; | |
415 return NULL; | |
416 } | |
417 number++; | |
418 } else{ /* non-numbered argument case */ | |
419 if( number > 0 ){ | |
420 *rv = -1; | |
421 return NULL; | |
422 } | |
423 i = 1; | |
424 } | |
425 break; | |
426 } | |
427 | |
428 c = *p++; | |
429 } | |
430 } | |
431 | |
432 if( number == 0 ){ | |
433 return NULL; | |
434 } | |
435 | |
436 | |
437 if( number > NAS_DEFAULT_NUM ){ | |
438 nas = (struct NumArg*)PR_MALLOC( number * sizeof( struct NumArg ) ); | |
439 if( !nas ){ | |
440 *rv = -1; | |
441 return NULL; | |
442 } | |
443 } else { | |
444 nas = nasArray; | |
445 } | |
446 | |
447 for( i = 0; i < number; i++ ){ | |
448 nas[i].type = TYPE_UNKNOWN; | |
449 } | |
450 | |
451 | |
452 /* | |
453 ** second pass: | |
454 ** set nas[].type | |
455 */ | |
456 | |
457 p = fmt; | |
458 while( ( c = *p++ ) != 0 ){ | |
459 if( c != '%' ) continue; | |
460 c = *p++; | |
461 if( c == '%' ) continue; | |
462 | |
463 cn = 0; | |
464 while( c && c != '$' ){ /* should imporve error check later */ | |
465 cn = cn*10 + c - '0'; | |
466 c = *p++; | |
467 } | |
468 | |
469 if( !c || cn < 1 || cn > number ){ | |
470 *rv = -1; | |
471 break; | |
472 } | |
473 | |
474 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ | |
475 cn--; | |
476 if( nas[cn].type != TYPE_UNKNOWN ) | |
477 continue; | |
478 | |
479 c = *p++; | |
480 | |
481 /* width */ | |
482 if (c == '*') { | |
483 /* not supported feature, for the argument is not numbered */ | |
484 *rv = -1; | |
485 break; | |
486 } | |
487 | |
488 while ((c >= '0') && (c <= '9')) { | |
489 c = *p++; | |
490 } | |
491 | |
492 /* precision */ | |
493 if (c == '.') { | |
494 c = *p++; | |
495 if (c == '*') { | |
496 /* not supported feature, for the argument is not numbered */ | |
497 *rv = -1; | |
498 break; | |
499 } | |
500 | |
501 while ((c >= '0') && (c <= '9')) { | |
502 c = *p++; | |
503 } | |
504 } | |
505 | |
506 /* size */ | |
507 nas[cn].type = TYPE_INTN; | |
508 if (c == 'h') { | |
509 nas[cn].type = TYPE_INT16; | |
510 c = *p++; | |
511 } else if (c == 'L') { | |
512 /* XXX not quite sure here */ | |
513 nas[cn].type = TYPE_INT64; | |
514 c = *p++; | |
515 } else if (c == 'l') { | |
516 nas[cn].type = TYPE_INT32; | |
517 c = *p++; | |
518 if (c == 'l') { | |
519 nas[cn].type = TYPE_INT64; | |
520 c = *p++; | |
521 } | |
522 } 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 nas[ cn ].type = TYPE_INT64; | |
527 } else { | |
528 nas[ cn ].type = TYPE_UNKNOWN; | |
529 } | |
530 c = *p++; | |
531 } | |
532 | |
533 /* format */ | |
534 switch (c) { | |
535 case 'd': | |
536 case 'c': | |
537 case 'i': | |
538 case 'o': | |
539 case 'u': | |
540 case 'x': | |
541 case 'X': | |
542 break; | |
543 | |
544 case 'e': | |
545 case 'f': | |
546 case 'g': | |
547 nas[ cn ].type = TYPE_DOUBLE; | |
548 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 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 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 PR_ASSERT(0); | |
573 nas[ cn ].type = TYPE_UNKNOWN; | |
574 break; | |
575 | |
576 case 's': | |
577 nas[ cn ].type = TYPE_STRING; | |
578 break; | |
579 | |
580 case 'n': | |
581 nas[ cn ].type = TYPE_INTSTR; | |
582 break; | |
583 | |
584 default: | |
585 PR_ASSERT(0); | |
586 nas[ cn ].type = TYPE_UNKNOWN; | |
587 break; | |
588 } | |
589 | |
590 /* get a legal para. */ | |
591 if( nas[ cn ].type == TYPE_UNKNOWN ){ | |
592 *rv = -1; | |
593 break; | |
594 } | |
595 } | |
596 | |
597 | |
598 /* | |
599 ** third pass | |
600 ** fill the nas[cn].ap | |
601 */ | |
602 | |
603 if( *rv < 0 ){ | |
604 if( nas != nasArray ) | |
605 PR_DELETE( nas ); | |
606 return NULL; | |
607 } | |
608 | |
609 cn = 0; | |
610 while( cn < number ){ | |
611 if( nas[cn].type == TYPE_UNKNOWN ){ | |
612 cn++; | |
613 continue; | |
614 } | |
615 | |
616 switch( nas[cn].type ){ | |
617 case TYPE_INT16: | |
618 case TYPE_UINT16: | |
619 case TYPE_INTN: | |
620 nas[cn].u.i = va_arg( ap, int ); | |
621 break; | |
622 | |
623 case TYPE_UINTN: | |
624 nas[cn].u.ui = va_arg( ap, unsigned int ); | |
625 break; | |
626 | |
627 case TYPE_INT32: | |
628 nas[cn].u.i32 = va_arg( ap, PRInt32 ); | |
629 break; | |
630 | |
631 case TYPE_UINT32: | |
632 nas[cn].u.ui32 = va_arg( ap, PRUint32 ); | |
633 break; | |
634 | |
635 case TYPE_INT64: | |
636 nas[cn].u.ll = va_arg( ap, PRInt64 ); | |
637 break; | |
638 | |
639 case TYPE_UINT64: | |
640 nas[cn].u.ull = va_arg( ap, PRUint64 ); | |
641 break; | |
642 | |
643 case TYPE_STRING: | |
644 nas[cn].u.s = va_arg( ap, char* ); | |
645 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 nas[cn].u.ip = va_arg( ap, int* ); | |
655 break; | |
656 | |
657 case TYPE_DOUBLE: | |
658 nas[cn].u.d = va_arg( ap, double ); | |
659 break; | |
660 | |
661 default: | |
662 if( nas != nasArray ) | |
663 PR_DELETE( nas ); | |
664 *rv = -1; | |
665 return NULL; | |
666 } | |
667 | |
668 cn++; | |
669 } | |
670 | |
671 | |
672 return nas; | |
673 } | |
674 | |
675 /* | |
676 ** The workhorse sprintf code. | |
677 */ | |
678 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 struct NumArg* nas = NULL; | |
700 struct NumArg* nap = NULL; | |
701 struct NumArg nasArray[ NAS_DEFAULT_NUM ]; | |
702 char pattern[20]; | |
703 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 nas = BuildArgArray( fmt, ap, &rv, nasArray ); | |
714 if( rv < 0 ){ | |
715 /* the fmt contains error Numbered Argument format, jliu@netscape.com */ | |
716 PR_ASSERT(0); | |
717 return rv; | |
718 } | |
719 | |
720 while ((c = *fmt++) != 0) { | |
721 if (c != '%') { | |
722 rv = (*ss->stuff)(ss, fmt - 1, 1); | |
723 if (rv < 0) { | |
724 return rv; | |
725 } | |
726 continue; | |
727 } | |
728 fmt0 = fmt - 1; | |
729 | |
730 /* | |
731 ** Gobble up the % format string. Hopefully we have handled all | |
732 ** of the strange cases! | |
733 */ | |
734 flags = 0; | |
735 c = *fmt++; | |
736 if (c == '%') { | |
737 /* quoting a % with %% */ | |
738 rv = (*ss->stuff)(ss, fmt - 1, 1); | |
739 if (rv < 0) { | |
740 return rv; | |
741 } | |
742 continue; | |
743 } | |
744 | |
745 if( nas != NULL ){ | |
746 /* the fmt contains the Numbered Arguments feature */ | |
747 i = 0; | |
748 while( c && c != '$' ){ /* should imporve error check later
*/ | |
749 i = ( i * 10 ) + ( c - '0' ); | |
750 c = *fmt++; | |
751 } | |
752 | |
753 if( nas[i-1].type == TYPE_UNKNOWN ){ | |
754 if( nas && ( nas != nasArray ) ) | |
755 PR_DELETE( nas ); | |
756 return -1; | |
757 } | |
758 | |
759 nap = &nas[i-1]; | |
760 dolPt = fmt; | |
761 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 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { | |
772 if (c == '-') flags |= FLAG_LEFT; | |
773 if (c == '+') flags |= FLAG_SIGNED; | |
774 if (c == ' ') flags |= FLAG_SPACED; | |
775 if (c == '0') flags |= FLAG_ZEROS; | |
776 c = *fmt++; | |
777 } | |
778 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED; | |
779 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS; | |
780 | |
781 /* width */ | |
782 if (c == '*') { | |
783 c = *fmt++; | |
784 width = va_arg(ap, int); | |
785 } else { | |
786 width = 0; | |
787 while ((c >= '0') && (c <= '9')) { | |
788 width = (width * 10) + (c - '0'); | |
789 c = *fmt++; | |
790 } | |
791 } | |
792 | |
793 /* precision */ | |
794 prec = -1; | |
795 if (c == '.') { | |
796 c = *fmt++; | |
797 if (c == '*') { | |
798 c = *fmt++; | |
799 prec = va_arg(ap, int); | |
800 } else { | |
801 prec = 0; | |
802 while ((c >= '0') && (c <= '9')) { | |
803 prec = (prec * 10) + (c - '0'); | |
804 c = *fmt++; | |
805 } | |
806 } | |
807 } | |
808 | |
809 /* size */ | |
810 type = TYPE_INTN; | |
811 if (c == 'h') { | |
812 type = TYPE_INT16; | |
813 c = *fmt++; | |
814 } else if (c == 'L') { | |
815 /* XXX not quite sure here */ | |
816 type = TYPE_INT64; | |
817 c = *fmt++; | |
818 } else if (c == 'l') { | |
819 type = TYPE_INT32; | |
820 c = *fmt++; | |
821 if (c == 'l') { | |
822 type = TYPE_INT64; | |
823 c = *fmt++; | |
824 } | |
825 } else if (c == 'z') { | |
826 if (sizeof(size_t) == sizeof(PRInt32)) { | |
827 type = TYPE_INT32; | |
828 } else if (sizeof(size_t) == sizeof(PRInt64)) { | |
829 type = TYPE_INT64; | |
830 } | |
831 c = *fmt++; | |
832 } | |
833 | |
834 /* format */ | |
835 hexp = hex; | |
836 switch (c) { | |
837 case 'd': case 'i': /* decimal/integer */ | |
838 radix = 10; | |
839 goto fetch_and_convert; | |
840 | |
841 case 'o': /* octal */ | |
842 radix = 8; | |
843 type |= 1; | |
844 goto fetch_and_convert; | |
845 | |
846 case 'u': /* unsigned decimal */ | |
847 radix = 10; | |
848 type |= 1; | |
849 goto fetch_and_convert; | |
850 | |
851 case 'x': /* unsigned hex */ | |
852 radix = 16; | |
853 type |= 1; | |
854 goto fetch_and_convert; | |
855 | |
856 case 'X': /* unsigned HEX */ | |
857 radix = 16; | |
858 hexp = HEX; | |
859 type |= 1; | |
860 goto fetch_and_convert; | |
861 | |
862 fetch_and_convert: | |
863 switch (type) { | |
864 case TYPE_INT16: | |
865 u.l = nas ? nap->u.i : va_arg(ap, int); | |
866 if (u.l < 0) { | |
867 u.l = -u.l; | |
868 flags |= FLAG_NEG; | |
869 } | |
870 goto do_long; | |
871 case TYPE_UINT16: | |
872 u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff; | |
873 goto do_long; | |
874 case TYPE_INTN: | |
875 u.l = nas ? nap->u.i : va_arg(ap, int); | |
876 if (u.l < 0) { | |
877 u.l = -u.l; | |
878 flags |= FLAG_NEG; | |
879 } | |
880 goto do_long; | |
881 case TYPE_UINTN: | |
882 u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int)); | |
883 goto do_long; | |
884 | |
885 case TYPE_INT32: | |
886 u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32); | |
887 if (u.l < 0) { | |
888 u.l = -u.l; | |
889 flags |= FLAG_NEG; | |
890 } | |
891 goto do_long; | |
892 case TYPE_UINT32: | |
893 u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32)); | |
894 do_long: | |
895 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); | |
896 if (rv < 0) { | |
897 return rv; | |
898 } | |
899 break; | |
900 | |
901 case TYPE_INT64: | |
902 u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64); | |
903 if (!LL_GE_ZERO(u.ll)) { | |
904 LL_NEG(u.ll, u.ll); | |
905 flags |= FLAG_NEG; | |
906 } | |
907 goto do_longlong; | |
908 case TYPE_UINT64: | |
909 u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64); | |
910 do_longlong: | |
911 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); | |
912 if (rv < 0) { | |
913 return rv; | |
914 } | |
915 break; | |
916 } | |
917 break; | |
918 | |
919 case 'e': | |
920 case 'E': | |
921 case 'f': | |
922 case 'g': | |
923 u.d = nas ? nap->u.d : va_arg(ap, double); | |
924 if( nas != NULL ){ | |
925 i = fmt - dolPt; | |
926 if( i < sizeof( pattern ) ){ | |
927 pattern[0] = '%'; | |
928 memcpy( &pattern[1], dolPt, i ); | |
929 rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); | |
930 } | |
931 } else | |
932 rv = cvt_f(ss, u.d, fmt0, fmt); | |
933 | |
934 if (rv < 0) { | |
935 return rv; | |
936 } | |
937 break; | |
938 | |
939 case 'c': | |
940 u.ch = nas ? nap->u.i : va_arg(ap, int); | |
941 if ((flags & FLAG_LEFT) == 0) { | |
942 while (width-- > 1) { | |
943 rv = (*ss->stuff)(ss, " ", 1); | |
944 if (rv < 0) { | |
945 return rv; | |
946 } | |
947 } | |
948 } | |
949 rv = (*ss->stuff)(ss, &u.ch, 1); | |
950 if (rv < 0) { | |
951 return rv; | |
952 } | |
953 if (flags & FLAG_LEFT) { | |
954 while (width-- > 1) { | |
955 rv = (*ss->stuff)(ss, " ", 1); | |
956 if (rv < 0) { | |
957 return rv; | |
958 } | |
959 } | |
960 } | |
961 break; | |
962 | |
963 case 'p': | |
964 if (sizeof(void *) == sizeof(PRInt32)) { | |
965 type = TYPE_UINT32; | |
966 } else if (sizeof(void *) == sizeof(PRInt64)) { | |
967 type = TYPE_UINT64; | |
968 } else if (sizeof(void *) == sizeof(int)) { | |
969 type = TYPE_UINTN; | |
970 } else { | |
971 PR_ASSERT(0); | |
972 break; | |
973 } | |
974 radix = 16; | |
975 goto fetch_and_convert; | |
976 | |
977 #ifndef WIN32 | |
978 case 'S': | |
979 /* XXX not supported I suppose */ | |
980 PR_ASSERT(0); | |
981 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 u.s = nas ? nap->u.s : va_arg(ap, const char*); | |
1018 rv = cvt_s(ss, u.s, width, prec, flags); | |
1019 if (rv < 0) { | |
1020 return rv; | |
1021 } | |
1022 break; | |
1023 | |
1024 case 'n': | |
1025 u.ip = nas ? nap->u.ip : va_arg(ap, int*); | |
1026 if (u.ip) { | |
1027 *u.ip = ss->cur - ss->base; | |
1028 } | |
1029 break; | |
1030 | |
1031 default: | |
1032 /* Not a % token after all... skip it */ | |
1033 #if 0 | |
1034 PR_ASSERT(0); | |
1035 #endif | |
1036 rv = (*ss->stuff)(ss, "%", 1); | |
1037 if (rv < 0) { | |
1038 return rv; | |
1039 } | |
1040 rv = (*ss->stuff)(ss, fmt - 1, 1); | |
1041 if (rv < 0) { | |
1042 return rv; | |
1043 } | |
1044 } | |
1045 } | |
1046 | |
1047 /* Stuff trailing NUL */ | |
1048 rv = (*ss->stuff)(ss, "\0", 1); | |
1049 | |
1050 if( nas && ( nas != nasArray ) ){ | |
1051 PR_DELETE( nas ); | |
1052 } | |
1053 | |
1054 return rv; | |
1055 } | |
1056 | |
1057 /************************************************************************/ | |
1058 | |
1059 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 if (PR_UINT32_MAX - ss->maxlen < len || ss->maxlen + len > PR_INT32_MAX) { | |
1068 return -1; | |
1069 } | |
1070 rv = (*ss->func)(ss->arg, sp, len); | |
1071 if (rv < 0) { | |
1072 return rv; | |
1073 } | |
1074 ss->maxlen += len; | |
1075 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 va_start(ap, fmt); | |
1085 rv = PR_vsxprintf(func, arg, fmt, ap); | |
1086 va_end(ap); | |
1087 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 ss.stuff = FuncStuff; | |
1097 ss.func = func; | |
1098 ss.arg = arg; | |
1099 ss.maxlen = 0; | |
1100 rv = dosprintf(&ss, fmt, ap); | |
1101 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 static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len) | |
1109 { | |
1110 ptrdiff_t off; | |
1111 char *newbase; | |
1112 PRUint32 newlen; | |
1113 | |
1114 off = ss->cur - ss->base; | |
1115 if (PR_UINT32_MAX - len < off) { | |
1116 /* off + len would be too big. */ | |
1117 return -1; | |
1118 } | |
1119 if (off + len >= ss->maxlen) { | |
1120 /* Grow the buffer */ | |
1121 PRUint32 increment = (len > 32) ? len : 32; | |
1122 if (PR_UINT32_MAX - ss->maxlen < increment) { | |
1123 /* ss->maxlen + increment would overflow. */ | |
1124 return -1; | |
1125 } | |
1126 newlen = ss->maxlen + increment; | |
1127 if (newlen > PR_INT32_MAX) { | |
1128 return -1; | |
1129 } | |
1130 if (ss->base) { | |
1131 newbase = (char*) PR_REALLOC(ss->base, newlen); | |
1132 } else { | |
1133 newbase = (char*) PR_MALLOC(newlen); | |
1134 } | |
1135 if (!newbase) { | |
1136 /* Ran out of memory */ | |
1137 return -1; | |
1138 } | |
1139 ss->base = newbase; | |
1140 ss->maxlen = newlen; | |
1141 ss->cur = ss->base + off; | |
1142 } | |
1143 | |
1144 /* Copy data */ | |
1145 while (len) { | |
1146 --len; | |
1147 *ss->cur++ = *sp++; | |
1148 } | |
1149 PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen); | |
1150 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 va_start(ap, fmt); | |
1162 rv = PR_vsmprintf(fmt, ap); | |
1163 va_end(ap); | |
1164 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 PR_DELETE(mem); | |
1173 } | |
1174 | |
1175 PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap) | |
1176 { | |
1177 SprintfState ss; | |
1178 int rv; | |
1179 | |
1180 ss.stuff = GrowStuff; | |
1181 ss.base = 0; | |
1182 ss.cur = 0; | |
1183 ss.maxlen = 0; | |
1184 rv = dosprintf(&ss, fmt, ap); | |
1185 if (rv < 0) { | |
1186 if (ss.base) { | |
1187 PR_DELETE(ss.base); | |
1188 } | |
1189 return 0; | |
1190 } | |
1191 return ss.base; | |
1192 } | |
1193 | |
1194 /* | |
1195 ** Stuff routine that discards overflow data | |
1196 */ | |
1197 static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len) | |
1198 { | |
1199 PRUint32 limit = ss->maxlen - (ss->cur - ss->base); | |
1200 | |
1201 if (len > limit) { | |
1202 len = limit; | |
1203 } | |
1204 while (len) { | |
1205 --len; | |
1206 *ss->cur++ = *sp++; | |
1207 } | |
1208 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 va_start(ap, fmt); | |
1221 rv = PR_vsnprintf(out, outlen, fmt, ap); | |
1222 va_end(ap); | |
1223 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 PR_ASSERT(outlen != 0 && outlen <= PR_INT32_MAX); | |
1233 if (outlen == 0 || outlen > PR_INT32_MAX) { | |
1234 return 0; | |
1235 } | |
1236 | |
1237 ss.stuff = LimitStuff; | |
1238 ss.base = out; | |
1239 ss.cur = out; | |
1240 ss.maxlen = outlen; | |
1241 (void) dosprintf(&ss, fmt, ap); | |
1242 | |
1243 /* If we added chars, and we didn't append a null, do it now. */ | |
1244 if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') ) | |
1245 *(ss.cur - 1) = '\0'; | |
1246 | |
1247 n = ss.cur - ss.base; | |
1248 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 va_start(ap, fmt); | |
1257 rv = PR_vsprintf_append(last, fmt, ap); | |
1258 va_end(ap); | |
1259 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 ss.stuff = GrowStuff; | |
1268 if (last) { | |
1269 size_t lastlen = strlen(last); | |
1270 if (lastlen > PR_INT32_MAX) { | |
1271 return 0; | |
1272 } | |
1273 ss.base = last; | |
1274 ss.cur = last + lastlen; | |
1275 ss.maxlen = lastlen; | |
1276 } else { | |
1277 ss.base = 0; | |
1278 ss.cur = 0; | |
1279 ss.maxlen = 0; | |
1280 } | |
1281 rv = dosprintf(&ss, fmt, ap); | |
1282 if (rv < 0) { | |
1283 if (ss.base) { | |
1284 PR_DELETE(ss.base); | |
1285 } | |
1286 return 0; | |
1287 } | |
1288 return ss.base; | |
1289 } | |
1290 | |
OLD | NEW |