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