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 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
13 * Kipp E.B. Hickman <kipp@netscape.com> (original author)
14 * Frank Yung-Fong Tang <ftang@netscape.com>
15 * Daniele Nicolodi <daniele@grinta.net>
22 #include "mozilla/Logging.h"
23 #include "mozilla/Sprintf.h"
24 #include "nsCRTGlue.h"
25 #include "nsTextFormatter.h"
27 struct nsTextFormatter::SprintfStateStr
{
28 int (*stuff
)(SprintfStateStr
* aState
, const char16_t
* aStr
, uint32_t aLen
);
42 #define _UNSIGNED 0x20
44 #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
47 ** Fill into the buffer using the data in src
49 int nsTextFormatter::fill2(SprintfStateStr
* aState
, const char16_t
* aSrc
,
50 int aSrcLen
, int aWidth
, int aFlags
) {
56 if ((aWidth
> 0) && ((aFlags
& _LEFT
) == 0)) {
57 if (aFlags
& _ZEROS
) {
60 while (--aWidth
>= 0) {
61 rv
= (*aState
->stuff
)(aState
, &space
, 1);
68 /* Copy out the source data */
69 rv
= (*aState
->stuff
)(aState
, aSrc
, aSrcLen
);
75 if ((aWidth
> 0) && ((aFlags
& _LEFT
) != 0)) {
76 while (--aWidth
>= 0) {
77 rv
= (*aState
->stuff
)(aState
, &space
, 1);
87 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
89 int nsTextFormatter::fill_n(nsTextFormatter::SprintfStateStr
* aState
,
90 const char16_t
* aSrc
, int aSrcLen
, int aWidth
,
91 int aPrec
, int aFlags
) {
100 char16_t space
= ' ';
103 if ((aFlags
& _UNSIGNED
) == 0) {
107 } else if (aFlags
& _SIGNED
) {
110 } else if (aFlags
& _SPACED
) {
115 cvtwidth
= signwidth
+ aSrcLen
;
118 if (aPrec
> aSrcLen
) {
119 /* Need zero filling */
120 precwidth
= aPrec
- aSrcLen
;
121 cvtwidth
+= precwidth
;
125 if ((aFlags
& _ZEROS
) && (aPrec
< 0)) {
126 if (aWidth
> cvtwidth
) {
128 zerowidth
= aWidth
- cvtwidth
;
129 cvtwidth
+= zerowidth
;
133 if (aFlags
& _LEFT
) {
134 if (aWidth
> cvtwidth
) {
135 /* Space filling on the right (i.e. left adjusting) */
136 rightspaces
= aWidth
- cvtwidth
;
139 if (aWidth
> cvtwidth
) {
140 /* Space filling on the left (i.e. right adjusting) */
141 leftspaces
= aWidth
- cvtwidth
;
144 while (--leftspaces
>= 0) {
145 rv
= (*aState
->stuff
)(aState
, &space
, 1);
151 rv
= (*aState
->stuff
)(aState
, &sign
, 1);
156 while (--precwidth
>= 0) {
157 rv
= (*aState
->stuff
)(aState
, &space
, 1);
162 while (--zerowidth
>= 0) {
163 rv
= (*aState
->stuff
)(aState
, &zero
, 1);
168 rv
= (*aState
->stuff
)(aState
, aSrc
, aSrcLen
);
172 while (--rightspaces
>= 0) {
173 rv
= (*aState
->stuff
)(aState
, &space
, 1);
182 ** Convert a 64-bit integer into its printable form
184 int nsTextFormatter::cvt_ll(SprintfStateStr
* aState
, uint64_t aNum
, int aWidth
,
185 int aPrec
, int aRadix
, int aFlags
,
186 const char16_t
* aHexStr
) {
187 char16_t cvtbuf
[100];
191 /* according to the man page this needs to happen */
192 if (aPrec
== 0 && aNum
== 0) {
197 ** Converting decimal is a little tricky. In the unsigned case we
198 ** need to stop when we hit 10 digits. In the signed case, we can
199 ** stop when the number is zero.
201 cvt
= &cvtbuf
[0] + ELEMENTS_OF(cvtbuf
);
204 uint64_t quot
= aNum
/ aRadix
;
205 uint64_t rem
= aNum
% aRadix
;
206 *--cvt
= aHexStr
[rem
& 0xf];
216 ** Now that we have the number converted without its sign, deal with
217 ** the sign and zero padding.
219 return fill_n(aState
, cvt
, digits
, aWidth
, aPrec
, aFlags
);
223 ** Convert a double precision floating point number into its printable
226 int nsTextFormatter::cvt_f(SprintfStateStr
* aState
, double aDouble
, int aWidth
,
227 int aPrec
, const char16_t aType
, int aFlags
) {
242 } else if (aPrec
> 50) {
243 // limit precision to avoid PR_dtoa bug 108335
244 // and to prevent buffers overflows
257 numdigits
= aPrec
+ 1;
271 NS_ERROR("invalid aType passed to cvt_f");
274 if (PR_dtoa(aDouble
, mode
, numdigits
, &decpt
, &sign
, &endnum
, num
, bufsz
) ==
279 numdigits
= endnum
- num
;
284 } else if (aFlags
& _SIGNED
) {
289 while ((*bufp
++ = *nump
++)) {
303 while (aPrec
-- > 0) {
309 ::snprintf(bufp
, bufsz
- (bufp
- buf
), "%+03d", decpt
- 1);
318 while (decpt
++ && aPrec
-- > 0) {
321 while (*nump
&& aPrec
-- > 0) {
324 while (aPrec
-- > 0) {
329 while (*nump
&& decpt
-- > 0) {
332 while (decpt
-- > 0) {
337 while (*nump
&& aPrec
-- > 0) {
340 while (aPrec
-- > 0) {
351 if ((decpt
< -3) || ((decpt
- 1) >= aPrec
)) {
361 ::snprintf(bufp
, bufsz
- (bufp
- buf
), "%+03d", decpt
- 1);
375 while (*nump
&& decpt
-- > 0) {
379 while (decpt
-- > 0) {
395 char16_t
* rbufp
= rbuf
;
398 while ((*rbufp
++ = *bufp
++)) {
402 return fill2(aState
, rbuf
, NS_strlen(rbuf
), aWidth
, aFlags
);
406 ** Convert a string into its printable form. |aWidth| is the output
407 ** width. |aPrec| is the maximum number of characters of |aStr| to output,
408 ** where -1 means until NUL.
410 int nsTextFormatter::cvt_S(SprintfStateStr
* aState
, const char16_t
* aStr
,
411 int aWidth
, int aPrec
, int aFlags
) {
418 /* Limit string length by precision value */
419 slen
= aStr
? NS_strlen(aStr
) : 6;
427 return fill2(aState
, aStr
? aStr
: u
"(null)", slen
, aWidth
, aFlags
);
431 ** Convert a string into its printable form. |aWidth| is the output
432 ** width. |aPrec| is the maximum number of characters of |aStr| to output,
433 ** where -1 means until NUL.
435 int nsTextFormatter::cvt_s(nsTextFormatter::SprintfStateStr
* aState
,
436 const char* aStr
, int aWidth
, int aPrec
,
438 // Be sure to handle null the same way as %S.
439 if (aStr
== nullptr) {
440 return cvt_S(aState
, nullptr, aWidth
, aPrec
, aFlags
);
442 NS_ConvertUTF8toUTF16
utf16Val(aStr
);
443 return cvt_S(aState
, utf16Val
.get(), aWidth
, aPrec
, aFlags
);
447 ** The workhorse sprintf code.
449 int nsTextFormatter::dosprintf(SprintfStateStr
* aState
, const char16_t
* aFmt
,
450 mozilla::Span
<BoxedValue
> aValues
) {
451 static const char16_t space
= ' ';
452 static const char16_t hex
[] = u
"0123456789abcdef";
453 static const char16_t HEX
[] = u
"0123456789ABCDEF";
454 static const BoxedValue
emptyString(u
"");
457 int flags
, width
, prec
, radix
;
459 const char16_t
* hexp
;
461 // Next argument for non-numbered arguments.
462 size_t nextNaturalArg
= 0;
463 // True if we ever saw a numbered argument.
464 bool sawNumberedArg
= false;
466 while ((c
= *aFmt
++) != 0) {
470 rv
= (*aState
->stuff
)(aState
, aFmt
- 1, 1);
477 // Save the location of the "%" in case we decide it isn't a
478 // format and want to just emit the text from the format string.
479 const char16_t
* percentPointer
= aFmt
- 1;
482 ** Gobble up the % format string. Hopefully we have handled all
483 ** of the strange cases!
488 /* quoting a % with %% */
489 rv
= (*aState
->stuff
)(aState
, aFmt
- 1, 1);
496 // Check for a numbered argument.
497 bool sawWidth
= false;
498 const BoxedValue
* thisArg
= nullptr;
499 if (c
>= '0' && c
<= '9') {
500 size_t argNumber
= 0;
501 while (c
&& c
>= '0' && c
<= '9') {
502 argNumber
= (argNumber
* 10) + (c
- '0');
507 // Mixing numbered arguments and implicit arguments is
509 if (nextNaturalArg
> 0) {
515 // Numbered arguments start at 1.
517 if (argNumber
>= aValues
.Length()) {
518 // A correctness issue but not a safety issue.
520 thisArg
= &emptyString
;
522 thisArg
= &aValues
[argNumber
];
524 sawNumberedArg
= true;
533 * Examine optional flags. Note that we do not implement the
534 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
535 * somewhat ambiguous and not ideal, which is perhaps why
536 * the various sprintf() implementations are inconsistent
539 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
554 if (flags
& _SIGNED
) {
563 // Not supported with numbered arguments.
564 if (sawNumberedArg
) {
568 if (nextNaturalArg
>= aValues
.Length() ||
569 !aValues
[nextNaturalArg
].IntCompatible()) {
570 // A correctness issue but not a safety issue.
574 width
= aValues
[nextNaturalArg
++].mValue
.mInt
;
579 while ((c
>= '0') && (c
<= '9')) {
580 width
= (width
* 10) + (c
- '0');
591 // Not supported with numbered arguments.
592 if (sawNumberedArg
) {
596 if (nextNaturalArg
>= aValues
.Length() ||
597 !aValues
[nextNaturalArg
].IntCompatible()) {
598 // A correctness issue but not a safety issue.
601 prec
= aValues
[nextNaturalArg
++].mValue
.mInt
;
606 while ((c
>= '0') && (c
<= '9')) {
607 prec
= (prec
* 10) + (c
- '0');
613 // If the argument isn't known yet, find it now. This is done
614 // after the width and precision code, in case '*' was used.
615 if (thisArg
== nullptr) {
616 // Mixing numbered arguments and implicit arguments is
618 if (sawNumberedArg
) {
622 if (nextNaturalArg
>= aValues
.Length()) {
623 // A correctness issue but not a safety issue.
625 thisArg
= &emptyString
;
627 thisArg
= &aValues
[nextNaturalArg
++];
631 /* Size. Defaults to 32 bits. */
632 uint64_t mask
= UINT32_MAX
;
636 } else if (c
== 'L') {
639 } else if (c
== 'l') {
652 // Several `MOZ_ASSERT`s below check for argument compatibility
653 // with the format specifier. These are only debug assertions,
654 // not release assertions, and exist to catch problems in C++
655 // callers of `nsTextFormatter`, as we do not have compile-time
656 // checking of format strings. In release mode, these assertions
657 // will be no-ops, and we will fall through to printing the
658 // argument based on the known type of the argument.
661 case 'i': /* decimal/integer */
662 MOZ_ASSERT(thisArg
->IntCompatible());
665 case 'o': /* octal */
666 MOZ_ASSERT(thisArg
->IntCompatible());
671 case 'u': /* unsigned decimal */
672 MOZ_ASSERT(thisArg
->IntCompatible());
677 case 'x': /* unsigned hex */
678 MOZ_ASSERT(thisArg
->IntCompatible());
683 case 'X': /* unsigned HEX */
684 MOZ_ASSERT(thisArg
->IntCompatible());
695 MOZ_ASSERT(thisArg
->mKind
== DOUBLE
);
696 // Type-based printing below.
701 MOZ_ASSERT(thisArg
->mKind
== STRING
|| thisArg
->mKind
== STRING16
);
702 // Type-based printing below.
706 if (!thisArg
->IntCompatible()) {
708 // Type-based printing below.
712 if ((flags
& _LEFT
) == 0) {
713 while (width
-- > 1) {
714 rv
= (*aState
->stuff
)(aState
, &space
, 1);
720 char16_t ch
= thisArg
->mValue
.mInt
;
721 rv
= (*aState
->stuff
)(aState
, &ch
, 1);
726 while (width
-- > 1) {
727 rv
= (*aState
->stuff
)(aState
, &space
, 1);
737 if (!thisArg
->PointerCompatible()) {
741 static_assert(sizeof(uint64_t) >= sizeof(void*),
742 "pointers are larger than 64 bits");
743 rv
= cvt_ll(aState
, uintptr_t(thisArg
->mValue
.mPtr
), width
, prec
, 16,
744 flags
| _UNSIGNED
, hexp
);
751 if (thisArg
->mKind
!= INTPOINTER
) {
755 if (thisArg
->mValue
.mIntPtr
!= nullptr) {
756 *thisArg
->mValue
.mIntPtr
= aState
->cur
- aState
->base
;
761 /* Not a % token after all... skip it */
762 rv
= (*aState
->stuff
)(aState
, percentPointer
, aFmt
- percentPointer
);
769 // If we get here, we want to handle the argument according to its
770 // actual type; modified by the flags as appropriate.
771 switch (thisArg
->mKind
) {
774 int64_t val
= thisArg
->mValue
.mInt
;
775 if ((flags
& _UNSIGNED
) == 0 && val
< 0) {
779 rv
= cvt_ll(aState
, uint64_t(val
) & mask
, width
, prec
, radix
, flags
,
784 // Always treat these as unsigned hex, no matter the format.
785 static_assert(sizeof(uint64_t) >= sizeof(void*),
786 "pointers are larger than 64 bits");
787 rv
= cvt_ll(aState
, uintptr_t(thisArg
->mValue
.mPtr
), width
, prec
, 16,
788 flags
| _UNSIGNED
, hexp
);
791 if (c
!= 'f' && c
!= 'E' && c
!= 'e' && c
!= 'G' && c
!= 'g') {
792 // Pick some default.
795 rv
= cvt_f(aState
, thisArg
->mValue
.mDouble
, width
, prec
, c
, flags
);
798 rv
= cvt_s(aState
, thisArg
->mValue
.mString
, width
, prec
, flags
);
801 rv
= cvt_S(aState
, thisArg
->mValue
.mString16
, width
, prec
, flags
);
816 /************************************************************************/
818 int nsTextFormatter::StringStuff(nsTextFormatter::SprintfStateStr
* aState
,
819 const char16_t
* aStr
, uint32_t aLen
) {
820 ptrdiff_t off
= aState
->cur
- aState
->base
;
822 nsAString
* str
= static_cast<nsAString
*>(aState
->stuffclosure
);
823 str
->Append(aStr
, aLen
);
825 aState
->base
= str
->BeginWriting();
826 aState
->cur
= aState
->base
+ off
;
831 void nsTextFormatter::vssprintf(nsAString
& aOut
, const char16_t
* aFmt
,
832 mozilla::Span
<BoxedValue
> aValues
) {
834 ss
.stuff
= StringStuff
;
838 ss
.stuffclosure
= &aOut
;
841 dosprintf(&ss
, aFmt
, aValues
);
845 ** Stuff routine that discards overflow data
847 int nsTextFormatter::LimitStuff(SprintfStateStr
* aState
, const char16_t
* aStr
,
849 uint32_t limit
= aState
->maxlen
- (aState
->cur
- aState
->base
);
856 *aState
->cur
++ = *aStr
++;
861 uint32_t nsTextFormatter::vsnprintf(char16_t
* aOut
, uint32_t aOutLen
,
862 const char16_t
* aFmt
,
863 mozilla::Span
<BoxedValue
> aValues
) {
866 MOZ_ASSERT((int32_t)aOutLen
> 0);
867 if ((int32_t)aOutLen
<= 0) {
871 ss
.stuff
= LimitStuff
;
875 int result
= dosprintf(&ss
, aFmt
, aValues
);
877 if (ss
.cur
== ss
.base
) {
881 // Append a NUL. However, be sure not to count it in the returned
883 if (ss
.cur
- ss
.base
>= ptrdiff_t(ss
.maxlen
)) {
888 // Check the result now, so that an unterminated string can't
894 return ss
.cur
- ss
.base
;