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 the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 ** Portable safe sprintf code.
41 ** Author: Kipp E.B. Hickman
54 ** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
58 ** XXX This needs to be internationalized!
61 typedef struct SprintfStateStr SprintfState
;
63 struct SprintfStateStr
{
64 int (*stuff
)(SprintfState
*ss
, const char *sp
, PRUint32 len
);
70 int (*func
)(void *arg
, const char *sp
, PRUint32 len
);
78 int type
; /* type of the numbered argument */
79 union { /* the numbered argument */
92 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
100 #define TYPE_UINT32 5
102 #define TYPE_UINT64 7
103 #define TYPE_STRING 8
104 #define TYPE_DOUBLE 9
105 #define TYPE_INTSTR 10
106 #define TYPE_UNKNOWN 20
108 #define FLAG_LEFT 0x1
109 #define FLAG_SIGNED 0x2
110 #define FLAG_SPACED 0x4
111 #define FLAG_ZEROS 0x8
112 #define FLAG_NEG 0x10
115 ** Fill into the buffer using the data in src
117 static int fill2(SprintfState
*ss
, const char *src
, int srclen
, int width
,
124 if ((width
> 0) && ((flags
& FLAG_LEFT
) == 0)) { /* Right adjusting */
125 if (flags
& FLAG_ZEROS
) {
128 while (--width
>= 0) {
129 rv
= (*ss
->stuff
)(ss
, &space
, 1);
136 /* Copy out the source data */
137 rv
= (*ss
->stuff
)(ss
, src
, srclen
);
142 if ((width
> 0) && ((flags
& FLAG_LEFT
) != 0)) { /* Left adjusting */
143 while (--width
>= 0) {
144 rv
= (*ss
->stuff
)(ss
, &space
, 1);
154 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
156 static int fill_n(SprintfState
*ss
, const char *src
, int srclen
, int width
,
157 int prec
, int type
, int flags
)
168 if ((type
& 1) == 0) {
169 if (flags
& FLAG_NEG
) {
172 } else if (flags
& FLAG_SIGNED
) {
175 } else if (flags
& FLAG_SPACED
) {
180 cvtwidth
= signwidth
+ srclen
;
184 precwidth
= prec
- srclen
; /* Need zero filling */
185 cvtwidth
+= precwidth
;
189 if ((flags
& FLAG_ZEROS
) && (prec
< 0)) {
190 if (width
> cvtwidth
) {
191 zerowidth
= width
- cvtwidth
; /* Zero filling */
192 cvtwidth
+= zerowidth
;
196 if (flags
& FLAG_LEFT
) {
197 if (width
> cvtwidth
) {
198 /* Space filling on the right (i.e. left adjusting) */
199 rightspaces
= width
- cvtwidth
;
202 if (width
> cvtwidth
) {
203 /* Space filling on the left (i.e. right adjusting) */
204 leftspaces
= width
- cvtwidth
;
207 while (--leftspaces
>= 0) {
208 rv
= (*ss
->stuff
)(ss
, " ", 1);
214 rv
= (*ss
->stuff
)(ss
, &sign
, 1);
219 while (--precwidth
>= 0) {
220 rv
= (*ss
->stuff
)(ss
, "0", 1);
225 while (--zerowidth
>= 0) {
226 rv
= (*ss
->stuff
)(ss
, "0", 1);
231 rv
= (*ss
->stuff
)(ss
, src
, srclen
);
235 while (--rightspaces
>= 0) {
236 rv
= (*ss
->stuff
)(ss
, " ", 1);
245 ** Convert a long into its printable form
247 static int cvt_l(SprintfState
*ss
, long num
, int width
, int prec
, int radix
,
248 int type
, int flags
, const char *hexp
)
254 /* according to the man page this needs to happen */
255 if ((prec
== 0) && (num
== 0)) {
260 ** Converting decimal is a little tricky. In the unsigned case we
261 ** need to stop when we hit 10 digits. In the signed case, we can
262 ** stop when the number is zero.
264 cvt
= cvtbuf
+ sizeof(cvtbuf
);
267 int digit
= (((unsigned long)num
) % radix
) & 0xF;
268 *--cvt
= hexp
[digit
];
270 num
= (long)(((unsigned long)num
) / radix
);
278 ** Now that we have the number converted without its sign, deal with
279 ** the sign and zero padding.
281 return fill_n(ss
, cvt
, digits
, width
, prec
, type
, flags
);
285 ** Convert a 64-bit integer into its printable form
287 static int cvt_ll(SprintfState
*ss
, PRInt64 num
, int width
, int prec
, int radix
,
288 int type
, int flags
, const char *hexp
)
295 /* according to the man page this needs to happen */
296 if ((prec
== 0) && (LL_IS_ZERO(num
))) {
301 ** Converting decimal is a little tricky. In the unsigned case we
302 ** need to stop when we hit 10 digits. In the signed case, we can
303 ** stop when the number is zero.
306 cvt
= cvtbuf
+ sizeof(cvtbuf
);
308 while (!LL_IS_ZERO(num
)) {
311 LL_UDIVMOD("
, &rem
, num
, rad
);
313 *--cvt
= hexp
[digit
& 0xf];
323 ** Now that we have the number converted without its sign, deal with
324 ** the sign and zero padding.
326 return fill_n(ss
, cvt
, digits
, width
, prec
, type
, flags
);
330 ** Convert a double precision floating point number into its printable
333 ** XXX stop using sprintf to convert floating point
335 static int cvt_f(SprintfState
*ss
, double d
, const char *fmt0
, const char *fmt1
)
339 int amount
= fmt1
- fmt0
;
341 PR_ASSERT((amount
> 0) && (amount
< sizeof(fin
)));
342 if (amount
>= sizeof(fin
)) {
343 /* Totally bogus % command to sprintf. Just ignore it */
346 memcpy(fin
, fmt0
, amount
);
349 /* Convert floating point using the native sprintf code */
354 PR_ASSERT(*p
!= 'L');
359 sprintf(fout
, fin
, d
);
362 ** This assert will catch overflow's of fout, when building with
363 ** debugging on. At least this way we can track down the evil piece
364 ** of calling code and fix it!
366 PR_ASSERT(strlen(fout
) < sizeof(fout
));
368 return (*ss
->stuff
)(ss
, fout
, strlen(fout
));
372 ** Convert a string into its printable form. "width" is the output
373 ** width. "prec" is the maximum number of characters of "s" to output,
374 ** where -1 means until NUL.
376 static int cvt_s(SprintfState
*ss
, const char *str
, int width
, int prec
,
384 /* Limit string length by precision value */
389 /* this is: slen = strnlen(str, prec); */
390 register const char *s
;
392 for(s
= str
; prec
&& *s
; s
++, prec
-- )
400 return fill2(ss
, str
, slen
, width
, flags
);
404 ** BuildArgArray stands for Numbered Argument list Sprintf
406 ** fmp = "%4$i, %2$d, %3s, %1d";
407 ** the number must start from 1, and no gap among them
410 static struct NumArg
* BuildArgArray( const char *fmt
, va_list ap
, int* rv
, struct NumArg
* nasArray
)
412 int number
= 0, cn
= 0, i
;
420 ** determine how many legal % I have got, then allocate space
426 while( ( c
= *p
++ ) != 0 ){
429 if( ( c
= *p
++ ) == '%' ) /* skip %% case */
433 if( c
> '9' || c
< '0' ){
434 if( c
== '$' ){ /* numbered argument case */
440 } else{ /* non-numbered argument case */
459 if( number
> NAS_DEFAULT_NUM
){
460 nas
= (struct NumArg
*)PR_MALLOC( number
* sizeof( struct NumArg
) );
469 for( i
= 0; i
< number
; i
++ ){
470 nas
[i
].type
= TYPE_UNKNOWN
;
480 while( ( c
= *p
++ ) != 0 ){
481 if( c
!= '%' ) continue;
483 if( c
== '%' ) continue;
486 while( c
&& c
!= '$' ){ /* should imporve error check later */
487 cn
= cn
*10 + c
- '0';
491 if( !c
|| cn
< 1 || cn
> number
){
496 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
498 if( nas
[cn
].type
!= TYPE_UNKNOWN
)
505 /* not supported feature, for the argument is not numbered */
510 while ((c
>= '0') && (c
<= '9')) {
518 /* not supported feature, for the argument is not numbered */
523 while ((c
>= '0') && (c
<= '9')) {
529 nas
[cn
].type
= TYPE_INTN
;
531 nas
[cn
].type
= TYPE_INT16
;
533 } else if (c
== 'L') {
534 /* XXX not quite sure here */
535 nas
[cn
].type
= TYPE_INT64
;
537 } else if (c
== 'l') {
538 nas
[cn
].type
= TYPE_INT32
;
541 nas
[cn
].type
= TYPE_INT64
;
560 nas
[ cn
].type
= TYPE_DOUBLE
;
564 /* XXX should use cpp */
565 if (sizeof(void *) == sizeof(PRInt32
)) {
566 nas
[ cn
].type
= TYPE_UINT32
;
567 } else if (sizeof(void *) == sizeof(PRInt64
)) {
568 nas
[ cn
].type
= TYPE_UINT64
;
569 } else if (sizeof(void *) == sizeof(PRIntn
)) {
570 nas
[ cn
].type
= TYPE_UINTN
;
572 nas
[ cn
].type
= TYPE_UNKNOWN
;
580 /* XXX not supported I suppose */
582 nas
[ cn
].type
= TYPE_UNKNOWN
;
586 nas
[ cn
].type
= TYPE_STRING
;
590 nas
[ cn
].type
= TYPE_INTSTR
;
595 nas
[ cn
].type
= TYPE_UNKNOWN
;
599 /* get a legal para. */
600 if( nas
[ cn
].type
== TYPE_UNKNOWN
){
609 ** fill the nas[cn].ap
613 if( nas
!= nasArray
)
619 while( cn
< number
){
620 if( nas
[cn
].type
== TYPE_UNKNOWN
){
625 switch( nas
[cn
].type
){
629 nas
[cn
].u
.i
= va_arg( ap
, int );
633 nas
[cn
].u
.ui
= va_arg( ap
, unsigned int );
637 nas
[cn
].u
.i32
= va_arg( ap
, PRInt32
);
641 nas
[cn
].u
.ui32
= va_arg( ap
, PRUint32
);
645 nas
[cn
].u
.ll
= va_arg( ap
, PRInt64
);
649 nas
[cn
].u
.ull
= va_arg( ap
, PRUint64
);
653 nas
[cn
].u
.s
= va_arg( ap
, char* );
657 nas
[cn
].u
.ip
= va_arg( ap
, int* );
661 nas
[cn
].u
.d
= va_arg( ap
, double );
665 if( nas
!= nasArray
)
679 ** The workhorse sprintf code.
681 static int dosprintf(SprintfState
*ss
, const char *fmt
, va_list ap
)
684 int flags
, width
, prec
, radix
, type
;
695 static char *hex
= "0123456789abcdef";
696 static char *HEX
= "0123456789ABCDEF";
699 struct NumArg
* nas
= NULL
;
701 struct NumArg nasArray
[ NAS_DEFAULT_NUM
];
703 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 imporve error check later */
747 i
= ( i
* 10 ) + ( c
- '0' );
751 if( nas
[i
-1].type
== TYPE_UNKNOWN
){
752 if( nas
&& ( nas
!= nasArray
) )
763 * Examine optional flags. Note that we do not implement the
764 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
765 * somewhat ambiguous and not ideal, which is perhaps why
766 * the various sprintf() implementations are inconsistent
769 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
770 if (c
== '-') flags
|= FLAG_LEFT
;
771 if (c
== '+') flags
|= FLAG_SIGNED
;
772 if (c
== ' ') flags
|= FLAG_SPACED
;
773 if (c
== '0') flags
|= FLAG_ZEROS
;
776 if (flags
& FLAG_SIGNED
) flags
&= ~FLAG_SPACED
;
777 if (flags
& FLAG_LEFT
) flags
&= ~FLAG_ZEROS
;
782 width
= va_arg(ap
, int);
785 while ((c
>= '0') && (c
<= '9')) {
786 width
= (width
* 10) + (c
- '0');
797 prec
= va_arg(ap
, int);
800 while ((c
>= '0') && (c
<= '9')) {
801 prec
= (prec
* 10) + (c
- '0');
812 } else if (c
== 'L') {
813 /* XXX not quite sure here */
816 } else if (c
== 'l') {
828 case 'd': case 'i': /* decimal/integer */
830 goto fetch_and_convert
;
832 case 'o': /* octal */
835 goto fetch_and_convert
;
837 case 'u': /* unsigned decimal */
840 goto fetch_and_convert
;
842 case 'x': /* unsigned hex */
845 goto fetch_and_convert
;
847 case 'X': /* unsigned HEX */
851 goto fetch_and_convert
;
856 u
.l
= nas
? nap
->u
.i
: va_arg(ap
, int);
863 u
.l
= (nas
? nap
->u
.i
: va_arg(ap
, int)) & 0xffff;
866 u
.l
= nas
? nap
->u
.i
: va_arg(ap
, int);
873 u
.l
= (long)(nas
? nap
->u
.ui
: va_arg(ap
, unsigned int));
877 u
.l
= nas
? nap
->u
.i32
: va_arg(ap
, PRInt32
);
884 u
.l
= (long)(nas
? nap
->u
.ui32
: va_arg(ap
, PRUint32
));
886 rv
= cvt_l(ss
, u
.l
, width
, prec
, radix
, type
, flags
, hexp
);
893 u
.ll
= nas
? nap
->u
.ll
: va_arg(ap
, PRInt64
);
894 if (!LL_GE_ZERO(u
.ll
)) {
900 u
.ll
= nas
? nap
->u
.ull
: va_arg(ap
, PRUint64
);
902 rv
= cvt_ll(ss
, u
.ll
, width
, prec
, radix
, type
, flags
, hexp
);
914 u
.d
= nas
? nap
->u
.d
: va_arg(ap
, double);
917 if( i
< sizeof( pattern
) ){
919 memcpy( &pattern
[1], dolPt
, i
);
920 rv
= cvt_f(ss
, u
.d
, pattern
, &pattern
[i
+1] );
923 rv
= cvt_f(ss
, u
.d
, fmt0
, fmt
);
931 u
.ch
= nas
? nap
->u
.i
: va_arg(ap
, int);
932 if ((flags
& FLAG_LEFT
) == 0) {
933 while (width
-- > 1) {
934 rv
= (*ss
->stuff
)(ss
, " ", 1);
940 rv
= (*ss
->stuff
)(ss
, &u
.ch
, 1);
944 if (flags
& FLAG_LEFT
) {
945 while (width
-- > 1) {
946 rv
= (*ss
->stuff
)(ss
, " ", 1);
955 if (sizeof(void *) == sizeof(PRInt32
)) {
957 } else if (sizeof(void *) == sizeof(PRInt64
)) {
959 } else if (sizeof(void *) == sizeof(int)) {
966 goto fetch_and_convert
;
973 /* XXX not supported I suppose */
979 u
.s
= nas
? nap
->u
.s
: va_arg(ap
, const char*);
980 rv
= cvt_s(ss
, u
.s
, width
, prec
, flags
);
987 u
.ip
= nas
? nap
->u
.ip
: va_arg(ap
, int*);
989 *u
.ip
= ss
->cur
- ss
->base
;
994 /* Not a % token after all... skip it */
998 rv
= (*ss
->stuff
)(ss
, "%", 1);
1002 rv
= (*ss
->stuff
)(ss
, fmt
- 1, 1);
1009 /* Stuff trailing NUL */
1010 rv
= (*ss
->stuff
)(ss
, "\0", 1);
1012 if( nas
&& ( nas
!= nasArray
) ){
1019 /************************************************************************/
1021 static int FuncStuff(SprintfState
*ss
, const char *sp
, PRUint32 len
)
1025 rv
= (*ss
->func
)(ss
->arg
, sp
, len
);
1033 PR_IMPLEMENT(PRUint32
) PR_sxprintf(PRStuffFunc func
, void *arg
,
1034 const char *fmt
, ...)
1040 rv
= PR_vsxprintf(func
, arg
, fmt
, ap
);
1045 PR_IMPLEMENT(PRUint32
) PR_vsxprintf(PRStuffFunc func
, void *arg
,
1046 const char *fmt
, va_list ap
)
1051 ss
.stuff
= FuncStuff
;
1055 rv
= dosprintf(&ss
, fmt
, ap
);
1056 return (rv
< 0) ? (PRUint32
)-1 : ss
.maxlen
;
1060 ** Stuff routine that automatically grows the malloc'd output buffer
1061 ** before it overflows.
1063 static int GrowStuff(SprintfState
*ss
, const char *sp
, PRUint32 len
)
1069 off
= ss
->cur
- ss
->base
;
1070 if (off
+ len
>= ss
->maxlen
) {
1071 /* Grow the buffer */
1072 newlen
= ss
->maxlen
+ ((len
> 32) ? len
: 32);
1074 newbase
= (char*) PR_REALLOC(ss
->base
, newlen
);
1076 newbase
= (char*) PR_MALLOC(newlen
);
1079 /* Ran out of memory */
1083 ss
->maxlen
= newlen
;
1084 ss
->cur
= ss
->base
+ off
;
1092 PR_ASSERT((PRUint32
)(ss
->cur
- ss
->base
) <= ss
->maxlen
);
1097 ** sprintf into a malloc'd buffer
1099 PR_IMPLEMENT(char *) PR_smprintf(const char *fmt
, ...)
1105 rv
= PR_vsmprintf(fmt
, ap
);
1111 ** Free memory allocated, for the caller, by PR_smprintf
1113 PR_IMPLEMENT(void) PR_smprintf_free(char *mem
)
1118 PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt
, va_list ap
)
1123 ss
.stuff
= GrowStuff
;
1127 rv
= dosprintf(&ss
, fmt
, ap
);
1138 ** Stuff routine that discards overflow data
1140 static int LimitStuff(SprintfState
*ss
, const char *sp
, PRUint32 len
)
1142 PRUint32 limit
= ss
->maxlen
- (ss
->cur
- ss
->base
);
1155 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1158 PR_IMPLEMENT(PRUint32
) PR_snprintf(char *out
, PRUint32 outlen
, const char *fmt
, ...)
1163 PR_ASSERT((PRInt32
)outlen
> 0);
1164 if ((PRInt32
)outlen
<= 0) {
1169 rv
= PR_vsnprintf(out
, outlen
, fmt
, ap
);
1174 PR_IMPLEMENT(PRUint32
) PR_vsnprintf(char *out
, PRUint32 outlen
,const char *fmt
,
1180 PR_ASSERT((PRInt32
)outlen
> 0);
1181 if ((PRInt32
)outlen
<= 0) {
1185 ss
.stuff
= LimitStuff
;
1189 (void) dosprintf(&ss
, fmt
, ap
);
1191 /* If we added chars, and we didn't append a null, do it now. */
1192 if( (ss
.cur
!= ss
.base
) && (*(ss
.cur
- 1) != '\0') )
1193 *(ss
.cur
- 1) = '\0';
1195 n
= ss
.cur
- ss
.base
;
1196 return n
? n
- 1 : n
;
1199 PR_IMPLEMENT(char *) PR_sprintf_append(char *last
, const char *fmt
, ...)
1205 rv
= PR_vsprintf_append(last
, fmt
, ap
);
1210 PR_IMPLEMENT(char *) PR_vsprintf_append(char *last
, const char *fmt
, va_list ap
)
1215 ss
.stuff
= GrowStuff
;
1217 int lastlen
= strlen(last
);
1219 ss
.cur
= last
+ lastlen
;
1220 ss
.maxlen
= lastlen
;
1226 rv
= dosprintf(&ss
, fmt
, ap
);