/src/mozilla-central/xpcom/string/nsTextFormatter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
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 | | * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7 |
11 | | * |
12 | | * Contributor(s): |
13 | | * Kipp E.B. Hickman <kipp@netscape.com> (original author) |
14 | | * Frank Yung-Fong Tang <ftang@netscape.com> |
15 | | * Daniele Nicolodi <daniele@grinta.net> |
16 | | */ |
17 | | |
18 | | /* |
19 | | * Copied from xpcom/ds/nsTextFormatter.cpp r1.22 |
20 | | * Changed to use nsMemory and Frozen linkage |
21 | | * -- Prasad <prasad@medhas.org> |
22 | | */ |
23 | | |
24 | | #include <stddef.h> |
25 | | #include <stdio.h> |
26 | | #include <string.h> |
27 | | #include "prdtoa.h" |
28 | | #include "mozilla/Logging.h" |
29 | | #include "mozilla/Sprintf.h" |
30 | | #include "nsCRTGlue.h" |
31 | | #include "nsTextFormatter.h" |
32 | | #include "nsMemory.h" |
33 | | |
34 | | struct nsTextFormatter::SprintfStateStr |
35 | | { |
36 | | int (*stuff)(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen); |
37 | | |
38 | | char16_t* base; |
39 | | char16_t* cur; |
40 | | uint32_t maxlen; |
41 | | |
42 | | void* stuffclosure; |
43 | | }; |
44 | | |
45 | 48 | #define _LEFT 0x1 |
46 | 48 | #define _SIGNED 0x2 |
47 | 24 | #define _SPACED 0x4 |
48 | 24 | #define _ZEROS 0x8 |
49 | 24 | #define _NEG 0x10 |
50 | 48 | #define _UNSIGNED 0x20 |
51 | | |
52 | 24 | #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0])) |
53 | | |
54 | | /* |
55 | | ** Fill into the buffer using the data in src |
56 | | */ |
57 | | int |
58 | | nsTextFormatter::fill2(SprintfStateStr* aState, const char16_t* aSrc, int aSrcLen, int aWidth, |
59 | | int aFlags) |
60 | 0 | { |
61 | 0 | char16_t space = ' '; |
62 | 0 | int rv; |
63 | 0 |
|
64 | 0 | aWidth -= aSrcLen; |
65 | 0 | /* Right adjusting */ |
66 | 0 | if ((aWidth > 0) && ((aFlags & _LEFT) == 0)) { |
67 | 0 | if (aFlags & _ZEROS) { |
68 | 0 | space = '0'; |
69 | 0 | } |
70 | 0 | while (--aWidth >= 0) { |
71 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
72 | 0 | if (rv < 0) { |
73 | 0 | return rv; |
74 | 0 | } |
75 | 0 | } |
76 | 0 | } |
77 | 0 |
|
78 | 0 | /* Copy out the source data */ |
79 | 0 | rv = (*aState->stuff)(aState, aSrc, aSrcLen); |
80 | 0 | if (rv < 0) { |
81 | 0 | return rv; |
82 | 0 | } |
83 | 0 | |
84 | 0 | /* Left adjusting */ |
85 | 0 | if ((aWidth > 0) && ((aFlags & _LEFT) != 0)) { |
86 | 0 | while (--aWidth >= 0) { |
87 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
88 | 0 | if (rv < 0) { |
89 | 0 | return rv; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | } |
93 | 0 | return 0; |
94 | 0 | } |
95 | | |
96 | | /* |
97 | | ** Fill a number. The order is: optional-sign zero-filling conversion-digits |
98 | | */ |
99 | | int |
100 | | nsTextFormatter::fill_n(nsTextFormatter::SprintfStateStr* aState, const char16_t* aSrc, |
101 | | int aSrcLen, int aWidth, int aPrec, int aFlags) |
102 | 24 | { |
103 | 24 | int zerowidth = 0; |
104 | 24 | int precwidth = 0; |
105 | 24 | int signwidth = 0; |
106 | 24 | int leftspaces = 0; |
107 | 24 | int rightspaces = 0; |
108 | 24 | int cvtwidth; |
109 | 24 | int rv; |
110 | 24 | char16_t sign; |
111 | 24 | char16_t space = ' '; |
112 | 24 | char16_t zero = '0'; |
113 | 24 | |
114 | 24 | if ((aFlags & _UNSIGNED) == 0) { |
115 | 24 | if (aFlags & _NEG) { |
116 | 0 | sign = '-'; |
117 | 0 | signwidth = 1; |
118 | 24 | } else if (aFlags & _SIGNED) { |
119 | 0 | sign = '+'; |
120 | 0 | signwidth = 1; |
121 | 24 | } else if (aFlags & _SPACED) { |
122 | 0 | sign = ' '; |
123 | 0 | signwidth = 1; |
124 | 0 | } |
125 | 24 | } |
126 | 24 | cvtwidth = signwidth + aSrcLen; |
127 | 24 | |
128 | 24 | if (aPrec > 0) { |
129 | 0 | if (aPrec > aSrcLen) { |
130 | 0 | /* Need zero filling */ |
131 | 0 | precwidth = aPrec - aSrcLen; |
132 | 0 | cvtwidth += precwidth; |
133 | 0 | } |
134 | 0 | } |
135 | 24 | |
136 | 24 | if ((aFlags & _ZEROS) && (aPrec < 0)) { |
137 | 0 | if (aWidth > cvtwidth) { |
138 | 0 | /* Zero filling */ |
139 | 0 | zerowidth = aWidth - cvtwidth; |
140 | 0 | cvtwidth += zerowidth; |
141 | 0 | } |
142 | 0 | } |
143 | 24 | |
144 | 24 | if (aFlags & _LEFT) { |
145 | 0 | if (aWidth > cvtwidth) { |
146 | 0 | /* Space filling on the right (i.e. left adjusting) */ |
147 | 0 | rightspaces = aWidth - cvtwidth; |
148 | 0 | } |
149 | 24 | } else { |
150 | 24 | if (aWidth > cvtwidth) { |
151 | 0 | /* Space filling on the left (i.e. right adjusting) */ |
152 | 0 | leftspaces = aWidth - cvtwidth; |
153 | 0 | } |
154 | 24 | } |
155 | 24 | while (--leftspaces >= 0) { |
156 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
157 | 0 | if (rv < 0) { |
158 | 0 | return rv; |
159 | 0 | } |
160 | 0 | } |
161 | 24 | if (signwidth) { |
162 | 0 | rv = (*aState->stuff)(aState, &sign, 1); |
163 | 0 | if (rv < 0) { |
164 | 0 | return rv; |
165 | 0 | } |
166 | 24 | } |
167 | 24 | while (--precwidth >= 0) { |
168 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
169 | 0 | if (rv < 0) { |
170 | 0 | return rv; |
171 | 0 | } |
172 | 0 | } |
173 | 24 | while (--zerowidth >= 0) { |
174 | 0 | rv = (*aState->stuff)(aState, &zero, 1); |
175 | 0 | if (rv < 0) { |
176 | 0 | return rv; |
177 | 0 | } |
178 | 0 | } |
179 | 24 | rv = (*aState->stuff)(aState, aSrc, aSrcLen); |
180 | 24 | if (rv < 0) { |
181 | 0 | return rv; |
182 | 0 | } |
183 | 24 | while (--rightspaces >= 0) { |
184 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
185 | 0 | if (rv < 0) { |
186 | 0 | return rv; |
187 | 0 | } |
188 | 0 | } |
189 | 24 | return 0; |
190 | 24 | } |
191 | | |
192 | | /* |
193 | | ** Convert a 64-bit integer into its printable form |
194 | | */ |
195 | | int |
196 | | nsTextFormatter::cvt_ll(SprintfStateStr* aState, uint64_t aNum, int aWidth, int aPrec, int aRadix, |
197 | | int aFlags, const char16_t* aHexStr) |
198 | 24 | { |
199 | 24 | char16_t cvtbuf[100]; |
200 | 24 | char16_t* cvt; |
201 | 24 | int digits; |
202 | 24 | |
203 | 24 | /* according to the man page this needs to happen */ |
204 | 24 | if (aPrec == 0 && aNum == 0) { |
205 | 0 | return 0; |
206 | 0 | } |
207 | 24 | |
208 | 24 | /* |
209 | 24 | ** Converting decimal is a little tricky. In the unsigned case we |
210 | 24 | ** need to stop when we hit 10 digits. In the signed case, we can |
211 | 24 | ** stop when the number is zero. |
212 | 24 | */ |
213 | 24 | cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf); |
214 | 24 | digits = 0; |
215 | 60 | while (aNum != 0) { |
216 | 36 | uint64_t quot = aNum / aRadix; |
217 | 36 | uint64_t rem = aNum % aRadix; |
218 | 36 | *--cvt = aHexStr[rem & 0xf]; |
219 | 36 | digits++; |
220 | 36 | aNum = quot; |
221 | 36 | } |
222 | 24 | if (digits == 0) { |
223 | 0 | *--cvt = '0'; |
224 | 0 | digits++; |
225 | 0 | } |
226 | 24 | |
227 | 24 | /* |
228 | 24 | ** Now that we have the number converted without its sign, deal with |
229 | 24 | ** the sign and zero padding. |
230 | 24 | */ |
231 | 24 | return fill_n(aState, cvt, digits, aWidth, aPrec, aFlags); |
232 | 24 | } |
233 | | |
234 | | /* |
235 | | ** Convert a double precision floating point number into its printable |
236 | | ** form. |
237 | | */ |
238 | | int |
239 | | nsTextFormatter::cvt_f(SprintfStateStr* aState, double aDouble, int aWidth, int aPrec, |
240 | | const char16_t aType, int aFlags) |
241 | 0 | { |
242 | 0 | int mode = 2; |
243 | 0 | int decpt; |
244 | 0 | int sign; |
245 | 0 | char buf[256]; |
246 | 0 | char* bufp = buf; |
247 | 0 | int bufsz = 256; |
248 | 0 | char num[256]; |
249 | 0 | char* nump; |
250 | 0 | char* endnum; |
251 | 0 | int numdigits = 0; |
252 | 0 | char exp = 'e'; |
253 | 0 |
|
254 | 0 | if (aPrec == -1) { |
255 | 0 | aPrec = 6; |
256 | 0 | } else if (aPrec > 50) { |
257 | 0 | // limit precision to avoid PR_dtoa bug 108335 |
258 | 0 | // and to prevent buffers overflows |
259 | 0 | aPrec = 50; |
260 | 0 | } |
261 | 0 |
|
262 | 0 | switch (aType) { |
263 | 0 | case 'f': |
264 | 0 | numdigits = aPrec; |
265 | 0 | mode = 3; |
266 | 0 | break; |
267 | 0 | case 'E': |
268 | 0 | exp = 'E'; |
269 | 0 | MOZ_FALLTHROUGH; |
270 | 0 | case 'e': |
271 | 0 | numdigits = aPrec + 1; |
272 | 0 | mode = 2; |
273 | 0 | break; |
274 | 0 | case 'G': |
275 | 0 | exp = 'E'; |
276 | 0 | MOZ_FALLTHROUGH; |
277 | 0 | case 'g': |
278 | 0 | if (aPrec == 0) { |
279 | 0 | aPrec = 1; |
280 | 0 | } |
281 | 0 | numdigits = aPrec; |
282 | 0 | mode = 2; |
283 | 0 | break; |
284 | 0 | default: |
285 | 0 | NS_ERROR("invalid aType passed to cvt_f"); |
286 | 0 | } |
287 | 0 |
|
288 | 0 | if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign, |
289 | 0 | &endnum, num, bufsz) == PR_FAILURE) { |
290 | 0 | buf[0] = '\0'; |
291 | 0 | return -1; |
292 | 0 | } |
293 | 0 | numdigits = endnum - num; |
294 | 0 | nump = num; |
295 | 0 |
|
296 | 0 | if (sign) { |
297 | 0 | *bufp++ = '-'; |
298 | 0 | } else if (aFlags & _SIGNED) { |
299 | 0 | *bufp++ = '+'; |
300 | 0 | } |
301 | 0 |
|
302 | 0 | if (decpt == 9999) { |
303 | 0 | while ((*bufp++ = *nump++)) { |
304 | 0 | } |
305 | 0 | } else { |
306 | 0 |
|
307 | 0 | switch (aType) { |
308 | 0 |
|
309 | 0 | case 'E': |
310 | 0 | case 'e': |
311 | 0 |
|
312 | 0 | *bufp++ = *nump++; |
313 | 0 | if (aPrec > 0) { |
314 | 0 | *bufp++ = '.'; |
315 | 0 | while (*nump) { |
316 | 0 | *bufp++ = *nump++; |
317 | 0 | aPrec--; |
318 | 0 | } |
319 | 0 | while (aPrec-- > 0) { |
320 | 0 | *bufp++ = '0'; |
321 | 0 | } |
322 | 0 | } |
323 | 0 | *bufp++ = exp; |
324 | 0 |
|
325 | 0 | ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1); |
326 | 0 | break; |
327 | 0 |
|
328 | 0 | case 'f': |
329 | 0 |
|
330 | 0 | if (decpt < 1) { |
331 | 0 | *bufp++ = '0'; |
332 | 0 | if (aPrec > 0) { |
333 | 0 | *bufp++ = '.'; |
334 | 0 | while (decpt++ && aPrec-- > 0) { |
335 | 0 | *bufp++ = '0'; |
336 | 0 | } |
337 | 0 | while (*nump && aPrec-- > 0) { |
338 | 0 | *bufp++ = *nump++; |
339 | 0 | } |
340 | 0 | while (aPrec-- > 0) { |
341 | 0 | *bufp++ = '0'; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } else { |
345 | 0 | while (*nump && decpt-- > 0) { |
346 | 0 | *bufp++ = *nump++; |
347 | 0 | } |
348 | 0 | while (decpt-- > 0) { |
349 | 0 | *bufp++ = '0'; |
350 | 0 | } |
351 | 0 | if (aPrec > 0) { |
352 | 0 | *bufp++ = '.'; |
353 | 0 | while (*nump && aPrec-- > 0) { |
354 | 0 | *bufp++ = *nump++; |
355 | 0 | } |
356 | 0 | while (aPrec-- > 0) { |
357 | 0 | *bufp++ = '0'; |
358 | 0 | } |
359 | 0 | } |
360 | 0 | } |
361 | 0 | *bufp = '\0'; |
362 | 0 | break; |
363 | 0 |
|
364 | 0 | case 'G': |
365 | 0 | case 'g': |
366 | 0 |
|
367 | 0 | if ((decpt < -3) || ((decpt - 1) >= aPrec)) { |
368 | 0 | *bufp++ = *nump++; |
369 | 0 | numdigits--; |
370 | 0 | if (numdigits > 0) { |
371 | 0 | *bufp++ = '.'; |
372 | 0 | while (*nump) { |
373 | 0 | *bufp++ = *nump++; |
374 | 0 | } |
375 | 0 | } |
376 | 0 | *bufp++ = exp; |
377 | 0 | ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1); |
378 | 0 | } else { |
379 | 0 | if (decpt < 1) { |
380 | 0 | *bufp++ = '0'; |
381 | 0 | if (aPrec > 0) { |
382 | 0 | *bufp++ = '.'; |
383 | 0 | while (decpt++) { |
384 | 0 | *bufp++ = '0'; |
385 | 0 | } |
386 | 0 | while (*nump) { |
387 | 0 | *bufp++ = *nump++; |
388 | 0 | } |
389 | 0 | } |
390 | 0 | } else { |
391 | 0 | while (*nump && decpt-- > 0) { |
392 | 0 | *bufp++ = *nump++; |
393 | 0 | numdigits--; |
394 | 0 | } |
395 | 0 | while (decpt-- > 0) { |
396 | 0 | *bufp++ = '0'; |
397 | 0 | } |
398 | 0 | if (numdigits > 0) { |
399 | 0 | *bufp++ = '.'; |
400 | 0 | while (*nump) { |
401 | 0 | *bufp++ = *nump++; |
402 | 0 | } |
403 | 0 | } |
404 | 0 | } |
405 | 0 | *bufp = '\0'; |
406 | 0 | } |
407 | 0 | } |
408 | 0 | } |
409 | 0 |
|
410 | 0 | char16_t rbuf[256]; |
411 | 0 | char16_t* rbufp = rbuf; |
412 | 0 | bufp = buf; |
413 | 0 | // cast to char16_t |
414 | 0 | while ((*rbufp++ = *bufp++)) { |
415 | 0 | } |
416 | 0 | *rbufp = '\0'; |
417 | 0 |
|
418 | 0 | return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags); |
419 | 0 | } |
420 | | |
421 | | /* |
422 | | ** Convert a string into its printable form. |aWidth| is the output |
423 | | ** width. |aPrec| is the maximum number of characters of |aStr| to output, |
424 | | ** where -1 means until NUL. |
425 | | */ |
426 | | int |
427 | | nsTextFormatter::cvt_S(SprintfStateStr* aState, const char16_t* aStr, int aWidth, int aPrec, |
428 | | int aFlags) |
429 | 0 | { |
430 | 0 | int slen; |
431 | 0 |
|
432 | 0 | if (aPrec == 0) { |
433 | 0 | return 0; |
434 | 0 | } |
435 | 0 | |
436 | 0 | /* Limit string length by precision value */ |
437 | 0 | slen = aStr ? NS_strlen(aStr) : 6; |
438 | 0 | if (aPrec > 0) { |
439 | 0 | if (aPrec < slen) { |
440 | 0 | slen = aPrec; |
441 | 0 | } |
442 | 0 | } |
443 | 0 |
|
444 | 0 | /* and away we go */ |
445 | 0 | return fill2(aState, aStr ? aStr : u"(null)", slen, aWidth, aFlags); |
446 | 0 | } |
447 | | |
448 | | /* |
449 | | ** Convert a string into its printable form. |aWidth| is the output |
450 | | ** width. |aPrec| is the maximum number of characters of |aStr| to output, |
451 | | ** where -1 means until NUL. |
452 | | */ |
453 | | int |
454 | | nsTextFormatter::cvt_s(nsTextFormatter::SprintfStateStr* aState, const char* aStr, int aWidth, |
455 | | int aPrec, int aFlags) |
456 | 0 | { |
457 | 0 | // Be sure to handle null the same way as %S. |
458 | 0 | if (aStr == nullptr) { |
459 | 0 | return cvt_S(aState, nullptr, aWidth, aPrec, aFlags); |
460 | 0 | } |
461 | 0 | NS_ConvertUTF8toUTF16 utf16Val(aStr); |
462 | 0 | return cvt_S(aState, utf16Val.get(), aWidth, aPrec, aFlags); |
463 | 0 | } |
464 | | |
465 | | /* |
466 | | ** The workhorse sprintf code. |
467 | | */ |
468 | | int |
469 | | nsTextFormatter::dosprintf(SprintfStateStr* aState, const char16_t* aFmt, |
470 | | mozilla::Span<BoxedValue> aValues) |
471 | 12 | { |
472 | 12 | static const char16_t space = ' '; |
473 | 12 | static const char16_t hex[] = u"0123456789abcdef"; |
474 | 12 | static const char16_t HEX[] = u"0123456789ABCDEF"; |
475 | 12 | static const BoxedValue emptyString(u""); |
476 | 12 | |
477 | 12 | char16_t c; |
478 | 12 | int flags, width, prec, radix; |
479 | 12 | |
480 | 12 | const char16_t* hexp; |
481 | 12 | |
482 | 12 | // Next argument for non-numbered arguments. |
483 | 12 | size_t nextNaturalArg = 0; |
484 | 12 | // True if we ever saw a numbered argument. |
485 | 12 | bool sawNumberedArg = false; |
486 | 12 | |
487 | 48 | while ((c = *aFmt++) != 0) { |
488 | 36 | int rv; |
489 | 36 | |
490 | 36 | if (c != '%') { |
491 | 12 | rv = (*aState->stuff)(aState, aFmt - 1, 1); |
492 | 12 | if (rv < 0) { |
493 | 0 | return rv; |
494 | 0 | } |
495 | 12 | continue; |
496 | 12 | } |
497 | 24 | |
498 | 24 | // Save the location of the "%" in case we decide it isn't a |
499 | 24 | // format and want to just emit the text from the format string. |
500 | 24 | const char16_t* percentPointer = aFmt - 1; |
501 | 24 | |
502 | 24 | /* |
503 | 24 | ** Gobble up the % format string. Hopefully we have handled all |
504 | 24 | ** of the strange cases! |
505 | 24 | */ |
506 | 24 | flags = 0; |
507 | 24 | c = *aFmt++; |
508 | 24 | if (c == '%') { |
509 | 0 | /* quoting a % with %% */ |
510 | 0 | rv = (*aState->stuff)(aState, aFmt - 1, 1); |
511 | 0 | if (rv < 0) { |
512 | 0 | return rv; |
513 | 0 | } |
514 | 0 | continue; |
515 | 0 | } |
516 | 24 | |
517 | 24 | // Check for a numbered argument. |
518 | 24 | bool sawWidth = false; |
519 | 24 | const BoxedValue* thisArg = nullptr; |
520 | 24 | if (c >= '0' && c <= '9') { |
521 | 0 | size_t argNumber = 0; |
522 | 0 | while (c && c >= '0' && c <= '9') { |
523 | 0 | argNumber = (argNumber * 10) + (c - '0'); |
524 | 0 | c = *aFmt++; |
525 | 0 | } |
526 | 0 |
|
527 | 0 | if (c == '$') { |
528 | 0 | // Mixing numbered arguments and implicit arguments is |
529 | 0 | // disallowed. |
530 | 0 | if (nextNaturalArg > 0) { |
531 | 0 | return -1; |
532 | 0 | } |
533 | 0 | |
534 | 0 | c = *aFmt++; |
535 | 0 |
|
536 | 0 | // Numbered arguments start at 1. |
537 | 0 | --argNumber; |
538 | 0 | if (argNumber >= aValues.Length()) { |
539 | 0 | // A correctness issue but not a safety issue. |
540 | 0 | MOZ_ASSERT(false); |
541 | 0 | thisArg = &emptyString; |
542 | 0 | } else { |
543 | 0 | thisArg = &aValues[argNumber]; |
544 | 0 | } |
545 | 0 | sawNumberedArg = true; |
546 | 0 | } else { |
547 | 0 | width = argNumber; |
548 | 0 | sawWidth = true; |
549 | 0 | } |
550 | 0 | } |
551 | 24 | |
552 | 24 | if (!sawWidth) { |
553 | 24 | /* |
554 | 24 | * Examine optional flags. Note that we do not implement the |
555 | 24 | * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is |
556 | 24 | * somewhat ambiguous and not ideal, which is perhaps why |
557 | 24 | * the various sprintf() implementations are inconsistent |
558 | 24 | * on this feature. |
559 | 24 | */ |
560 | 24 | while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { |
561 | 0 | if (c == '-') { |
562 | 0 | flags |= _LEFT; |
563 | 0 | } |
564 | 0 | if (c == '+') { |
565 | 0 | flags |= _SIGNED; |
566 | 0 | } |
567 | 0 | if (c == ' ') { |
568 | 0 | flags |= _SPACED; |
569 | 0 | } |
570 | 0 | if (c == '0') { |
571 | 0 | flags |= _ZEROS; |
572 | 0 | } |
573 | 0 | c = *aFmt++; |
574 | 0 | } |
575 | 24 | if (flags & _SIGNED) { |
576 | 0 | flags &= ~_SPACED; |
577 | 0 | } |
578 | 24 | if (flags & _LEFT) { |
579 | 0 | flags &= ~_ZEROS; |
580 | 0 | } |
581 | 24 | |
582 | 24 | /* width */ |
583 | 24 | if (c == '*') { |
584 | 0 | // Not supported with numbered arguments. |
585 | 0 | if (sawNumberedArg) { |
586 | 0 | return -1; |
587 | 0 | } |
588 | 0 | |
589 | 0 | if (nextNaturalArg >= aValues.Length() || !aValues[nextNaturalArg].IntCompatible()) { |
590 | 0 | // A correctness issue but not a safety issue. |
591 | 0 | MOZ_ASSERT(false); |
592 | 0 | width = 0; |
593 | 0 | } else { |
594 | 0 | width = aValues[nextNaturalArg++].mValue.mInt; |
595 | 0 | } |
596 | 0 | c = *aFmt++; |
597 | 24 | } else { |
598 | 24 | width = 0; |
599 | 24 | while ((c >= '0') && (c <= '9')) { |
600 | 0 | width = (width * 10) + (c - '0'); |
601 | 0 | c = *aFmt++; |
602 | 0 | } |
603 | 24 | } |
604 | 24 | } |
605 | 24 | |
606 | 24 | /* precision */ |
607 | 24 | prec = -1; |
608 | 24 | if (c == '.') { |
609 | 0 | c = *aFmt++; |
610 | 0 | if (c == '*') { |
611 | 0 | // Not supported with numbered arguments. |
612 | 0 | if (sawNumberedArg) { |
613 | 0 | return -1; |
614 | 0 | } |
615 | 0 | |
616 | 0 | if (nextNaturalArg >= aValues.Length() || !aValues[nextNaturalArg].IntCompatible()) { |
617 | 0 | // A correctness issue but not a safety issue. |
618 | 0 | MOZ_ASSERT(false); |
619 | 0 | } else { |
620 | 0 | prec = aValues[nextNaturalArg++].mValue.mInt; |
621 | 0 | } |
622 | 0 | c = *aFmt++; |
623 | 0 | } else { |
624 | 0 | prec = 0; |
625 | 0 | while ((c >= '0') && (c <= '9')) { |
626 | 0 | prec = (prec * 10) + (c - '0'); |
627 | 0 | c = *aFmt++; |
628 | 0 | } |
629 | 0 | } |
630 | 0 | } |
631 | 24 | |
632 | 24 | // If the argument isn't known yet, find it now. This is done |
633 | 24 | // after the width and precision code, in case '*' was used. |
634 | 24 | if (thisArg == nullptr) { |
635 | 24 | // Mixing numbered arguments and implicit arguments is |
636 | 24 | // disallowed. |
637 | 24 | if (sawNumberedArg) { |
638 | 0 | return -1; |
639 | 0 | } |
640 | 24 | |
641 | 24 | if (nextNaturalArg >= aValues.Length()) { |
642 | 0 | // A correctness issue but not a safety issue. |
643 | 0 | MOZ_ASSERT(false); |
644 | 0 | thisArg = &emptyString; |
645 | 24 | } else { |
646 | 24 | thisArg = &aValues[nextNaturalArg++]; |
647 | 24 | } |
648 | 24 | } |
649 | 24 | |
650 | 24 | /* Size. Defaults to 32 bits. */ |
651 | 24 | uint64_t mask = UINT32_MAX; |
652 | 24 | if (c == 'h') { |
653 | 0 | c = *aFmt++; |
654 | 0 | mask = UINT16_MAX; |
655 | 24 | } else if (c == 'L') { |
656 | 0 | c = *aFmt++; |
657 | 0 | mask = UINT64_MAX; |
658 | 24 | } else if (c == 'l') { |
659 | 24 | c = *aFmt++; |
660 | 24 | if (c == 'l') { |
661 | 0 | c = *aFmt++; |
662 | 0 | mask = UINT64_MAX; |
663 | 24 | } else { |
664 | 24 | mask = UINT32_MAX; |
665 | 24 | } |
666 | 24 | } |
667 | 24 | |
668 | 24 | /* format */ |
669 | 24 | hexp = hex; |
670 | 24 | radix = 10; |
671 | 24 | // Several `MOZ_ASSERT`s below check for argument compatibility |
672 | 24 | // with the format specifier. These are only debug assertions, |
673 | 24 | // not release assertions, and exist to catch problems in C++ |
674 | 24 | // callers of `nsTextFormatter`, as we do not have compile-time |
675 | 24 | // checking of format strings. In release mode, these assertions |
676 | 24 | // will be no-ops, and we will fall through to printing the |
677 | 24 | // argument based on the known type of the argument. |
678 | 24 | switch (c) { |
679 | 24 | case 'd': |
680 | 24 | case 'i': /* decimal/integer */ |
681 | 24 | MOZ_ASSERT(thisArg->IntCompatible()); |
682 | 24 | break; |
683 | 24 | |
684 | 24 | case 'o': /* octal */ |
685 | 0 | MOZ_ASSERT(thisArg->IntCompatible()); |
686 | 0 | radix = 8; |
687 | 0 | flags |= _UNSIGNED; |
688 | 0 | break; |
689 | 24 | |
690 | 24 | case 'u': /* unsigned decimal */ |
691 | 0 | MOZ_ASSERT(thisArg->IntCompatible()); |
692 | 0 | radix = 10; |
693 | 0 | flags |= _UNSIGNED; |
694 | 0 | break; |
695 | 24 | |
696 | 24 | case 'x': /* unsigned hex */ |
697 | 0 | MOZ_ASSERT(thisArg->IntCompatible()); |
698 | 0 | radix = 16; |
699 | 0 | flags |= _UNSIGNED; |
700 | 0 | break; |
701 | 24 | |
702 | 24 | case 'X': /* unsigned HEX */ |
703 | 0 | MOZ_ASSERT(thisArg->IntCompatible()); |
704 | 0 | radix = 16; |
705 | 0 | hexp = HEX; |
706 | 0 | flags |= _UNSIGNED; |
707 | 0 | break; |
708 | 24 | |
709 | 24 | case 'e': |
710 | 0 | case 'E': |
711 | 0 | case 'f': |
712 | 0 | case 'g': |
713 | 0 | case 'G': |
714 | 0 | MOZ_ASSERT(thisArg->mKind == DOUBLE); |
715 | 0 | // Type-based printing below. |
716 | 0 | break; |
717 | 0 |
|
718 | 0 | case 'S': |
719 | 0 | MOZ_ASSERT(thisArg->mKind == STRING16); |
720 | 0 | // Type-based printing below. |
721 | 0 | break; |
722 | 0 |
|
723 | 0 | case 's': |
724 | 0 | MOZ_ASSERT(thisArg->mKind == STRING); |
725 | 0 | // Type-based printing below. |
726 | 0 | break; |
727 | 0 |
|
728 | 0 | case 'c': { |
729 | 0 | if (!thisArg->IntCompatible()) { |
730 | 0 | MOZ_ASSERT(false); |
731 | 0 | // Type-based printing below. |
732 | 0 | break; |
733 | 0 | } |
734 | 0 |
|
735 | 0 | if ((flags & _LEFT) == 0) { |
736 | 0 | while (width-- > 1) { |
737 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
738 | 0 | if (rv < 0) { |
739 | 0 | return rv; |
740 | 0 | } |
741 | 0 | } |
742 | 0 | } |
743 | 0 | char16_t ch = thisArg->mValue.mInt; |
744 | 0 | rv = (*aState->stuff)(aState, &ch, 1); |
745 | 0 | if (rv < 0) { |
746 | 0 | return rv; |
747 | 0 | } |
748 | 0 | if (flags & _LEFT) { |
749 | 0 | while (width-- > 1) { |
750 | 0 | rv = (*aState->stuff)(aState, &space, 1); |
751 | 0 | if (rv < 0) { |
752 | 0 | return rv; |
753 | 0 | } |
754 | 0 | } |
755 | 0 | } |
756 | 0 | } |
757 | 0 | continue; |
758 | 0 |
|
759 | 0 | case 'p': |
760 | 0 | if (!thisArg->PointerCompatible()) { |
761 | 0 | MOZ_ASSERT(false); |
762 | 0 | break; |
763 | 0 | } |
764 | 0 | static_assert(sizeof(uint64_t) >= sizeof(void*), "pointers are larger than 64 bits"); |
765 | 0 | rv = cvt_ll(aState, uintptr_t(thisArg->mValue.mPtr), width, prec, 16, flags | _UNSIGNED, |
766 | 0 | hexp); |
767 | 0 | if (rv < 0) { |
768 | 0 | return rv; |
769 | 0 | } |
770 | 0 | continue; |
771 | 0 |
|
772 | 0 | case 'n': |
773 | 0 | if (thisArg->mKind != INTPOINTER) { |
774 | 0 | return -1; |
775 | 0 | } |
776 | 0 | |
777 | 0 | if (thisArg->mValue.mIntPtr != nullptr) { |
778 | 0 | *thisArg->mValue.mIntPtr = aState->cur - aState->base; |
779 | 0 | } |
780 | 0 | continue; |
781 | 0 |
|
782 | 0 | default: |
783 | 0 | /* Not a % token after all... skip it */ |
784 | 0 | rv = (*aState->stuff)(aState, percentPointer, aFmt - percentPointer); |
785 | 0 | if (rv < 0) { |
786 | 0 | return rv; |
787 | 0 | } |
788 | 0 | continue; |
789 | 24 | } |
790 | 24 | |
791 | 24 | // If we get here, we want to handle the argument according to its |
792 | 24 | // actual type; modified by the flags as appropriate. |
793 | 24 | switch (thisArg->mKind) { |
794 | 24 | case INT: |
795 | 24 | case UINT: { |
796 | 24 | int64_t val = thisArg->mValue.mInt; |
797 | 24 | if ((flags & _UNSIGNED) == 0 && val < 0) { |
798 | 0 | val = -val; |
799 | 0 | flags |= _NEG; |
800 | 0 | } |
801 | 24 | rv = cvt_ll(aState, uint64_t(val) & mask, width, prec, radix, flags, hexp); |
802 | 24 | } |
803 | 24 | break; |
804 | 24 | case INTPOINTER: |
805 | 0 | case POINTER: |
806 | 0 | // Always treat these as unsigned hex, no matter the format. |
807 | 0 | static_assert(sizeof(uint64_t) >= sizeof(void*), "pointers are larger than 64 bits"); |
808 | 0 | rv = cvt_ll(aState, uintptr_t(thisArg->mValue.mPtr), width, prec, 16, flags | _UNSIGNED, |
809 | 0 | hexp); |
810 | 0 | break; |
811 | 0 | case DOUBLE: |
812 | 0 | if (c != 'f' && c != 'E' && c != 'e' && c != 'G' && c != 'g') { |
813 | 0 | // Pick some default. |
814 | 0 | c = 'g'; |
815 | 0 | } |
816 | 0 | rv = cvt_f(aState, thisArg->mValue.mDouble, width, prec, c, flags); |
817 | 0 | break; |
818 | 0 | case STRING: |
819 | 0 | rv = cvt_s(aState, thisArg->mValue.mString, width, prec, flags); |
820 | 0 | break; |
821 | 0 | case STRING16: |
822 | 0 | rv = cvt_S(aState, thisArg->mValue.mString16, width, prec, flags); |
823 | 0 | break; |
824 | 0 | default: |
825 | 0 | // Can't happen. |
826 | 0 | MOZ_ASSERT(0); |
827 | 24 | } |
828 | 24 | |
829 | 24 | if (rv < 0) { |
830 | 0 | return rv; |
831 | 0 | } |
832 | 24 | } |
833 | 12 | |
834 | 12 | return 0; |
835 | 12 | } |
836 | | |
837 | | /************************************************************************/ |
838 | | |
839 | | int |
840 | | nsTextFormatter::StringStuff(nsTextFormatter::SprintfStateStr* aState, const char16_t* aStr, |
841 | | uint32_t aLen) |
842 | 36 | { |
843 | 36 | ptrdiff_t off = aState->cur - aState->base; |
844 | 36 | |
845 | 36 | nsAString* str = static_cast<nsAString*>(aState->stuffclosure); |
846 | 36 | str->Append(aStr, aLen); |
847 | 36 | |
848 | 36 | aState->base = str->BeginWriting(); |
849 | 36 | aState->cur = aState->base + off; |
850 | 36 | |
851 | 36 | return 0; |
852 | 36 | } |
853 | | |
854 | | void |
855 | | nsTextFormatter::vssprintf(nsAString& aOut, const char16_t* aFmt, |
856 | | mozilla::Span<BoxedValue> aValues) |
857 | 12 | { |
858 | 12 | SprintfStateStr ss; |
859 | 12 | ss.stuff = StringStuff; |
860 | 12 | ss.base = 0; |
861 | 12 | ss.cur = 0; |
862 | 12 | ss.maxlen = 0; |
863 | 12 | ss.stuffclosure = &aOut; |
864 | 12 | |
865 | 12 | aOut.Truncate(); |
866 | 12 | dosprintf(&ss, aFmt, aValues); |
867 | 12 | } |
868 | | |
869 | | /* |
870 | | ** Stuff routine that discards overflow data |
871 | | */ |
872 | | int |
873 | | nsTextFormatter::LimitStuff(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen) |
874 | 0 | { |
875 | 0 | uint32_t limit = aState->maxlen - (aState->cur - aState->base); |
876 | 0 |
|
877 | 0 | if (aLen > limit) { |
878 | 0 | aLen = limit; |
879 | 0 | } |
880 | 0 | while (aLen) { |
881 | 0 | --aLen; |
882 | 0 | *aState->cur++ = *aStr++; |
883 | 0 | } |
884 | 0 | return 0; |
885 | 0 | } |
886 | | |
887 | | uint32_t |
888 | | nsTextFormatter::vsnprintf(char16_t* aOut, uint32_t aOutLen, |
889 | | const char16_t* aFmt, mozilla::Span<BoxedValue> aValues) |
890 | 0 | { |
891 | 0 | SprintfStateStr ss; |
892 | 0 |
|
893 | 0 | MOZ_ASSERT((int32_t)aOutLen > 0); |
894 | 0 | if ((int32_t)aOutLen <= 0) { |
895 | 0 | return 0; |
896 | 0 | } |
897 | 0 | |
898 | 0 | ss.stuff = LimitStuff; |
899 | 0 | ss.base = aOut; |
900 | 0 | ss.cur = aOut; |
901 | 0 | ss.maxlen = aOutLen; |
902 | 0 | int result = dosprintf(&ss, aFmt, aValues); |
903 | 0 |
|
904 | 0 | if (ss.cur == ss.base) { |
905 | 0 | return 0; |
906 | 0 | } |
907 | 0 | |
908 | 0 | // Append a NUL. However, be sure not to count it in the returned |
909 | 0 | // length. |
910 | 0 | if (ss.cur - ss.base >= ptrdiff_t(ss.maxlen)) { |
911 | 0 | --ss.cur; |
912 | 0 | } |
913 | 0 | *ss.cur = '\0'; |
914 | 0 |
|
915 | 0 | // Check the result now, so that an unterminated string can't |
916 | 0 | // possibly escape. |
917 | 0 | if (result < 0) { |
918 | 0 | return -1; |
919 | 0 | } |
920 | 0 | |
921 | 0 | return ss.cur - ss.base; |
922 | 0 | } |