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>
19 * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
20 * Changed to use nsMemory and Frozen linkage
21 * -- Prasad <prasad@medhas.org>
28 #include "mozilla/Logging.h"
29 #include "mozilla/Sprintf.h"
30 #include "nsCRTGlue.h"
31 #include "nsTextFormatter.h"
34 struct nsTextFormatter::SprintfStateStr
{
35 int (*stuff
)(SprintfStateStr
* aState
, const char16_t
* aStr
, uint32_t aLen
);
49 #define _UNSIGNED 0x20
51 #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
54 ** Fill into the buffer using the data in src
56 int nsTextFormatter::fill2(SprintfStateStr
* aState
, const char16_t
* aSrc
,
57 int aSrcLen
, int aWidth
, int aFlags
) {
63 if ((aWidth
> 0) && ((aFlags
& _LEFT
) == 0)) {
64 if (aFlags
& _ZEROS
) {
67 while (--aWidth
>= 0) {
68 rv
= (*aState
->stuff
)(aState
, &space
, 1);
75 /* Copy out the source data */
76 rv
= (*aState
->stuff
)(aState
, aSrc
, aSrcLen
);
82 if ((aWidth
> 0) && ((aFlags
& _LEFT
) != 0)) {
83 while (--aWidth
>= 0) {
84 rv
= (*aState
->stuff
)(aState
, &space
, 1);
94 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
96 int nsTextFormatter::fill_n(nsTextFormatter::SprintfStateStr
* aState
,
97 const char16_t
* aSrc
, int aSrcLen
, int aWidth
,
98 int aPrec
, int aFlags
) {
107 char16_t space
= ' ';
110 if ((aFlags
& _UNSIGNED
) == 0) {
114 } else if (aFlags
& _SIGNED
) {
117 } else if (aFlags
& _SPACED
) {
122 cvtwidth
= signwidth
+ aSrcLen
;
125 if (aPrec
> aSrcLen
) {
126 /* Need zero filling */
127 precwidth
= aPrec
- aSrcLen
;
128 cvtwidth
+= precwidth
;
132 if ((aFlags
& _ZEROS
) && (aPrec
< 0)) {
133 if (aWidth
> cvtwidth
) {
135 zerowidth
= aWidth
- cvtwidth
;
136 cvtwidth
+= zerowidth
;
140 if (aFlags
& _LEFT
) {
141 if (aWidth
> cvtwidth
) {
142 /* Space filling on the right (i.e. left adjusting) */
143 rightspaces
= aWidth
- cvtwidth
;
146 if (aWidth
> cvtwidth
) {
147 /* Space filling on the left (i.e. right adjusting) */
148 leftspaces
= aWidth
- cvtwidth
;
151 while (--leftspaces
>= 0) {
152 rv
= (*aState
->stuff
)(aState
, &space
, 1);
158 rv
= (*aState
->stuff
)(aState
, &sign
, 1);
163 while (--precwidth
>= 0) {
164 rv
= (*aState
->stuff
)(aState
, &space
, 1);
169 while (--zerowidth
>= 0) {
170 rv
= (*aState
->stuff
)(aState
, &zero
, 1);
175 rv
= (*aState
->stuff
)(aState
, aSrc
, aSrcLen
);
179 while (--rightspaces
>= 0) {
180 rv
= (*aState
->stuff
)(aState
, &space
, 1);
189 ** Convert a 64-bit integer into its printable form
191 int nsTextFormatter::cvt_ll(SprintfStateStr
* aState
, uint64_t aNum
, int aWidth
,
192 int aPrec
, int aRadix
, int aFlags
,
193 const char16_t
* aHexStr
) {
194 char16_t cvtbuf
[100];
198 /* according to the man page this needs to happen */
199 if (aPrec
== 0 && aNum
== 0) {
204 ** Converting decimal is a little tricky. In the unsigned case we
205 ** need to stop when we hit 10 digits. In the signed case, we can
206 ** stop when the number is zero.
208 cvt
= &cvtbuf
[0] + ELEMENTS_OF(cvtbuf
);
211 uint64_t quot
= aNum
/ aRadix
;
212 uint64_t rem
= aNum
% aRadix
;
213 *--cvt
= aHexStr
[rem
& 0xf];
223 ** Now that we have the number converted without its sign, deal with
224 ** the sign and zero padding.
226 return fill_n(aState
, cvt
, digits
, aWidth
, aPrec
, aFlags
);
230 ** Convert a double precision floating point number into its printable
233 int nsTextFormatter::cvt_f(SprintfStateStr
* aState
, double aDouble
, int aWidth
,
234 int aPrec
, const char16_t aType
, int aFlags
) {
249 } else if (aPrec
> 50) {
250 // limit precision to avoid PR_dtoa bug 108335
251 // and to prevent buffers overflows
264 numdigits
= aPrec
+ 1;
278 NS_ERROR("invalid aType passed to cvt_f");
281 if (PR_dtoa(aDouble
, mode
, numdigits
, &decpt
, &sign
, &endnum
, num
, bufsz
) ==
286 numdigits
= endnum
- num
;
291 } else if (aFlags
& _SIGNED
) {
296 while ((*bufp
++ = *nump
++)) {
310 while (aPrec
-- > 0) {
316 ::snprintf(bufp
, bufsz
- (bufp
- buf
), "%+03d", decpt
- 1);
325 while (decpt
++ && aPrec
-- > 0) {
328 while (*nump
&& aPrec
-- > 0) {
331 while (aPrec
-- > 0) {
336 while (*nump
&& decpt
-- > 0) {
339 while (decpt
-- > 0) {
344 while (*nump
&& aPrec
-- > 0) {
347 while (aPrec
-- > 0) {
358 if ((decpt
< -3) || ((decpt
- 1) >= aPrec
)) {
368 ::snprintf(bufp
, bufsz
- (bufp
- buf
), "%+03d", decpt
- 1);
382 while (*nump
&& decpt
-- > 0) {
386 while (decpt
-- > 0) {
402 char16_t
* rbufp
= rbuf
;
405 while ((*rbufp
++ = *bufp
++)) {
409 return fill2(aState
, rbuf
, NS_strlen(rbuf
), aWidth
, aFlags
);
413 ** Convert a string into its printable form. |aWidth| is the output
414 ** width. |aPrec| is the maximum number of characters of |aStr| to output,
415 ** where -1 means until NUL.
417 int nsTextFormatter::cvt_S(SprintfStateStr
* aState
, const char16_t
* aStr
,
418 int aWidth
, int aPrec
, int aFlags
) {
425 /* Limit string length by precision value */
426 slen
= aStr
? NS_strlen(aStr
) : 6;
434 return fill2(aState
, aStr
? aStr
: u
"(null)", slen
, aWidth
, aFlags
);
438 ** Convert a string into its printable form. |aWidth| is the output
439 ** width. |aPrec| is the maximum number of characters of |aStr| to output,
440 ** where -1 means until NUL.
442 int nsTextFormatter::cvt_s(nsTextFormatter::SprintfStateStr
* aState
,
443 const char* aStr
, int aWidth
, int aPrec
,
445 // Be sure to handle null the same way as %S.
446 if (aStr
== nullptr) {
447 return cvt_S(aState
, nullptr, aWidth
, aPrec
, aFlags
);
449 NS_ConvertUTF8toUTF16
utf16Val(aStr
);
450 return cvt_S(aState
, utf16Val
.get(), aWidth
, aPrec
, aFlags
);
454 ** The workhorse sprintf code.
456 int nsTextFormatter::dosprintf(SprintfStateStr
* aState
, const char16_t
* aFmt
,
457 mozilla::Span
<BoxedValue
> aValues
) {
458 static const char16_t space
= ' ';
459 static const char16_t hex
[] = u
"0123456789abcdef";
460 static const char16_t HEX
[] = u
"0123456789ABCDEF";
461 static const BoxedValue
emptyString(u
"");
464 int flags
, width
, prec
, radix
;
466 const char16_t
* hexp
;
468 // Next argument for non-numbered arguments.
469 size_t nextNaturalArg
= 0;
470 // True if we ever saw a numbered argument.
471 bool sawNumberedArg
= false;
473 while ((c
= *aFmt
++) != 0) {
477 rv
= (*aState
->stuff
)(aState
, aFmt
- 1, 1);
484 // Save the location of the "%" in case we decide it isn't a
485 // format and want to just emit the text from the format string.
486 const char16_t
* percentPointer
= aFmt
- 1;
489 ** Gobble up the % format string. Hopefully we have handled all
490 ** of the strange cases!
495 /* quoting a % with %% */
496 rv
= (*aState
->stuff
)(aState
, aFmt
- 1, 1);
503 // Check for a numbered argument.
504 bool sawWidth
= false;
505 const BoxedValue
* thisArg
= nullptr;
506 if (c
>= '0' && c
<= '9') {
507 size_t argNumber
= 0;
508 while (c
&& c
>= '0' && c
<= '9') {
509 argNumber
= (argNumber
* 10) + (c
- '0');
514 // Mixing numbered arguments and implicit arguments is
516 if (nextNaturalArg
> 0) {
522 // Numbered arguments start at 1.
524 if (argNumber
>= aValues
.Length()) {
525 // A correctness issue but not a safety issue.
527 thisArg
= &emptyString
;
529 thisArg
= &aValues
[argNumber
];
531 sawNumberedArg
= true;
540 * Examine optional flags. Note that we do not implement the
541 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
542 * somewhat ambiguous and not ideal, which is perhaps why
543 * the various sprintf() implementations are inconsistent
546 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
561 if (flags
& _SIGNED
) {
570 // Not supported with numbered arguments.
571 if (sawNumberedArg
) {
575 if (nextNaturalArg
>= aValues
.Length() ||
576 !aValues
[nextNaturalArg
].IntCompatible()) {
577 // A correctness issue but not a safety issue.
581 width
= aValues
[nextNaturalArg
++].mValue
.mInt
;
586 while ((c
>= '0') && (c
<= '9')) {
587 width
= (width
* 10) + (c
- '0');
598 // Not supported with numbered arguments.
599 if (sawNumberedArg
) {
603 if (nextNaturalArg
>= aValues
.Length() ||
604 !aValues
[nextNaturalArg
].IntCompatible()) {
605 // A correctness issue but not a safety issue.
608 prec
= aValues
[nextNaturalArg
++].mValue
.mInt
;
613 while ((c
>= '0') && (c
<= '9')) {
614 prec
= (prec
* 10) + (c
- '0');
620 // If the argument isn't known yet, find it now. This is done
621 // after the width and precision code, in case '*' was used.
622 if (thisArg
== nullptr) {
623 // Mixing numbered arguments and implicit arguments is
625 if (sawNumberedArg
) {
629 if (nextNaturalArg
>= aValues
.Length()) {
630 // A correctness issue but not a safety issue.
632 thisArg
= &emptyString
;
634 thisArg
= &aValues
[nextNaturalArg
++];
638 /* Size. Defaults to 32 bits. */
639 uint64_t mask
= UINT32_MAX
;
643 } else if (c
== 'L') {
646 } else if (c
== 'l') {
659 // Several `MOZ_ASSERT`s below check for argument compatibility
660 // with the format specifier. These are only debug assertions,
661 // not release assertions, and exist to catch problems in C++
662 // callers of `nsTextFormatter`, as we do not have compile-time
663 // checking of format strings. In release mode, these assertions
664 // will be no-ops, and we will fall through to printing the
665 // argument based on the known type of the argument.
668 case 'i': /* decimal/integer */
669 MOZ_ASSERT(thisArg
->IntCompatible());
672 case 'o': /* octal */
673 MOZ_ASSERT(thisArg
->IntCompatible());
678 case 'u': /* unsigned decimal */
679 MOZ_ASSERT(thisArg
->IntCompatible());
684 case 'x': /* unsigned hex */
685 MOZ_ASSERT(thisArg
->IntCompatible());
690 case 'X': /* unsigned HEX */
691 MOZ_ASSERT(thisArg
->IntCompatible());
702 MOZ_ASSERT(thisArg
->mKind
== DOUBLE
);
703 // Type-based printing below.
707 MOZ_ASSERT(thisArg
->mKind
== STRING16
);
708 // Type-based printing below.
712 MOZ_ASSERT(thisArg
->mKind
== STRING
);
713 // Type-based printing below.
717 if (!thisArg
->IntCompatible()) {
719 // Type-based printing below.
723 if ((flags
& _LEFT
) == 0) {
724 while (width
-- > 1) {
725 rv
= (*aState
->stuff
)(aState
, &space
, 1);
731 char16_t ch
= thisArg
->mValue
.mInt
;
732 rv
= (*aState
->stuff
)(aState
, &ch
, 1);
737 while (width
-- > 1) {
738 rv
= (*aState
->stuff
)(aState
, &space
, 1);
748 if (!thisArg
->PointerCompatible()) {
752 static_assert(sizeof(uint64_t) >= sizeof(void*),
753 "pointers are larger than 64 bits");
754 rv
= cvt_ll(aState
, uintptr_t(thisArg
->mValue
.mPtr
), width
, prec
, 16,
755 flags
| _UNSIGNED
, hexp
);
762 if (thisArg
->mKind
!= INTPOINTER
) {
766 if (thisArg
->mValue
.mIntPtr
!= nullptr) {
767 *thisArg
->mValue
.mIntPtr
= aState
->cur
- aState
->base
;
772 /* Not a % token after all... skip it */
773 rv
= (*aState
->stuff
)(aState
, percentPointer
, aFmt
- percentPointer
);
780 // If we get here, we want to handle the argument according to its
781 // actual type; modified by the flags as appropriate.
782 switch (thisArg
->mKind
) {
785 int64_t val
= thisArg
->mValue
.mInt
;
786 if ((flags
& _UNSIGNED
) == 0 && val
< 0) {
790 rv
= cvt_ll(aState
, uint64_t(val
) & mask
, width
, prec
, radix
, flags
,
795 // Always treat these as unsigned hex, no matter the format.
796 static_assert(sizeof(uint64_t) >= sizeof(void*),
797 "pointers are larger than 64 bits");
798 rv
= cvt_ll(aState
, uintptr_t(thisArg
->mValue
.mPtr
), width
, prec
, 16,
799 flags
| _UNSIGNED
, hexp
);
802 if (c
!= 'f' && c
!= 'E' && c
!= 'e' && c
!= 'G' && c
!= 'g') {
803 // Pick some default.
806 rv
= cvt_f(aState
, thisArg
->mValue
.mDouble
, width
, prec
, c
, flags
);
809 rv
= cvt_s(aState
, thisArg
->mValue
.mString
, width
, prec
, flags
);
812 rv
= cvt_S(aState
, thisArg
->mValue
.mString16
, width
, prec
, flags
);
827 /************************************************************************/
829 int nsTextFormatter::StringStuff(nsTextFormatter::SprintfStateStr
* aState
,
830 const char16_t
* aStr
, uint32_t aLen
) {
831 ptrdiff_t off
= aState
->cur
- aState
->base
;
833 nsAString
* str
= static_cast<nsAString
*>(aState
->stuffclosure
);
834 str
->Append(aStr
, aLen
);
836 aState
->base
= str
->BeginWriting();
837 aState
->cur
= aState
->base
+ off
;
842 void nsTextFormatter::vssprintf(nsAString
& aOut
, const char16_t
* aFmt
,
843 mozilla::Span
<BoxedValue
> aValues
) {
845 ss
.stuff
= StringStuff
;
849 ss
.stuffclosure
= &aOut
;
852 dosprintf(&ss
, aFmt
, aValues
);
856 ** Stuff routine that discards overflow data
858 int nsTextFormatter::LimitStuff(SprintfStateStr
* aState
, const char16_t
* aStr
,
860 uint32_t limit
= aState
->maxlen
- (aState
->cur
- aState
->base
);
867 *aState
->cur
++ = *aStr
++;
872 uint32_t nsTextFormatter::vsnprintf(char16_t
* aOut
, uint32_t aOutLen
,
873 const char16_t
* aFmt
,
874 mozilla::Span
<BoxedValue
> aValues
) {
877 MOZ_ASSERT((int32_t)aOutLen
> 0);
878 if ((int32_t)aOutLen
<= 0) {
882 ss
.stuff
= LimitStuff
;
886 int result
= dosprintf(&ss
, aFmt
, aValues
);
888 if (ss
.cur
== ss
.base
) {
892 // Append a NUL. However, be sure not to count it in the returned
894 if (ss
.cur
- ss
.base
>= ptrdiff_t(ss
.maxlen
)) {
899 // Check the result now, so that an unterminated string can't
905 return ss
.cur
- ss
.base
;