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/. */
8 * Portable safe sprintf code.
10 * Author: Kipp E.B. Hickman
13 #include "double-conversion/double-conversion.h"
14 #include "mozilla/AllocPolicy.h"
15 #include "mozilla/Likely.h"
16 #include "mozilla/Printf.h"
17 #include "mozilla/Sprintf.h"
18 #include "mozilla/UniquePtrExtensions.h"
19 #include "mozilla/Vector.h"
30 using double_conversion::DoubleToStringConverter
;
31 using DTSC
= DoubleToStringConverter
;
34 * Numbered Argument State
37 int type
; // type of the current ap
38 va_list ap
; // point to the corresponding position on ap
41 using NumArgStateVector
=
42 mozilla::Vector
<NumArgState
, 20, mozilla::MallocAllocPolicy
>;
44 // For values up to and including TYPE_DOUBLE, the lowest bit indicates
45 // whether the type is signed (0) or unsigned (1).
52 #define TYPE_LONGLONG 6
53 #define TYPE_ULONGLONG 7
56 #define TYPE_INTSTR 10
57 #define TYPE_POINTER 11
59 # define TYPE_WSTRING 12
63 #define TYPE_UNKNOWN 20
66 #define FLAG_SIGNED 0x2
67 #define FLAG_SPACED 0x4
68 #define FLAG_ZEROS 0x8
71 static const char hex
[] = "0123456789abcdef";
72 static const char HEX
[] = "0123456789ABCDEF";
74 // Fill into the buffer using the data in src
75 bool mozilla::PrintfTarget::fill2(const char* src
, int srclen
, int width
,
80 if (width
> 0 && (flags
& FLAG_LEFT
) == 0) { // Right adjusting
81 if (flags
& FLAG_ZEROS
) {
84 while (--width
>= 0) {
85 if (!emit(&space
, 1)) {
91 // Copy out the source data
92 if (!emit(src
, srclen
)) {
96 if (width
> 0 && (flags
& FLAG_LEFT
) != 0) { // Left adjusting
97 while (--width
>= 0) {
98 if (!emit(&space
, 1)) {
107 * Fill a number. The order is: optional-sign zero-filling conversion-digits
109 bool mozilla::PrintfTarget::fill_n(const char* src
, int srclen
, int width
,
110 int prec
, int type
, int flags
) {
119 if ((type
& 1) == 0) {
120 if (flags
& FLAG_NEG
) {
123 } else if (flags
& FLAG_SIGNED
) {
126 } else if (flags
& FLAG_SPACED
) {
131 cvtwidth
= signwidth
+ srclen
;
133 if (prec
> 0 && (type
!= TYPE_DOUBLE
)) {
135 precwidth
= prec
- srclen
; // Need zero filling
136 cvtwidth
+= precwidth
;
140 if ((flags
& FLAG_ZEROS
) && ((type
== TYPE_DOUBLE
) || (prec
< 0))) {
141 if (width
> cvtwidth
) {
142 zerowidth
= width
- cvtwidth
; // Zero filling
143 cvtwidth
+= zerowidth
;
147 if (flags
& FLAG_LEFT
) {
148 if (width
> cvtwidth
) {
149 // Space filling on the right (i.e. left adjusting)
150 rightspaces
= width
- cvtwidth
;
153 if (width
> cvtwidth
) {
154 // Space filling on the left (i.e. right adjusting)
155 leftspaces
= width
- cvtwidth
;
158 while (--leftspaces
>= 0) {
164 if (!emit(&sign
, 1)) {
168 while (--precwidth
>= 0) {
173 while (--zerowidth
>= 0) {
178 if (!emit(src
, uint32_t(srclen
))) {
181 while (--rightspaces
>= 0) {
189 // All that the cvt_* functions care about as far as the TYPE_* constants is
190 // that the low bit is set to indicate unsigned, or unset to indicate signed.
191 // So we don't try to hard to ensure that the passed TYPE_* constant lines
192 // up with the actual size of the number being printed here. The main printf
193 // code, below, does have to care so that the correct bits are extracted from
195 bool mozilla::PrintfTarget::appendIntDec(int32_t num
) {
202 return cvt_l(n
, -1, -1, 10, TYPE_INTN
, flags
, hex
);
205 bool mozilla::PrintfTarget::appendIntDec(uint32_t num
) {
206 return cvt_l(num
, -1, -1, 10, TYPE_UINTN
, 0, hex
);
209 bool mozilla::PrintfTarget::appendIntOct(uint32_t num
) {
210 return cvt_l(num
, -1, -1, 8, TYPE_UINTN
, 0, hex
);
213 bool mozilla::PrintfTarget::appendIntHex(uint32_t num
) {
214 return cvt_l(num
, -1, -1, 16, TYPE_UINTN
, 0, hex
);
217 bool mozilla::PrintfTarget::appendIntDec(int64_t num
) {
223 return cvt_ll(num
, -1, -1, 10, TYPE_INTN
, flags
, hex
);
226 bool mozilla::PrintfTarget::appendIntDec(uint64_t num
) {
227 return cvt_ll(num
, -1, -1, 10, TYPE_UINTN
, 0, hex
);
230 bool mozilla::PrintfTarget::appendIntOct(uint64_t num
) {
231 return cvt_ll(num
, -1, -1, 8, TYPE_UINTN
, 0, hex
);
234 bool mozilla::PrintfTarget::appendIntHex(uint64_t num
) {
235 return cvt_ll(num
, -1, -1, 16, TYPE_UINTN
, 0, hex
);
238 /* Convert a long into its printable form. */
239 bool mozilla::PrintfTarget::cvt_l(long num
, int width
, int prec
, int radix
,
240 int type
, int flags
, const char* hexp
) {
245 // according to the man page this needs to happen
246 if ((prec
== 0) && (num
== 0)) {
247 return fill_n("", 0, width
, prec
, type
, flags
);
250 // Converting decimal is a little tricky. In the unsigned case we
251 // need to stop when we hit 10 digits. In the signed case, we can
252 // stop when the number is zero.
253 cvt
= cvtbuf
+ sizeof(cvtbuf
);
256 int digit
= (((unsigned long)num
) % radix
) & 0xF;
257 *--cvt
= hexp
[digit
];
259 num
= (long)(((unsigned long)num
) / radix
);
266 // Now that we have the number converted without its sign, deal with
267 // the sign and zero padding.
268 return fill_n(cvt
, digits
, width
, prec
, type
, flags
);
271 /* Convert a 64-bit integer into its printable form. */
272 bool mozilla::PrintfTarget::cvt_ll(int64_t num
, int width
, int prec
, int radix
,
273 int type
, int flags
, const char* hexp
) {
274 // According to the man page, this needs to happen.
275 if (prec
== 0 && num
== 0) {
276 return fill_n("", 0, width
, prec
, type
, flags
);
279 // Converting decimal is a little tricky. In the unsigned case we
280 // need to stop when we hit 10 digits. In the signed case, we can
281 // stop when the number is zero.
282 int64_t rad
= int64_t(radix
);
284 char* cvt
= cvtbuf
+ sizeof(cvtbuf
);
287 int64_t quot
= uint64_t(num
) / rad
;
288 int64_t rem
= uint64_t(num
) % rad
;
289 int32_t digit
= int32_t(rem
);
290 *--cvt
= hexp
[digit
& 0xf];
299 // Now that we have the number converted without its sign, deal with
300 // the sign and zero padding.
301 return fill_n(cvt
, digits
, width
, prec
, type
, flags
);
305 constexpr static size_t lengthof(const char (&)[N
]) {
309 // Longest possible output from ToFixed for positive numbers:
310 // [0-9]{kMaxFixedDigitsBeforePoint}\.[0-9]{kMaxFixedDigitsAfterPoint}?
311 constexpr int FIXED_MAX_CHARS
=
312 DTSC::kMaxFixedDigitsBeforePoint
+ 1 + DTSC::kMaxFixedDigitsAfterPoint
;
314 // Longest possible output from ToExponential:
315 // [1-9]\.[0-9]{kMaxExponentialDigits}e[+-][0-9]{1,3}
316 // (std::numeric_limits<double>::max() has exponent 308).
317 constexpr int EXPONENTIAL_MAX_CHARS
=
318 lengthof("1.") + DTSC::kMaxExponentialDigits
+ lengthof("e+999");
320 // Longest possible output from ToPrecise:
321 // [0-9\.]{kMaxPrecisionDigits+1} or
322 // [1-9]\.[0-9]{kMaxPrecisionDigits-1}e[+-][0-9]{1,3}
323 constexpr int PRECISE_MAX_CHARS
=
324 lengthof("1.") + DTSC::kMaxPrecisionDigits
- 1 + lengthof("e+999");
326 constexpr int DTSC_MAX_CHARS
=
327 std::max({FIXED_MAX_CHARS
, EXPONENTIAL_MAX_CHARS
, PRECISE_MAX_CHARS
});
330 * Convert a double precision floating point number into its printable
333 bool mozilla::PrintfTarget::cvt_f(double d
, char c
, int width
, int prec
,
335 bool lower
= islower(c
);
336 const char* inf
= lower
? "inf" : "INF";
337 const char* nan
= lower
? "nan" : "NAN";
338 char e
= lower
? 'e' : 'E';
339 DoubleToStringConverter
converter(DTSC::UNIQUE_ZERO
| DTSC::NO_TRAILING_ZERO
|
340 DTSC::EMIT_POSITIVE_EXPONENT_SIGN
,
341 inf
, nan
, e
, 0, 0, 4, 0, 2);
342 // Longest of the above cases, plus space for a terminal nul character.
343 char buf
[DTSC_MAX_CHARS
+ 1];
344 double_conversion::StringBuilder
builder(buf
, sizeof(buf
));
345 bool success
= false;
346 if (std::signbit(d
)) {
350 if (!std::isfinite(d
)) {
351 flags
&= ~FLAG_ZEROS
;
353 // "If the precision is missing, it shall be taken as 6."
360 success
= converter
.ToExponential(d
, prec
, &builder
);
364 success
= converter
.ToFixed(d
, prec
, &builder
);
368 // "If an explicit precision is zero, it shall be taken as 1."
369 success
= converter
.ToPrecision(d
, prec
? prec
: 1, &builder
);
375 int len
= builder
.position();
376 char* cvt
= builder
.Finalize();
377 return fill_n(cvt
, len
, width
, prec
, TYPE_DOUBLE
, flags
);
381 * Convert a string into its printable form. "width" is the output
382 * width. "prec" is the maximum number of characters of "s" to output,
383 * where -1 means until NUL.
385 bool mozilla::PrintfTarget::cvt_s(const char* s
, int width
, int prec
,
394 // Limit string length by precision value
395 int slen
= int(strlen(s
));
396 if (0 < prec
&& prec
< slen
) {
401 return fill2(s
, slen
, width
, flags
);
405 * BuildArgArray stands for Numbered Argument list Sprintf
407 * fmp = "%4$i, %2$d, %3s, %1d";
408 * the number must start from 1, and no gap among them
410 static bool BuildArgArray(const char* fmt
, va_list ap
, NumArgStateVector
& nas
) {
411 size_t number
= 0, cn
= 0, i
;
416 // Detemine how many legal % I have got, then allocate space.
420 while ((c
= *p
++) != 0) {
424 if ((c
= *p
++) == '%') { // skip %% case
429 if (c
> '9' || c
< '0') {
430 if (c
== '$') { // numbered argument case
432 MOZ_CRASH("Bad format string");
435 } else { // non-numbered argument case
437 MOZ_CRASH("Bad format string");
452 // Only allow a limited number of arguments.
453 MOZ_RELEASE_ASSERT(number
<= 20);
455 if (!nas
.growByUninitialized(number
)) {
459 for (i
= 0; i
< number
; i
++) {
460 nas
[i
].type
= TYPE_UNKNOWN
;
467 while ((c
= *p
++) != 0) {
477 while (c
&& c
!= '$') { // should improve error check later
478 cn
= cn
* 10 + c
- '0';
482 if (!c
|| cn
< 1 || cn
> number
) {
483 MOZ_CRASH("Bad format string");
486 // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
488 if (nas
[cn
].type
!= TYPE_UNKNOWN
) {
495 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
501 // not supported feature, for the argument is not numbered
502 MOZ_CRASH("Bad format string");
505 while ((c
>= '0') && (c
<= '9')) {
513 // not supported feature, for the argument is not numbered
514 MOZ_CRASH("Bad format string");
517 while ((c
>= '0') && (c
<= '9')) {
523 nas
[cn
].type
= TYPE_INTN
;
525 nas
[cn
].type
= TYPE_SHORT
;
528 nas
[cn
].type
= TYPE_SCHAR
;
531 } else if (c
== 'L') {
532 nas
[cn
].type
= TYPE_LONGLONG
;
534 } else if (c
== 'l') {
535 nas
[cn
].type
= TYPE_LONG
;
538 nas
[cn
].type
= TYPE_LONGLONG
;
541 } else if (c
== 'z' || c
== 'I') {
542 static_assert(sizeof(size_t) == sizeof(int) ||
543 sizeof(size_t) == sizeof(long) ||
544 sizeof(size_t) == sizeof(long long),
545 "size_t is not one of the expected sizes");
546 nas
[cn
].type
= sizeof(size_t) == sizeof(int) ? TYPE_INTN
547 : sizeof(size_t) == sizeof(long) ? TYPE_LONG
550 } else if (c
== 't') {
551 static_assert(sizeof(ptrdiff_t) == sizeof(int) ||
552 sizeof(ptrdiff_t) == sizeof(long) ||
553 sizeof(ptrdiff_t) == sizeof(long long),
554 "ptrdiff_t is not one of the expected sizes");
555 nas
[cn
].type
= sizeof(ptrdiff_t) == sizeof(int) ? TYPE_INTN
556 : sizeof(ptrdiff_t) == sizeof(long) ? TYPE_LONG
559 } else if (c
== 'j') {
560 static_assert(sizeof(intmax_t) == sizeof(int) ||
561 sizeof(intmax_t) == sizeof(long) ||
562 sizeof(intmax_t) == sizeof(long long),
563 "intmax_t is not one of the expected sizes");
564 nas
[cn
].type
= sizeof(intmax_t) == sizeof(int) ? TYPE_INTN
565 : sizeof(intmax_t) == sizeof(long) ? TYPE_LONG
581 // Mark as unsigned type.
591 nas
[cn
].type
= TYPE_DOUBLE
;
595 nas
[cn
].type
= TYPE_POINTER
;
600 nas
[cn
].type
= TYPE_WSTRING
;
603 nas
[cn
].type
= TYPE_UNKNOWN
;
609 if (nas
[cn
].type
== TYPE_LONG
) {
610 nas
[cn
].type
= TYPE_WSTRING
;
614 // Other type sizes are not supported here.
615 MOZ_ASSERT(nas
[cn
].type
== TYPE_INTN
);
616 nas
[cn
].type
= TYPE_STRING
;
620 nas
[cn
].type
= TYPE_INTSTR
;
625 nas
[cn
].type
= TYPE_UNKNOWN
;
630 if (nas
[cn
].type
== TYPE_UNKNOWN
) {
631 MOZ_CRASH("Bad format string");
639 while (cn
< number
) {
640 // A TYPE_UNKNOWN here means that the format asked for a
641 // positional argument without specifying the meaning of some
643 MOZ_ASSERT(nas
[cn
].type
!= TYPE_UNKNOWN
);
645 va_copy(nas
[cn
].ap
, ap
);
647 switch (nas
[cn
].type
) {
654 (void)va_arg(ap
, int);
657 (void)va_arg(ap
, long);
660 (void)va_arg(ap
, unsigned long);
663 (void)va_arg(ap
, long long);
666 (void)va_arg(ap
, unsigned long long);
669 (void)va_arg(ap
, char*);
672 (void)va_arg(ap
, int*);
675 (void)va_arg(ap
, double);
678 (void)va_arg(ap
, void*);
682 (void)va_arg(ap
, wchar_t*);
696 mozilla::PrintfTarget::PrintfTarget() : mEmitted(0) {}
698 bool mozilla::PrintfTarget::vprint(const char* fmt
, va_list ap
) {
700 int flags
, width
, prec
, radix
, type
;
717 // Build an argument array, IF the fmt is numbered argument
718 // list style, to contain the Numbered Argument list pointers.
720 NumArgStateVector nas
;
721 if (!BuildArgArray(fmt
, ap
, nas
)) {
722 // the fmt contains error Numbered Argument format, jliu@netscape.com
723 MOZ_CRASH("Bad format string");
726 while ((c
= *fmt
++) != 0) {
728 if (!emit(fmt
- 1, 1)) {
735 // Gobble up the % format string. Hopefully we have handled all
736 // of the strange cases!
740 // quoting a % with %%
741 if (!emit(fmt
- 1, 1)) {
749 // the fmt contains the Numbered Arguments feature
751 while (c
&& c
!= '$') { // should improve error check later
752 i
= (i
* 10) + (c
- '0');
756 if (nas
[i
- 1].type
== TYPE_UNKNOWN
) {
757 MOZ_CRASH("Bad format string");
764 // Examine optional flags. Note that we do not implement the
765 // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
766 // somewhat ambiguous and not ideal, which is perhaps why
767 // the various sprintf() implementations are inconsistent
769 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
774 flags
|= FLAG_SIGNED
;
777 flags
|= FLAG_SPACED
;
784 if (flags
& FLAG_SIGNED
) {
785 flags
&= ~FLAG_SPACED
;
787 if (flags
& FLAG_LEFT
) {
788 flags
&= ~FLAG_ZEROS
;
794 width
= va_arg(ap
, int);
798 flags
&= ~FLAG_ZEROS
;
802 while ((c
>= '0') && (c
<= '9')) {
803 width
= (width
* 10) + (c
- '0');
814 prec
= va_arg(ap
, int);
817 while ((c
>= '0') && (c
<= '9')) {
818 prec
= (prec
* 10) + (c
- '0');
833 } else if (c
== 'L') {
834 type
= TYPE_LONGLONG
;
836 } else if (c
== 'l') {
840 type
= TYPE_LONGLONG
;
843 } else if (c
== 'z' || c
== 'I') {
844 static_assert(sizeof(size_t) == sizeof(int) ||
845 sizeof(size_t) == sizeof(long) ||
846 sizeof(size_t) == sizeof(long long),
847 "size_t is not one of the expected sizes");
848 type
= sizeof(size_t) == sizeof(int) ? TYPE_INTN
849 : sizeof(size_t) == sizeof(long) ? TYPE_LONG
852 } else if (c
== 't') {
853 static_assert(sizeof(ptrdiff_t) == sizeof(int) ||
854 sizeof(ptrdiff_t) == sizeof(long) ||
855 sizeof(ptrdiff_t) == sizeof(long long),
856 "ptrdiff_t is not one of the expected sizes");
857 type
= sizeof(ptrdiff_t) == sizeof(int) ? TYPE_INTN
858 : sizeof(ptrdiff_t) == sizeof(long) ? TYPE_LONG
861 } else if (c
== 'j') {
862 static_assert(sizeof(intmax_t) == sizeof(int) ||
863 sizeof(intmax_t) == sizeof(long) ||
864 sizeof(intmax_t) == sizeof(long long),
865 "intmax_t is not one of the expected sizes");
866 type
= sizeof(intmax_t) == sizeof(int) ? TYPE_INTN
867 : sizeof(intmax_t) == sizeof(long) ? TYPE_LONG
876 case 'i': // decimal/integer
878 goto fetch_and_convert
;
883 goto fetch_and_convert
;
885 case 'u': // unsigned decimal
888 goto fetch_and_convert
;
890 case 'x': // unsigned hex
893 goto fetch_and_convert
;
895 case 'X': // unsigned HEX
899 goto fetch_and_convert
;
904 u
.l
= (signed char)va_arg(ap
, int);
911 u
.l
= (unsigned char)va_arg(ap
, unsigned int);
914 u
.l
= (short)va_arg(ap
, int);
921 u
.l
= (unsigned short)va_arg(ap
, unsigned int);
924 u
.l
= va_arg(ap
, int);
931 u
.l
= (long)va_arg(ap
, unsigned int);
935 u
.l
= va_arg(ap
, long);
942 u
.l
= (long)va_arg(ap
, unsigned long);
944 if (!cvt_l(u
.l
, width
, prec
, radix
, type
, flags
, hexp
)) {
951 u
.ll
= va_arg(ap
, long long);
958 u
.ll
= (uintptr_t)va_arg(ap
, void*);
961 u
.ll
= va_arg(ap
, unsigned long long);
963 if (!cvt_ll(u
.ll
, width
, prec
, radix
, type
, flags
, hexp
)) {
977 u
.d
= va_arg(ap
, double);
978 if (!cvt_f(u
.d
, c
, width
, prec
, flags
)) {
985 if ((flags
& FLAG_LEFT
) == 0) {
986 while (width
-- > 1) {
995 u
.ch
= va_arg(ap
, int);
996 if (!emit(&u
.ch
, 1)) {
1001 if (flags
& FLAG_LEFT
) {
1002 while (width
-- > 1) {
1003 if (!emit(" ", 1)) {
1011 type
= TYPE_POINTER
;
1013 goto fetch_and_convert
;
1016 if (type
== TYPE_INTN
) {
1017 u
.s
= va_arg(ap
, const char*);
1018 if (!cvt_s(u
.s
, width
, prec
, flags
)) {
1023 MOZ_ASSERT(type
== TYPE_LONG
);
1028 u
.ws
= va_arg(ap
, const wchar_t*);
1030 int rv
= WideCharToMultiByte(CP_ACP
, 0, u
.ws
, -1, NULL
, 0, NULL
, NULL
);
1031 if (rv
== 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION
) {
1032 if (!cvt_s("<unicode errors in string>", width
, prec
, flags
)) {
1039 UniqueFreePtr
<char[]> buf((char*)malloc(rv
));
1040 WideCharToMultiByte(CP_ACP
, 0, u
.ws
, -1, buf
.get(), rv
, NULL
, NULL
);
1043 if (!cvt_s(buf
.get(), width
, prec
, flags
)) {
1049 // Not supported here.
1055 u
.ip
= va_arg(ap
, int*);
1062 // Not a % token after all... skip it
1063 if (!emit("%", 1)) {
1066 if (!emit(fmt
- 1, 1)) {
1075 /************************************************************************/
1077 bool mozilla::PrintfTarget::print(const char* format
, ...) {
1080 va_start(ap
, format
);
1081 bool result
= vprint(format
, ap
);
1092 #undef TYPE_LONGLONG
1093 #undef TYPE_ULONGLONG