1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 Communicator client code, released
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
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 ** Author: Kipp E.B. Hickman
56 ** Note: on some platforms va_list is defined as an array,
57 ** and requires array notation.
60 #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
61 #elif defined(HAVE_VA_LIST_AS_ARRAY)
62 #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
64 #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
68 ** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it)
72 ** XXX This needs to be internationalized!
75 typedef struct SprintfStateStr SprintfState;
77 struct SprintfStateStr {
78 int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len);
84 int (*func)(void *arg, const char *sp, JSUint32 len);
89 ** Numbered Arguement State
92 int type; /* type of the current ap */
93 va_list ap; /* point to the corresponding position on ap */
96 #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
100 #define TYPE_UINT16 1
104 #define TYPE_UINT32 5
106 #define TYPE_UINT64 7
107 #define TYPE_STRING 8
108 #define TYPE_DOUBLE 9
109 #define TYPE_INTSTR 10
110 #define TYPE_WSTRING 11
111 #define TYPE_UNKNOWN 20
113 #define FLAG_LEFT 0x1
114 #define FLAG_SIGNED 0x2
115 #define FLAG_SPACED 0x4
116 #define FLAG_ZEROS 0x8
117 #define FLAG_NEG 0x10
120 ** Fill into the buffer using the data in src
122 static int fill2(SprintfState *ss, const char *src, int srclen, int width,
129 if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
130 if (flags & FLAG_ZEROS) {
133 while (--width >= 0) {
134 rv = (*ss->stuff)(ss, &space, 1);
141 /* Copy out the source data */
142 rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
147 if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
148 while (--width >= 0) {
149 rv = (*ss->stuff)(ss, &space, 1);
159 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
161 static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
162 int prec, int type, int flags)
173 if ((type & 1) == 0) {
174 if (flags & FLAG_NEG) {
177 } else if (flags & FLAG_SIGNED) {
180 } else if (flags & FLAG_SPACED) {
185 cvtwidth = signwidth + srclen;
189 precwidth = prec - srclen; /* Need zero filling */
190 cvtwidth += precwidth;
194 if ((flags & FLAG_ZEROS) && (prec < 0)) {
195 if (width > cvtwidth) {
196 zerowidth = width - cvtwidth; /* Zero filling */
197 cvtwidth += zerowidth;
201 if (flags & FLAG_LEFT) {
202 if (width > cvtwidth) {
203 /* Space filling on the right (i.e. left adjusting) */
204 rightspaces = width - cvtwidth;
207 if (width > cvtwidth) {
208 /* Space filling on the left (i.e. right adjusting) */
209 leftspaces = width - cvtwidth;
212 while (--leftspaces >= 0) {
213 rv = (*ss->stuff)(ss, " ", 1);
219 rv = (*ss->stuff)(ss, &sign, 1);
224 while (--precwidth >= 0) {
225 rv = (*ss->stuff)(ss, "0", 1);
230 while (--zerowidth >= 0) {
231 rv = (*ss->stuff)(ss, "0", 1);
236 rv = (*ss->stuff)(ss, src, (JSUint32)srclen);
240 while (--rightspaces >= 0) {
241 rv = (*ss->stuff)(ss, " ", 1);
250 ** Convert a long into its printable form
252 static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
253 int type, int flags, const char *hexp)
259 /* according to the man page this needs to happen */
260 if ((prec == 0) && (num == 0)) {
265 ** Converting decimal is a little tricky. In the unsigned case we
266 ** need to stop when we hit 10 digits. In the signed case, we can
267 ** stop when the number is zero.
269 cvt = cvtbuf + sizeof(cvtbuf);
272 int digit = (((unsigned long)num) % radix) & 0xF;
273 *--cvt = hexp[digit];
275 num = (long)(((unsigned long)num) / radix);
283 ** Now that we have the number converted without its sign, deal with
284 ** the sign and zero padding.
286 return fill_n(ss, cvt, digits, width, prec, type, flags);
290 ** Convert a 64-bit integer into its printable form
292 static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix,
293 int type, int flags, const char *hexp)
300 /* according to the man page this needs to happen */
301 if ((prec == 0) && (JSLL_IS_ZERO(num))) {
306 ** Converting decimal is a little tricky. In the unsigned case we
307 ** need to stop when we hit 10 digits. In the signed case, we can
308 ** stop when the number is zero.
310 JSLL_I2L(rad, radix);
311 cvt = cvtbuf + sizeof(cvtbuf);
313 while (!JSLL_IS_ZERO(num)) {
316 JSLL_UDIVMOD(", &rem, num, rad);
317 JSLL_L2I(digit, rem);
318 *--cvt = hexp[digit & 0xf];
328 ** Now that we have the number converted without its sign, deal with
329 ** the sign and zero padding.
331 return fill_n(ss, cvt, digits, width, prec, type, flags);
335 ** Convert a double precision floating point number into its printable
338 ** XXX stop using sprintf to convert floating point
340 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
344 int amount = fmt1 - fmt0;
346 JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
347 if (amount >= (int)sizeof(fin)) {
348 /* Totally bogus % command to sprintf. Just ignore it */
351 memcpy(fin, fmt0, (size_t)amount);
354 /* Convert floating point using the native sprintf code */
359 JS_ASSERT(*p != 'L');
364 sprintf(fout, fin, d);
367 ** This assert will catch overflow's of fout, when building with
368 ** debugging on. At least this way we can track down the evil piece
369 ** of calling code and fix it!
371 JS_ASSERT(strlen(fout) < sizeof(fout));
373 return (*ss->stuff)(ss, fout, strlen(fout));
377 ** Convert a string into its printable form. "width" is the output
378 ** width. "prec" is the maximum number of characters of "s" to output,
379 ** where -1 means until NUL.
381 static int cvt_s(SprintfState *ss, const char *s, int width, int prec,
389 /* Limit string length by precision value */
390 slen = s ? strlen(s) : 6;
398 return fill2(ss, s ? s : "(null)", slen, width, flags);
401 static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec,
406 * Supply NULL as the JSContext; errors are not reported,
407 * and malloc() is used to allocate the buffer buffer.
410 int slen = js_strlen(ws);
411 char *s = js_DeflateString(NULL, ws, slen);
413 return -1; /* JSStuffFunc error indicator. */
414 result = cvt_s(ss, s, width, prec, flags);
417 result = cvt_s(ss, NULL, width, prec, flags);
423 ** BuildArgArray stands for Numbered Argument list Sprintf
425 ** fmp = "%4$i, %2$d, %3s, %1d";
426 ** the number must start from 1, and no gap among them
429 static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )
431 int number = 0, cn = 0, i;
434 struct NumArgState *nas;
439 ** detemine how many legal % I have got, then allocate space
445 while( ( c = *p++ ) != 0 ){
448 if( ( c = *p++ ) == '%' ) /* skip %% case */
452 if( c > '9' || c < '0' ){
453 if( c == '$' ){ /* numbered argument csae */
459 } else { /* non-numbered argument case */
478 if( number > NAS_DEFAULT_NUM ){
479 nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) );
488 for( i = 0; i < number; i++ ){
489 nas[i].type = TYPE_UNKNOWN;
499 while( ( c = *p++ ) != 0 ){
500 if( c != '%' ) continue;
502 if( c == '%' ) continue;
505 while( c && c != '$' ){ /* should improve error check later */
506 cn = cn*10 + c - '0';
510 if( !c || cn < 1 || cn > number ){
515 /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
517 if( nas[cn].type != TYPE_UNKNOWN )
524 /* not supported feature, for the argument is not numbered */
529 while ((c >= '0') && (c <= '9')) {
537 /* not supported feature, for the argument is not numbered */
542 while ((c >= '0') && (c <= '9')) {
548 nas[cn].type = TYPE_INTN;
550 nas[cn].type = TYPE_INT16;
552 } else if (c == 'L') {
553 /* XXX not quite sure here */
554 nas[cn].type = TYPE_INT64;
556 } else if (c == 'l') {
557 nas[cn].type = TYPE_INT32;
560 nas[cn].type = TYPE_INT64;
579 nas[ cn ].type = TYPE_DOUBLE;
583 /* XXX should use cpp */
584 if (sizeof(void *) == sizeof(JSInt32)) {
585 nas[ cn ].type = TYPE_UINT32;
586 } else if (sizeof(void *) == sizeof(JSInt64)) {
587 nas[ cn ].type = TYPE_UINT64;
588 } else if (sizeof(void *) == sizeof(JSIntn)) {
589 nas[ cn ].type = TYPE_UINTN;
591 nas[ cn ].type = TYPE_UNKNOWN;
599 /* XXX not supported I suppose */
601 nas[ cn ].type = TYPE_UNKNOWN;
605 nas[ cn ].type = (nas[ cn ].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
609 nas[ cn ].type = TYPE_INTSTR;
614 nas[ cn ].type = TYPE_UNKNOWN;
618 /* get a legal para. */
619 if( nas[ cn ].type == TYPE_UNKNOWN ){
628 ** fill the nas[cn].ap
632 if( nas != nasArray )
638 while( cn < number ){
639 if( nas[cn].type == TYPE_UNKNOWN ){
644 VARARGS_ASSIGN(nas[cn].ap, ap);
646 switch( nas[cn].type ){
650 case TYPE_UINTN: (void)va_arg( ap, JSIntn ); break;
652 case TYPE_INT32: (void)va_arg( ap, JSInt32 ); break;
654 case TYPE_UINT32: (void)va_arg( ap, JSUint32 ); break;
656 case TYPE_INT64: (void)va_arg( ap, JSInt64 ); break;
658 case TYPE_UINT64: (void)va_arg( ap, JSUint64 ); break;
660 case TYPE_STRING: (void)va_arg( ap, char* ); break;
662 case TYPE_WSTRING: (void)va_arg( ap, jschar* ); break;
664 case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break;
666 case TYPE_DOUBLE: (void)va_arg( ap, double ); break;
669 if( nas != nasArray )
683 ** The workhorse sprintf code.
685 static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
688 int flags, width, prec, radix, type;
701 static const char hex[] = "0123456789abcdef";
702 static const char HEX[] = "0123456789ABCDEF";
705 struct NumArgState *nas = NULL;
706 struct NumArgState nasArray[ NAS_DEFAULT_NUM ];
708 const char *dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */
713 ** build an argument array, IF the fmt is numbered argument
714 ** list style, to contain the Numbered Argument list pointers
717 nas = BuildArgArray( fmt, ap, &rv, nasArray );
719 /* the fmt contains error Numbered Argument format, jliu@netscape.com */
724 while ((c = *fmt++) != 0) {
726 rv = (*ss->stuff)(ss, fmt - 1, 1);
735 ** Gobble up the % format string. Hopefully we have handled all
736 ** of the strange cases!
741 /* quoting a % with %% */
742 rv = (*ss->stuff)(ss, fmt - 1, 1);
750 /* the fmt contains the Numbered Arguments feature */
752 while( c && c != '$' ){ /* should imporve error check later */
753 i = ( i * 10 ) + ( c - '0' );
757 if( nas[i-1].type == TYPE_UNKNOWN ){
758 if( nas && ( nas != nasArray ) )
769 * Examine optional flags. Note that we do not implement the
770 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
771 * somewhat ambiguous and not ideal, which is perhaps why
772 * the various sprintf() implementations are inconsistent
775 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
776 if (c == '-') flags |= FLAG_LEFT;
777 if (c == '+') flags |= FLAG_SIGNED;
778 if (c == ' ') flags |= FLAG_SPACED;
779 if (c == '0') flags |= FLAG_ZEROS;
782 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
783 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
788 width = va_arg(ap, int);
791 while ((c >= '0') && (c <= '9')) {
792 width = (width * 10) + (c - '0');
803 prec = va_arg(ap, int);
806 while ((c >= '0') && (c <= '9')) {
807 prec = (prec * 10) + (c - '0');
818 } else if (c == 'L') {
819 /* XXX not quite sure here */
822 } else if (c == 'l') {
834 case 'd': case 'i': /* decimal/integer */
836 goto fetch_and_convert;
838 case 'o': /* octal */
841 goto fetch_and_convert;
843 case 'u': /* unsigned decimal */
846 goto fetch_and_convert;
848 case 'x': /* unsigned hex */
851 goto fetch_and_convert;
853 case 'X': /* unsigned HEX */
857 goto fetch_and_convert;
862 u.l = va_arg(ap, int);
869 u.l = va_arg(ap, int) & 0xffff;
872 u.l = va_arg(ap, int);
879 u.l = (long)va_arg(ap, unsigned int);
883 u.l = va_arg(ap, JSInt32);
890 u.l = (long)va_arg(ap, JSUint32);
892 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
899 u.ll = va_arg(ap, JSInt64);
900 if (!JSLL_GE_ZERO(u.ll)) {
901 JSLL_NEG(u.ll, u.ll);
906 u.ll = va_arg(ap, JSUint64);
908 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
920 u.d = va_arg(ap, double);
923 if( i < (int)sizeof( pattern ) ){
925 memcpy( &pattern[1], dolPt, (size_t)i );
926 rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
929 rv = cvt_f(ss, u.d, fmt0, fmt);
937 if ((flags & FLAG_LEFT) == 0) {
938 while (width-- > 1) {
939 rv = (*ss->stuff)(ss, " ", 1);
947 /* Treat %hc as %c unless js_CStringsAreUTF8. */
948 if (js_CStringsAreUTF8) {
949 u.wch = va_arg(ap, int);
950 utf8len = js_OneUcs4ToUtf8Char (utf8buf, u.wch);
951 rv = (*ss->stuff)(ss, (char *)utf8buf, utf8len);
955 u.ch = va_arg(ap, int);
956 rv = (*ss->stuff)(ss, &u.ch, 1);
962 if (flags & FLAG_LEFT) {
963 while (width-- > 1) {
964 rv = (*ss->stuff)(ss, " ", 1);
973 if (sizeof(void *) == sizeof(JSInt32)) {
975 } else if (sizeof(void *) == sizeof(JSInt64)) {
977 } else if (sizeof(void *) == sizeof(int)) {
984 goto fetch_and_convert;
991 /* XXX not supported I suppose */
997 if(type == TYPE_INT16) {
999 * This would do a simple string/byte conversion
1000 * unless js_CStringsAreUTF8.
1002 u.ws = va_arg(ap, const jschar*);
1003 rv = cvt_ws(ss, u.ws, width, prec, flags);
1005 u.s = va_arg(ap, const char*);
1006 rv = cvt_s(ss, u.s, width, prec, flags);
1014 u.ip = va_arg(ap, int*);
1016 *u.ip = ss->cur - ss->base;
1021 /* Not a % token after all... skip it */
1025 rv = (*ss->stuff)(ss, "%", 1);
1029 rv = (*ss->stuff)(ss, fmt - 1, 1);
1036 /* Stuff trailing NUL */
1037 rv = (*ss->stuff)(ss, "\0", 1);
1039 if( nas && ( nas != nasArray ) ){
1046 /************************************************************************/
1048 static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)
1052 rv = (*ss->func)(ss->arg, sp, len);
1060 JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg,
1061 const char *fmt, ...)
1067 rv = JS_vsxprintf(func, arg, fmt, ap);
1072 JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg,
1073 const char *fmt, va_list ap)
1078 ss.stuff = FuncStuff;
1082 rv = dosprintf(&ss, fmt, ap);
1083 return (rv < 0) ? (JSUint32)-1 : ss.maxlen;
1087 ** Stuff routine that automatically grows the malloc'd output buffer
1088 ** before it overflows.
1090 static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len)
1096 off = ss->cur - ss->base;
1097 if (off + len >= ss->maxlen) {
1098 /* Grow the buffer */
1099 newlen = ss->maxlen + ((len > 32) ? len : 32);
1101 newbase = (char*) js_realloc(ss->base, newlen);
1103 newbase = (char*) js_malloc(newlen);
1106 /* Ran out of memory */
1110 ss->maxlen = newlen;
1111 ss->cur = ss->base + off;
1119 JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen);
1124 ** sprintf into a malloc'd buffer
1126 JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...)
1132 rv = JS_vsmprintf(fmt, ap);
1138 ** Free memory allocated, for the caller, by JS_smprintf
1140 JS_PUBLIC_API(void) JS_smprintf_free(char *mem)
1145 JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap)
1150 ss.stuff = GrowStuff;
1154 rv = dosprintf(&ss, fmt, ap);
1165 ** Stuff routine that discards overflow data
1167 static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len)
1169 JSUint32 limit = ss->maxlen - (ss->cur - ss->base);
1182 ** sprintf into a fixed size buffer. Make sure there is a NUL at the end
1185 JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...)
1190 JS_ASSERT((JSInt32)outlen > 0);
1191 if ((JSInt32)outlen <= 0) {
1196 rv = JS_vsnprintf(out, outlen, fmt, ap);
1201 JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt,
1207 JS_ASSERT((JSInt32)outlen > 0);
1208 if ((JSInt32)outlen <= 0) {
1212 ss.stuff = LimitStuff;
1216 (void) dosprintf(&ss, fmt, ap);
1218 /* If we added chars, and we didn't append a null, do it now. */
1219 if( (ss.cur != ss.base) && (ss.cur[-1] != '\0') )
1222 n = ss.cur - ss.base;
1223 return n ? n - 1 : n;
1226 JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...)
1232 rv = JS_vsprintf_append(last, fmt, ap);
1237 JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap)
1242 ss.stuff = GrowStuff;
1244 int lastlen = strlen(last);
1246 ss.cur = last + lastlen;
1247 ss.maxlen = lastlen;
1253 rv = dosprintf(&ss, fmt, ap);