1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org Code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Prasad <prasad@medhas.org>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * Portable safe sprintf code.
42 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
45 * Kipp E.B. Hickman <kipp@netscape.com> (original author)
46 * Frank Yung-Fong Tang <ftang@netscape.com>
47 * Daniele Nicolodi <daniele@grinta.net>
51 * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
52 * Changed to use nsMemory and Frozen linkage
53 * -- Prasad <prasad@medhas.org>
65 #include "nsCRTGlue.h"
66 #include "nsTextFormatter.h"
70 ** Note: on some platforms va_list is defined as an array,
71 ** and requires array notation.
75 #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
76 #elif defined(HAVE_VA_LIST_AS_ARRAY)
77 #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
79 #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
82 typedef struct SprintfStateStr SprintfState
;
84 struct SprintfStateStr
{
85 int (*stuff
)(SprintfState
*ss
, const PRUnichar
*sp
, PRUint32 len
);
95 ** Numbered Arguement State
98 int type
; /* type of the current ap */
99 va_list ap
; /* point to the corresponding position on ap */
102 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
105 #define TYPE_UINT16 1
109 #define TYPE_UINT32 5
111 #define TYPE_UINT64 7
112 #define TYPE_STRING 8
113 #define TYPE_DOUBLE 9
114 #define TYPE_INTSTR 10
115 #define TYPE_UNISTRING 11
116 #define TYPE_UNKNOWN 20
124 #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
127 ** Fill into the buffer using the data in src
129 static int fill2(SprintfState
*ss
, const PRUnichar
*src
, int srclen
,
130 int width
, int flags
)
132 PRUnichar space
= ' ';
136 /* Right adjusting */
137 if ((width
> 0) && ((flags
& _LEFT
) == 0)) {
138 if (flags
& _ZEROS
) {
141 while (--width
>= 0) {
142 rv
= (*ss
->stuff
)(ss
, &space
, 1);
149 /* Copy out the source data */
150 rv
= (*ss
->stuff
)(ss
, src
, srclen
);
156 if ((width
> 0) && ((flags
& _LEFT
) != 0)) {
157 while (--width
>= 0) {
158 rv
= (*ss
->stuff
)(ss
, &space
, 1);
168 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
170 static int fill_n(SprintfState
*ss
, const PRUnichar
*src
, int srclen
,
171 int width
, int prec
, int type
, int flags
)
181 PRUnichar space
= ' ';
182 PRUnichar zero
= '0';
184 if ((type
& 1) == 0) {
188 } else if (flags
& _SIGNED
) {
191 } else if (flags
& _SPACED
) {
196 cvtwidth
= signwidth
+ srclen
;
200 /* Need zero filling */
201 precwidth
= prec
- srclen
;
202 cvtwidth
+= precwidth
;
206 if ((flags
& _ZEROS
) && (prec
< 0)) {
207 if (width
> cvtwidth
) {
209 zerowidth
= width
- cvtwidth
;
210 cvtwidth
+= zerowidth
;
215 if (width
> cvtwidth
) {
216 /* Space filling on the right (i.e. left adjusting) */
217 rightspaces
= width
- cvtwidth
;
220 if (width
> cvtwidth
) {
221 /* Space filling on the left (i.e. right adjusting) */
222 leftspaces
= width
- cvtwidth
;
225 while (--leftspaces
>= 0) {
226 rv
= (*ss
->stuff
)(ss
, &space
, 1);
232 rv
= (*ss
->stuff
)(ss
, &sign
, 1);
237 while (--precwidth
>= 0) {
238 rv
= (*ss
->stuff
)(ss
, &space
, 1);
243 while (--zerowidth
>= 0) {
244 rv
= (*ss
->stuff
)(ss
, &zero
, 1);
249 rv
= (*ss
->stuff
)(ss
, src
, srclen
);
253 while (--rightspaces
>= 0) {
254 rv
= (*ss
->stuff
)(ss
, &space
, 1);
263 ** Convert a long into its printable form
265 static int cvt_l(SprintfState
*ss
, long num
, int width
, int prec
,
266 int radix
, int type
, int flags
, const PRUnichar
*hexp
)
268 PRUnichar cvtbuf
[100];
272 /* according to the man page this needs to happen */
273 if ((prec
== 0) && (num
== 0)) {
278 ** Converting decimal is a little tricky. In the unsigned case we
279 ** need to stop when we hit 10 digits. In the signed case, we can
280 ** stop when the number is zero.
282 cvt
= &cvtbuf
[0] + ELEMENTS_OF(cvtbuf
);
285 int digit
= (((unsigned long)num
) % radix
) & 0xF;
286 *--cvt
= hexp
[digit
];
288 num
= (long)(((unsigned long)num
) / radix
);
296 ** Now that we have the number converted without its sign, deal with
297 ** the sign and zero padding.
299 return fill_n(ss
, cvt
, digits
, width
, prec
, type
, flags
);
303 ** Convert a 64-bit integer into its printable form
305 static int cvt_ll(SprintfState
*ss
, PRInt64 num
, int width
, int prec
,
306 int radix
, int type
, int flags
, const PRUnichar
*hexp
)
308 PRUnichar cvtbuf
[100];
313 /* according to the man page this needs to happen */
314 if ((prec
== 0) && (LL_IS_ZERO(num
))) {
319 ** Converting decimal is a little tricky. In the unsigned case we
320 ** need to stop when we hit 10 digits. In the signed case, we can
321 ** stop when the number is zero.
324 cvt
= &cvtbuf
[0] + ELEMENTS_OF(cvtbuf
);
326 while (!LL_IS_ZERO(num
)) {
329 LL_UDIVMOD("
, &rem
, num
, rad
);
331 *--cvt
= hexp
[digit
& 0xf];
341 ** Now that we have the number converted without its sign, deal with
342 ** the sign and zero padding.
344 return fill_n(ss
, cvt
, digits
, width
, prec
, type
, flags
);
348 ** Convert a double precision floating point number into its printable
351 static int cvt_f(SprintfState
*ss
, double d
, int width
, int prec
,
352 const PRUnichar type
, int flags
)
368 } else if (prec
> 50) {
369 // limit precision to avoid PR_dtoa bug 108335
370 // and to prevent buffers overflows
383 numdigits
= prec
+ 1;
397 NS_ERROR("invalid type passed to cvt_f");
400 if (PR_dtoa(d
, mode
, numdigits
, &decpt
, &sign
, &endnum
, num
, bufsz
) == PR_FAILURE
) {
404 numdigits
= endnum
- num
;
409 } else if (flags
& _SIGNED
) {
414 while ((*bufp
++ = *nump
++)) { }
434 PR_snprintf(bufp
, bufsz
- (bufp
- buf
), "%+03d", decpt
-1);
443 while (decpt
++ && prec
-- > 0) {
446 while (*nump
&& prec
-- > 0) {
454 while (*nump
&& decpt
-- > 0) {
457 while (decpt
-- > 0) {
462 while (*nump
&& prec
-- > 0) {
476 if ((decpt
< -3) || ((decpt
- 1) >= prec
)) {
486 PR_snprintf(bufp
, bufsz
- (bufp
- buf
), "%+03d", decpt
-1);
500 while (*nump
&& decpt
-- > 0) {
504 while (decpt
-- > 0) {
520 PRUnichar
*rbufp
= rbuf
;
523 while ((*rbufp
++ = *bufp
++)) { }
526 return fill2(ss
, rbuf
, NS_strlen(rbuf
), width
, flags
);
530 ** Convert a string into its printable form. "width" is the output
531 ** width. "prec" is the maximum number of characters of "s" to output,
532 ** where -1 means until NUL.
534 static int cvt_S(SprintfState
*ss
, const PRUnichar
*s
, int width
,
543 /* Limit string length by precision value */
544 slen
= s
? NS_strlen(s
) : 6;
552 NS_NAMED_LITERAL_STRING(nullstr
, "(null)");
554 return fill2(ss
, s
? s
: nullstr
.get(), slen
, width
, flags
);
558 ** Convert a string into its printable form. "width" is the output
559 ** width. "prec" is the maximum number of characters of "s" to output,
560 ** where -1 means until NUL.
562 static int cvt_s(SprintfState
*ss
, const char *s
, int width
,
565 NS_ConvertUTF8toUTF16
utf16Val(s
);
566 return cvt_S(ss
, utf16Val
.get(), width
, prec
, flags
);
570 ** BuildArgArray stands for Numbered Argument list Sprintf
572 ** fmp = "%4$i, %2$d, %3s, %1d";
573 ** the number must start from 1, and no gap among them
576 static struct NumArgState
* BuildArgArray(const PRUnichar
*fmt
,
577 va_list ap
, int * rv
,
578 struct NumArgState
* nasArray
)
580 int number
= 0, cn
= 0, i
;
583 struct NumArgState
* nas
;
587 ** detemine how many legal % I have got, then allocate space
592 while ((c
= *p
++) != 0) {
597 if ((c
= *p
++) == '%') {
602 if (c
> '9' || c
< '0') {
603 /* numbered argument csae */
613 /* non-numbered argument case */
630 if (number
> NAS_DEFAULT_NUM
) {
631 nas
= (struct NumArgState
*)nsMemory::Alloc(number
* sizeof(struct NumArgState
));
640 for (i
= 0; i
< number
; i
++) {
641 nas
[i
].type
= TYPE_UNKNOWN
;
649 while ((c
= *p
++) != 0) {
658 /* should imporve error check later */
659 while (c
&& c
!= '$') {
660 cn
= cn
*10 + c
- '0';
664 if (!c
|| cn
< 1 || cn
> number
) {
669 /* nas[cn] starts from 0, and make sure
670 nas[cn].type is not assigned */
672 if (nas
[cn
].type
!= TYPE_UNKNOWN
) {
680 /* not supported feature, for the argument is not numbered */
684 while ((c
>= '0') && (c
<= '9')) {
693 /* not supported feature, for the argument is not numbered */
697 while ((c
>= '0') && (c
<= '9')) {
704 nas
[cn
].type
= TYPE_INTN
;
706 nas
[cn
].type
= TYPE_INT16
;
708 } else if (c
== 'L') {
709 /* XXX not quite sure here */
710 nas
[cn
].type
= TYPE_INT64
;
712 } else if (c
== 'l') {
713 nas
[cn
].type
= TYPE_INT32
;
716 nas
[cn
].type
= TYPE_INT64
;
735 nas
[cn
].type
= TYPE_DOUBLE
;
739 /* XXX should use cpp */
740 if (sizeof(void *) == sizeof(PRInt32
)) {
741 nas
[cn
].type
= TYPE_UINT32
;
742 } else if (sizeof(void *) == sizeof(PRInt64
)) {
743 nas
[cn
].type
= TYPE_UINT64
;
744 } else if (sizeof(void *) == sizeof(PRIntn
)) {
745 nas
[cn
].type
= TYPE_UINTN
;
747 nas
[cn
].type
= TYPE_UNKNOWN
;
752 /* XXX not supported I suppose */
754 nas
[cn
].type
= TYPE_UNKNOWN
;
758 nas
[cn
].type
= TYPE_UNISTRING
;
762 nas
[cn
].type
= TYPE_STRING
;
766 nas
[cn
].type
= TYPE_INTSTR
;
771 nas
[cn
].type
= TYPE_UNKNOWN
;
775 /* get a legal para. */
776 if (nas
[cn
].type
== TYPE_UNKNOWN
) {
785 ** fill the nas[cn].ap
788 if( nas
!= nasArray
) {
795 while (cn
< number
) {
796 if (nas
[cn
].type
== TYPE_UNKNOWN
) {
801 VARARGS_ASSIGN(nas
[cn
].ap
, ap
);
803 switch (nas
[cn
].type
) {
807 case TYPE_UINTN
: (void)va_arg(ap
, PRIntn
); break;
809 case TYPE_INT32
: (void)va_arg(ap
, PRInt32
); break;
811 case TYPE_UINT32
: (void)va_arg(ap
, PRUint32
); break;
813 case TYPE_INT64
: (void)va_arg(ap
, PRInt64
); break;
815 case TYPE_UINT64
: (void)va_arg(ap
, PRUint64
); break;
817 case TYPE_STRING
: (void)va_arg(ap
, char*); break;
819 case TYPE_INTSTR
: (void)va_arg(ap
, PRIntn
*); break;
821 case TYPE_DOUBLE
: (void)va_arg(ap
, double); break;
823 case TYPE_UNISTRING
: (void)va_arg(ap
, PRUnichar
*); break;
826 if( nas
!= nasArray
) {
838 ** The workhorse sprintf code.
840 static int dosprintf(SprintfState
*ss
, const PRUnichar
*fmt
, va_list ap
)
843 int flags
, width
, prec
, radix
, type
;
854 PRUnichar space
= ' ';
855 const PRUnichar
*fmt0
;
858 hex
.AssignLiteral("0123456789abcdef");
861 HEX
.AssignLiteral("0123456789ABCDEF");
863 const PRUnichar
*hexp
;
865 struct NumArgState
* nas
= NULL
;
866 struct NumArgState nasArray
[NAS_DEFAULT_NUM
];
867 /* in "%4$.2f" dolPt will point to . */
868 const PRUnichar
* dolPt
= NULL
;
872 ** build an argument array, IF the fmt is numbered argument
873 ** list style, to contain the Numbered Argument list pointers
875 nas
= BuildArgArray (fmt
, ap
, &rv
, nasArray
);
877 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
882 while ((c
= *fmt
++) != 0) {
884 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
893 ** Gobble up the % format string. Hopefully we have handled all
894 ** of the strange cases!
899 /* quoting a % with %% */
900 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
908 /* the fmt contains the Numbered Arguments feature */
910 /* should imporve error check later */
911 while (c
&& c
!= '$') {
912 i
= (i
* 10) + (c
- '0');
916 if (nas
[i
-1].type
== TYPE_UNKNOWN
) {
917 if (nas
&& (nas
!= nasArray
)) {
923 VARARGS_ASSIGN(ap
, nas
[i
-1].ap
);
929 * Examine optional flags. Note that we do not implement the
930 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
931 * somewhat ambiguous and not ideal, which is perhaps why
932 * the various sprintf() implementations are inconsistent
935 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
936 if (c
== '-') flags
|= _LEFT
;
937 if (c
== '+') flags
|= _SIGNED
;
938 if (c
== ' ') flags
|= _SPACED
;
939 if (c
== '0') flags
|= _ZEROS
;
942 if (flags
& _SIGNED
) flags
&= ~_SPACED
;
943 if (flags
& _LEFT
) flags
&= ~_ZEROS
;
948 width
= va_arg(ap
, int);
951 while ((c
>= '0') && (c
<= '9')) {
952 width
= (width
* 10) + (c
- '0');
963 prec
= va_arg(ap
, int);
966 while ((c
>= '0') && (c
<= '9')) {
967 prec
= (prec
* 10) + (c
- '0');
978 } else if (c
== 'L') {
979 /* XXX not quite sure here */
982 } else if (c
== 'l') {
995 case 'i': /* decimal/integer */
997 goto fetch_and_convert
;
999 case 'o': /* octal */
1002 goto fetch_and_convert
;
1004 case 'u': /* unsigned decimal */
1007 goto fetch_and_convert
;
1009 case 'x': /* unsigned hex */
1012 goto fetch_and_convert
;
1014 case 'X': /* unsigned HEX */
1018 goto fetch_and_convert
;
1023 u
.l
= va_arg(ap
, int);
1030 u
.l
= va_arg(ap
, int) & 0xffff;
1033 u
.l
= va_arg(ap
, int);
1040 u
.l
= (long)va_arg(ap
, unsigned int);
1044 u
.l
= va_arg(ap
, PRInt32
);
1051 u
.l
= (long)va_arg(ap
, PRUint32
);
1053 rv
= cvt_l(ss
, u
.l
, width
, prec
, radix
, type
, flags
, hexp
);
1060 u
.ll
= va_arg(ap
, PRInt64
);
1061 if (!LL_GE_ZERO(u
.ll
)) {
1067 u
.ll
= va_arg(ap
, PRUint64
);
1069 rv
= cvt_ll(ss
, u
.ll
, width
, prec
, radix
, type
, flags
, hexp
);
1082 u
.d
= va_arg(ap
, double);
1083 rv
= cvt_f(ss
, u
.d
, width
, prec
, c
, flags
);
1090 u
.ch
= va_arg(ap
, int);
1091 if ((flags
& _LEFT
) == 0) {
1092 while (width
-- > 1) {
1093 rv
= (*ss
->stuff
)(ss
, &space
, 1);
1099 rv
= (*ss
->stuff
)(ss
, &u
.ch
, 1);
1103 if (flags
& _LEFT
) {
1104 while (width
-- > 1) {
1105 rv
= (*ss
->stuff
)(ss
, &space
, 1);
1114 if (sizeof(void *) == sizeof(PRInt32
)) {
1116 } else if (sizeof(void *) == sizeof(PRInt64
)) {
1118 } else if (sizeof(void *) == sizeof(int)) {
1125 goto fetch_and_convert
;
1129 /* XXX not supported I suppose */
1135 u
.S
= va_arg(ap
, const PRUnichar
*);
1136 rv
= cvt_S(ss
, u
.S
, width
, prec
, flags
);
1143 u
.s
= va_arg(ap
, const char*);
1144 rv
= cvt_s(ss
, u
.s
, width
, prec
, flags
);
1151 u
.ip
= va_arg(ap
, int*);
1153 *u
.ip
= ss
->cur
- ss
->base
;
1158 /* Not a % token after all... skip it */
1162 PRUnichar perct
= '%';
1163 rv
= (*ss
->stuff
)(ss
, &perct
, 1);
1167 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
1174 /* Stuff trailing NUL */
1175 PRUnichar null
= '\0';
1177 rv
= (*ss
->stuff
)(ss
, &null
, 1);
1179 if( nas
&& ( nas
!= nasArray
) ){
1186 /************************************************************************/
1189 StringStuff(SprintfState
* ss
, const PRUnichar
* sp
, PRUint32 len
)
1191 ptrdiff_t off
= ss
->cur
- ss
->base
;
1193 nsAString
* str
= static_cast<nsAString
*>(ss
->stuffclosure
);
1194 str
->Append(sp
, len
);
1196 ss
->base
= str
->BeginWriting();
1197 ss
->cur
= ss
->base
+ off
;
1203 ** Stuff routine that automatically grows the malloc'd output buffer
1204 ** before it overflows.
1206 static int GrowStuff(SprintfState
*ss
, const PRUnichar
*sp
, PRUint32 len
)
1212 off
= ss
->cur
- ss
->base
;
1213 if (off
+ len
>= ss
->maxlen
) {
1214 /* Grow the buffer */
1215 newlen
= ss
->maxlen
+ ((len
> 32) ? len
: 32);
1217 newbase
= (PRUnichar
*) nsMemory::Realloc(ss
->base
, newlen
*sizeof(PRUnichar
));
1219 newbase
= (PRUnichar
*) nsMemory::Alloc(newlen
*sizeof(PRUnichar
));
1222 /* Ran out of memory */
1226 ss
->maxlen
= newlen
;
1227 ss
->cur
= ss
->base
+ off
;
1235 PR_ASSERT((PRUint32
)(ss
->cur
- ss
->base
) <= ss
->maxlen
);
1240 ** sprintf into a malloc'd buffer
1242 PRUnichar
* nsTextFormatter::smprintf(const PRUnichar
*fmt
, ...)
1248 rv
= nsTextFormatter::vsmprintf(fmt
, ap
);
1253 PRUint32
nsTextFormatter::ssprintf(nsAString
& out
, const PRUnichar
* fmt
, ...)
1259 rv
= nsTextFormatter::vssprintf(out
, fmt
, ap
);
1264 PRUint32
nsTextFormatter::vssprintf(nsAString
& out
, const PRUnichar
* fmt
, va_list ap
)
1267 ss
.stuff
= StringStuff
;
1271 ss
.stuffclosure
= &out
;
1274 int n
= dosprintf(&ss
, fmt
, ap
);
1275 return n
? n
- 1 : n
;
1278 PRUnichar
* nsTextFormatter::vsmprintf(const PRUnichar
*fmt
, va_list ap
)
1283 ss
.stuff
= GrowStuff
;
1287 rv
= dosprintf(&ss
, fmt
, ap
);
1298 ** Stuff routine that discards overflow data
1300 static int LimitStuff(SprintfState
*ss
, const PRUnichar
*sp
, PRUint32 len
)
1302 PRUint32 limit
= ss
->maxlen
- (ss
->cur
- ss
->base
);
1315 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1318 PRUint32
nsTextFormatter::snprintf(PRUnichar
*out
, PRUint32 outlen
, const PRUnichar
*fmt
, ...)
1323 PR_ASSERT((PRInt32
)outlen
> 0);
1324 if ((PRInt32
)outlen
<= 0) {
1329 rv
= nsTextFormatter::vsnprintf(out
, outlen
, fmt
, ap
);
1334 PRUint32
nsTextFormatter::vsnprintf(PRUnichar
*out
, PRUint32 outlen
,const PRUnichar
*fmt
,
1340 PR_ASSERT((PRInt32
)outlen
> 0);
1341 if ((PRInt32
)outlen
<= 0) {
1345 ss
.stuff
= LimitStuff
;
1349 (void) dosprintf(&ss
, fmt
, ap
);
1351 /* If we added chars, and we didn't append a null, do it now. */
1352 if( (ss
.cur
!= ss
.base
) && (*(ss
.cur
- 1) != '\0') )
1355 n
= ss
.cur
- ss
.base
;
1356 return n
? n
- 1 : n
;
1360 * Free memory allocated, for the caller, by smprintf
1362 void nsTextFormatter::smprintf_free(PRUnichar
*mem
)
1364 nsMemory::Free(mem
);