1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ** Portable safe sprintf code.
9 ** Author: Kipp E.B. Hickman
21 #if defined(_MSC_VER) && _MSC_VER < 1900
22 # define snprintf _snprintf
26 ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
30 ** XXX This needs to be internationalized!
33 typedef struct SprintfStateStr SprintfState
;
35 struct SprintfStateStr
{
36 int (*stuff
)(SprintfState
* ss
, const char* sp
, PRUint32 len
);
40 PRUint32 maxlen
; /* Must not exceed PR_INT32_MAX. */
42 int (*func
)(void* arg
, const char* sp
, PRUint32 len
);
50 int type
; /* type of the numbered argument */
51 union { /* the numbered argument */
67 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
70 ** For numeric types, the signed versions must have even values,
71 ** and their corresponding unsigned versions must have the subsequent
84 #define TYPE_INTSTR 10
86 # define TYPE_WSTRING 11
88 #define TYPE_UNKNOWN 20
91 #define FLAG_SIGNED 0x2
92 #define FLAG_SPACED 0x4
93 #define FLAG_ZEROS 0x8
97 ** Fill into the buffer using the data in src
99 static int fill2(SprintfState
* ss
, const char* src
, int srclen
, int width
,
105 if ((width
> 0) && ((flags
& FLAG_LEFT
) == 0)) { /* Right adjusting */
106 if (flags
& FLAG_ZEROS
) {
109 while (--width
>= 0) {
110 rv
= (*ss
->stuff
)(ss
, &space
, 1);
117 /* Copy out the source data */
118 rv
= (*ss
->stuff
)(ss
, src
, srclen
);
123 if ((width
> 0) && ((flags
& FLAG_LEFT
) != 0)) { /* Left adjusting */
124 while (--width
>= 0) {
125 rv
= (*ss
->stuff
)(ss
, &space
, 1);
135 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
137 static int fill_n(SprintfState
* ss
, const char* src
, int srclen
, int width
,
138 int prec
, int type
, int flags
) {
148 if ((type
& 1) == 0) {
149 if (flags
& FLAG_NEG
) {
152 } else if (flags
& FLAG_SIGNED
) {
155 } else if (flags
& FLAG_SPACED
) {
160 cvtwidth
= signwidth
+ srclen
;
164 precwidth
= prec
- srclen
; /* Need zero filling */
165 cvtwidth
+= precwidth
;
169 if ((flags
& FLAG_ZEROS
) && (prec
< 0)) {
170 if (width
> cvtwidth
) {
171 zerowidth
= width
- cvtwidth
; /* Zero filling */
172 cvtwidth
+= zerowidth
;
176 if (flags
& FLAG_LEFT
) {
177 if (width
> cvtwidth
) {
178 /* Space filling on the right (i.e. left adjusting) */
179 rightspaces
= width
- cvtwidth
;
182 if (width
> cvtwidth
) {
183 /* Space filling on the left (i.e. right adjusting) */
184 leftspaces
= width
- cvtwidth
;
187 while (--leftspaces
>= 0) {
188 rv
= (*ss
->stuff
)(ss
, " ", 1);
194 rv
= (*ss
->stuff
)(ss
, &sign
, 1);
199 while (--precwidth
>= 0) {
200 rv
= (*ss
->stuff
)(ss
, "0", 1);
205 while (--zerowidth
>= 0) {
206 rv
= (*ss
->stuff
)(ss
, "0", 1);
211 rv
= (*ss
->stuff
)(ss
, src
, srclen
);
215 while (--rightspaces
>= 0) {
216 rv
= (*ss
->stuff
)(ss
, " ", 1);
225 ** Convert a long into its printable form
227 static int cvt_l(SprintfState
* ss
, long num
, int width
, int prec
, int radix
,
228 int type
, int flags
, const char* hexp
) {
233 /* according to the man page this needs to happen */
234 if ((prec
== 0) && (num
== 0)) {
239 ** Converting decimal is a little tricky. In the unsigned case we
240 ** need to stop when we hit 10 digits. In the signed case, we can
241 ** stop when the number is zero.
243 cvt
= cvtbuf
+ sizeof(cvtbuf
);
246 int digit
= (((unsigned long)num
) % radix
) & 0xF;
247 *--cvt
= hexp
[digit
];
249 num
= (long)(((unsigned long)num
) / radix
);
257 ** Now that we have the number converted without its sign, deal with
258 ** the sign and zero padding.
260 return fill_n(ss
, cvt
, digits
, width
, prec
, type
, flags
);
264 ** Convert a 64-bit integer into its printable form
266 static int cvt_ll(SprintfState
* ss
, PRInt64 num
, int width
, int prec
, int radix
,
267 int type
, int flags
, const char* hexp
) {
273 /* according to the man page this needs to happen */
274 if ((prec
== 0) && (LL_IS_ZERO(num
))) {
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.
284 cvt
= cvtbuf
+ sizeof(cvtbuf
);
286 while (!LL_IS_ZERO(num
)) {
289 LL_UDIVMOD("
, &rem
, num
, rad
);
291 *--cvt
= hexp
[digit
& 0xf];
301 ** Now that we have the number converted without its sign, deal with
302 ** the sign and zero padding.
304 return fill_n(ss
, cvt
, digits
, width
, prec
, type
, flags
);
308 ** Convert a double precision floating point number into its printable
311 ** XXX stop using snprintf to convert floating point
313 static int cvt_f(SprintfState
* ss
, double d
, const char* fmt0
,
317 int amount
= fmt1
- fmt0
;
319 if (amount
<= 0 || amount
>= sizeof(fin
)) {
320 /* Totally bogus % command to snprintf. Just ignore it */
323 memcpy(fin
, fmt0
, amount
);
326 /* Convert floating point using the native snprintf code */
331 PR_ASSERT(*p
!= 'L');
336 memset(fout
, 0, sizeof(fout
));
337 snprintf(fout
, sizeof(fout
), fin
, d
);
338 /* Explicitly null-terminate fout because on Windows snprintf doesn't
339 * append a null-terminator if the buffer is too small. */
340 fout
[sizeof(fout
) - 1] = '\0';
342 return (*ss
->stuff
)(ss
, fout
, strlen(fout
));
346 ** Convert a string into its printable form. "width" is the output
347 ** width. "prec" is the maximum number of characters of "s" to output,
348 ** where -1 means until NUL.
350 static int cvt_s(SprintfState
* ss
, const char* str
, int width
, int prec
,
358 /* Limit string length by precision value */
363 /* this is: slen = strnlen(str, prec); */
364 register const char* s
;
366 for (s
= str
; prec
&& *s
; s
++, prec
--);
373 return fill2(ss
, str
, slen
, width
, flags
);
377 ** BuildArgArray stands for Numbered Argument list Sprintf
379 ** fmt = "%4$i, %2$d, %3s, %1d";
380 ** the number must start from 1, and no gap among them
383 static struct NumArg
* BuildArgArray(const char* fmt
, va_list ap
, int* rv
,
384 struct NumArg
* nasArray
) {
385 int number
= 0, cn
= 0, i
;
392 ** determine how many legal % I have got, then allocate space
398 while ((c
= *p
++) != 0) {
402 if ((c
= *p
++) == '%') { /* skip %% case */
407 if (c
> '9' || c
< '0') {
408 if (c
== '$') { /* numbered argument case */
414 } else { /* non-numbered argument case */
432 if (number
> NAS_DEFAULT_NUM
) {
433 nas
= (struct NumArg
*)PR_MALLOC(number
* sizeof(struct NumArg
));
442 for (i
= 0; i
< number
; i
++) {
443 nas
[i
].type
= TYPE_UNKNOWN
;
452 while ((c
= *p
++) != 0) {
462 while (c
&& c
!= '$') { /* should improve error check later */
463 cn
= cn
* 10 + c
- '0';
467 if (!c
|| cn
< 1 || cn
> number
) {
472 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
474 if (nas
[cn
].type
!= TYPE_UNKNOWN
) {
482 /* not supported feature, for the argument is not numbered */
487 while ((c
>= '0') && (c
<= '9')) {
495 /* not supported feature, for the argument is not numbered */
500 while ((c
>= '0') && (c
<= '9')) {
506 nas
[cn
].type
= TYPE_INTN
;
508 nas
[cn
].type
= TYPE_INT16
;
510 } else if (c
== 'L') {
511 /* XXX not quite sure here */
512 nas
[cn
].type
= TYPE_INT64
;
514 } else if (c
== 'l') {
515 nas
[cn
].type
= TYPE_INT32
;
518 nas
[cn
].type
= TYPE_INT64
;
521 } else if (c
== 'z') {
522 if (sizeof(size_t) == sizeof(PRInt32
)) {
523 nas
[cn
].type
= TYPE_INT32
;
524 } else if (sizeof(size_t) == sizeof(PRInt64
)) {
525 nas
[cn
].type
= TYPE_INT64
;
527 nas
[cn
].type
= TYPE_UNKNOWN
;
546 nas
[cn
].type
= TYPE_DOUBLE
;
550 /* XXX should use cpp */
551 if (sizeof(void*) == sizeof(PRInt32
)) {
552 nas
[cn
].type
= TYPE_UINT32
;
553 } else if (sizeof(void*) == sizeof(PRInt64
)) {
554 nas
[cn
].type
= TYPE_UINT64
;
555 } else if (sizeof(void*) == sizeof(PRIntn
)) {
556 nas
[cn
].type
= TYPE_UINTN
;
558 nas
[cn
].type
= TYPE_UNKNOWN
;
564 nas
[cn
].type
= TYPE_WSTRING
;
570 /* XXX not supported I suppose */
572 nas
[cn
].type
= TYPE_UNKNOWN
;
576 nas
[cn
].type
= TYPE_STRING
;
580 nas
[cn
].type
= TYPE_INTSTR
;
585 nas
[cn
].type
= TYPE_UNKNOWN
;
589 /* get a legal para. */
590 if (nas
[cn
].type
== TYPE_UNKNOWN
) {
598 ** fill the nas[cn].ap
602 if (nas
!= nasArray
) {
609 while (cn
< number
) {
610 if (nas
[cn
].type
== TYPE_UNKNOWN
) {
615 switch (nas
[cn
].type
) {
619 nas
[cn
].u
.i
= va_arg(ap
, int);
623 nas
[cn
].u
.ui
= va_arg(ap
, unsigned int);
627 nas
[cn
].u
.i32
= va_arg(ap
, PRInt32
);
631 nas
[cn
].u
.ui32
= va_arg(ap
, PRUint32
);
635 nas
[cn
].u
.ll
= va_arg(ap
, PRInt64
);
639 nas
[cn
].u
.ull
= va_arg(ap
, PRUint64
);
643 nas
[cn
].u
.s
= va_arg(ap
, char*);
648 nas
[cn
].u
.ws
= va_arg(ap
, WCHAR
*);
653 nas
[cn
].u
.ip
= va_arg(ap
, int*);
657 nas
[cn
].u
.d
= va_arg(ap
, double);
661 if (nas
!= nasArray
) {
675 ** The workhorse sprintf code.
677 static int dosprintf(SprintfState
* ss
, const char* fmt
, va_list ap
) {
679 int flags
, width
, prec
, radix
, type
;
693 static char* hex
= "0123456789abcdef";
694 static char* HEX
= "0123456789ABCDEF";
697 struct NumArg
* nas
= NULL
;
698 struct NumArg
* nap
= NULL
;
699 struct NumArg nasArray
[NAS_DEFAULT_NUM
];
701 const char* dolPt
= NULL
; /* in "%4$.2f", dolPt will point to . */
707 ** build an argument array, IF the fmt is numbered argument
708 ** list style, to contain the Numbered Argument list pointers
711 nas
= BuildArgArray(fmt
, ap
, &rv
, nasArray
);
713 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
718 while ((c
= *fmt
++) != 0) {
720 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
729 ** Gobble up the % format string. Hopefully we have handled all
730 ** of the strange cases!
735 /* quoting a % with %% */
736 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
744 /* the fmt contains the Numbered Arguments feature */
746 while (c
&& c
!= '$') { /* should improve error check later */
747 i
= (i
* 10) + (c
- '0');
751 if (nas
[i
- 1].type
== TYPE_UNKNOWN
) {
752 if (nas
&& (nas
!= nasArray
)) {
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
770 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
775 flags
|= FLAG_SIGNED
;
778 flags
|= FLAG_SPACED
;
785 if (flags
& FLAG_SIGNED
) {
786 flags
&= ~FLAG_SPACED
;
788 if (flags
& FLAG_LEFT
) {
789 flags
&= ~FLAG_ZEROS
;
795 width
= va_arg(ap
, int);
798 while ((c
>= '0') && (c
<= '9')) {
799 width
= (width
* 10) + (c
- '0');
810 prec
= va_arg(ap
, int);
813 while ((c
>= '0') && (c
<= '9')) {
814 prec
= (prec
* 10) + (c
- '0');
825 } else if (c
== 'L') {
826 /* XXX not quite sure here */
829 } else if (c
== 'l') {
836 } else if (c
== 'z') {
837 if (sizeof(size_t) == sizeof(PRInt32
)) {
839 } else if (sizeof(size_t) == sizeof(PRInt64
)) {
849 case 'i': /* decimal/integer */
851 goto fetch_and_convert
;
853 case 'o': /* octal */
856 goto fetch_and_convert
;
858 case 'u': /* unsigned decimal */
861 goto fetch_and_convert
;
863 case 'x': /* unsigned hex */
866 goto fetch_and_convert
;
868 case 'X': /* unsigned HEX */
872 goto fetch_and_convert
;
877 u
.l
= nas
? nap
->u
.i
: va_arg(ap
, int);
884 u
.l
= (nas
? nap
->u
.i
: va_arg(ap
, int)) & 0xffff;
887 u
.l
= nas
? nap
->u
.i
: va_arg(ap
, int);
894 u
.l
= (long)(nas
? nap
->u
.ui
: va_arg(ap
, unsigned int));
898 u
.l
= nas
? nap
->u
.i32
: va_arg(ap
, PRInt32
);
905 u
.l
= (long)(nas
? nap
->u
.ui32
: va_arg(ap
, PRUint32
));
907 rv
= cvt_l(ss
, u
.l
, width
, prec
, radix
, type
, flags
, hexp
);
914 u
.ll
= nas
? nap
->u
.ll
: va_arg(ap
, PRInt64
);
915 if (!LL_GE_ZERO(u
.ll
)) {
921 u
.ll
= nas
? nap
->u
.ull
: va_arg(ap
, PRUint64
);
923 rv
= cvt_ll(ss
, u
.ll
, width
, prec
, radix
, type
, flags
, hexp
);
935 u
.d
= nas
? nap
->u
.d
: va_arg(ap
, double);
938 if (i
< sizeof(pattern
)) {
940 memcpy(&pattern
[1], dolPt
, i
);
941 rv
= cvt_f(ss
, u
.d
, pattern
, &pattern
[i
+ 1]);
944 rv
= cvt_f(ss
, u
.d
, fmt0
, fmt
);
953 u
.ch
= nas
? nap
->u
.i
: va_arg(ap
, int);
954 if ((flags
& FLAG_LEFT
) == 0) {
955 while (width
-- > 1) {
956 rv
= (*ss
->stuff
)(ss
, " ", 1);
962 rv
= (*ss
->stuff
)(ss
, &u
.ch
, 1);
966 if (flags
& FLAG_LEFT
) {
967 while (width
-- > 1) {
968 rv
= (*ss
->stuff
)(ss
, " ", 1);
977 if (sizeof(void*) == sizeof(PRInt32
)) {
979 } else if (sizeof(void*) == sizeof(PRInt64
)) {
981 } else if (sizeof(void*) == sizeof(int)) {
988 goto fetch_and_convert
;
992 /* XXX not supported I suppose */
1001 /* XXX not supported I suppose */
1008 u
.ws
= nas
? nap
->u
.ws
: va_arg(ap
, const WCHAR
*);
1010 /* Get the required size in rv */
1011 rv
= WideCharToMultiByte(CP_ACP
, 0, u
.ws
, -1, NULL
, 0, NULL
, NULL
);
1015 pBuf
= PR_MALLOC(rv
);
1016 WideCharToMultiByte(CP_ACP
, 0, u
.ws
, -1, pBuf
, (int)rv
, NULL
, NULL
);
1017 pBuf
[rv
- 1] = '\0';
1019 rv
= cvt_s(ss
, pBuf
, width
, prec
, flags
);
1021 /* We don't need the allocated buffer anymore */
1031 u
.s
= nas
? nap
->u
.s
: va_arg(ap
, const char*);
1032 rv
= cvt_s(ss
, u
.s
, width
, prec
, flags
);
1039 u
.ip
= nas
? nap
->u
.ip
: va_arg(ap
, int*);
1041 *u
.ip
= ss
->cur
- ss
->base
;
1046 /* Not a % token after all... skip it */
1050 rv
= (*ss
->stuff
)(ss
, "%", 1);
1054 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
1061 /* Stuff trailing NUL */
1062 rv
= (*ss
->stuff
)(ss
, "\0", 1);
1064 if (nas
&& (nas
!= nasArray
)) {
1071 /************************************************************************/
1073 static int FuncStuff(SprintfState
* ss
, const char* sp
, PRUint32 len
) {
1077 ** We will add len to ss->maxlen at the end of the function. First check
1078 ** if ss->maxlen + len would overflow or be greater than PR_INT32_MAX.
1080 if (PR_UINT32_MAX
- ss
->maxlen
< len
|| ss
->maxlen
+ len
> PR_INT32_MAX
) {
1083 rv
= (*ss
->func
)(ss
->arg
, sp
, len
);
1091 PR_IMPLEMENT(PRUint32
)
1092 PR_sxprintf(PRStuffFunc func
, void* arg
, const char* fmt
, ...) {
1097 rv
= PR_vsxprintf(func
, arg
, fmt
, ap
);
1102 PR_IMPLEMENT(PRUint32
)
1103 PR_vsxprintf(PRStuffFunc func
, void* arg
, const char* fmt
, va_list ap
) {
1107 ss
.stuff
= FuncStuff
;
1111 rv
= dosprintf(&ss
, fmt
, ap
);
1112 return (rv
< 0) ? (PRUint32
)-1 : ss
.maxlen
;
1116 ** Stuff routine that automatically grows the malloc'd output buffer
1117 ** before it overflows.
1119 static int GrowStuff(SprintfState
* ss
, const char* sp
, PRUint32 len
) {
1124 off
= ss
->cur
- ss
->base
;
1125 if (PR_UINT32_MAX
- len
< off
) {
1126 /* off + len would be too big. */
1129 if (off
+ len
>= ss
->maxlen
) {
1130 /* Grow the buffer */
1131 PRUint32 increment
= (len
> 32) ? len
: 32;
1132 if (PR_UINT32_MAX
- ss
->maxlen
< increment
) {
1133 /* ss->maxlen + increment would overflow. */
1136 newlen
= ss
->maxlen
+ increment
;
1137 if (newlen
> PR_INT32_MAX
) {
1141 newbase
= (char*)PR_REALLOC(ss
->base
, newlen
);
1143 newbase
= (char*)PR_MALLOC(newlen
);
1146 /* Ran out of memory */
1150 ss
->maxlen
= newlen
;
1151 ss
->cur
= ss
->base
+ off
;
1159 PR_ASSERT((PRUint32
)(ss
->cur
- ss
->base
) <= ss
->maxlen
);
1164 ** sprintf into a malloc'd buffer
1166 PR_IMPLEMENT(char*) PR_smprintf(const char* fmt
, ...) {
1171 rv
= PR_vsmprintf(fmt
, ap
);
1177 ** Free memory allocated, for the caller, by PR_smprintf
1179 PR_IMPLEMENT(void) PR_smprintf_free(char* mem
) { PR_DELETE(mem
); }
1181 PR_IMPLEMENT(char*) PR_vsmprintf(const char* fmt
, va_list ap
) {
1185 ss
.stuff
= GrowStuff
;
1189 rv
= dosprintf(&ss
, fmt
, ap
);
1200 ** Stuff routine that discards overflow data
1202 static int LimitStuff(SprintfState
* ss
, const char* sp
, PRUint32 len
) {
1203 PRUint32 limit
= ss
->maxlen
- (ss
->cur
- ss
->base
);
1216 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1219 PR_IMPLEMENT(PRUint32
)
1220 PR_snprintf(char* out
, PRUint32 outlen
, const char* fmt
, ...) {
1225 rv
= PR_vsnprintf(out
, outlen
, fmt
, ap
);
1230 PR_IMPLEMENT(PRUint32
)
1231 PR_vsnprintf(char* out
, PRUint32 outlen
, const char* fmt
, va_list ap
) {
1235 PR_ASSERT(outlen
!= 0 && outlen
<= PR_INT32_MAX
);
1236 if (outlen
== 0 || outlen
> PR_INT32_MAX
) {
1240 ss
.stuff
= LimitStuff
;
1244 (void)dosprintf(&ss
, fmt
, ap
);
1246 /* If we added chars, and we didn't append a null, do it now. */
1247 if ((ss
.cur
!= ss
.base
) && (*(ss
.cur
- 1) != '\0')) {
1248 *(ss
.cur
- 1) = '\0';
1251 n
= ss
.cur
- ss
.base
;
1252 return n
? n
- 1 : n
;
1255 PR_IMPLEMENT(char*) PR_sprintf_append(char* last
, const char* fmt
, ...) {
1260 rv
= PR_vsprintf_append(last
, fmt
, ap
);
1266 PR_vsprintf_append(char* last
, const char* fmt
, va_list ap
) {
1270 ss
.stuff
= GrowStuff
;
1272 size_t lastlen
= strlen(last
);
1273 if (lastlen
> PR_INT32_MAX
) {
1277 ss
.cur
= last
+ lastlen
;
1278 ss
.maxlen
= lastlen
;
1284 rv
= dosprintf(&ss
, fmt
, ap
);