michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Portable safe sprintf code. michael@0: * michael@0: * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7 michael@0: * michael@0: * Contributor(s): michael@0: * Kipp E.B. Hickman (original author) michael@0: * Frank Yung-Fong Tang michael@0: * Daniele Nicolodi michael@0: */ michael@0: michael@0: /* michael@0: * Copied from xpcom/ds/nsTextFormatter.cpp r1.22 michael@0: * Changed to use nsMemory and Frozen linkage michael@0: * -- Prasad michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "prdtoa.h" michael@0: #include "prlog.h" michael@0: #include "prprf.h" michael@0: #include "prmem.h" michael@0: #include "nsCRTGlue.h" michael@0: #include "nsTextFormatter.h" michael@0: #include "nsMemory.h" michael@0: michael@0: /* michael@0: ** Note: on some platforms va_list is defined as an array, michael@0: ** and requires array notation. michael@0: */ michael@0: michael@0: #ifdef HAVE_VA_COPY michael@0: #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) michael@0: #elif defined(HAVE_VA_LIST_AS_ARRAY) michael@0: #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] michael@0: #else michael@0: #define VARARGS_ASSIGN(foo, bar) (foo) = (bar) michael@0: #endif michael@0: michael@0: typedef struct SprintfStateStr SprintfState; michael@0: michael@0: struct SprintfStateStr { michael@0: int (*stuff)(SprintfState *ss, const char16_t *sp, uint32_t len); michael@0: michael@0: char16_t *base; michael@0: char16_t *cur; michael@0: uint32_t maxlen; michael@0: michael@0: void *stuffclosure; michael@0: }; michael@0: michael@0: /* michael@0: ** Numbered Arguement State michael@0: */ michael@0: struct NumArgState{ michael@0: int type; /* type of the current ap */ michael@0: va_list ap; /* point to the corresponding position on ap */ michael@0: michael@0: enum Type { michael@0: INT16, michael@0: UINT16, michael@0: INTN, michael@0: UINTN, michael@0: INT32, michael@0: UINT32, michael@0: INT64, michael@0: UINT64, michael@0: STRING, michael@0: DOUBLE, michael@0: INTSTR, michael@0: UNISTRING, michael@0: UNKNOWN michael@0: }; michael@0: }; michael@0: michael@0: #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */ michael@0: michael@0: #define _LEFT 0x1 michael@0: #define _SIGNED 0x2 michael@0: #define _SPACED 0x4 michael@0: #define _ZEROS 0x8 michael@0: #define _NEG 0x10 michael@0: michael@0: #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0])) michael@0: michael@0: /* michael@0: ** Fill into the buffer using the data in src michael@0: */ michael@0: static int fill2(SprintfState *ss, const char16_t *src, int srclen, michael@0: int width, int flags) michael@0: { michael@0: char16_t space = ' '; michael@0: int rv; michael@0: michael@0: width -= srclen; michael@0: /* Right adjusting */ michael@0: if ((width > 0) && ((flags & _LEFT) == 0)) { michael@0: if (flags & _ZEROS) { michael@0: space = '0'; michael@0: } michael@0: while (--width >= 0) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* Copy out the source data */ michael@0: rv = (*ss->stuff)(ss, src, srclen); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: michael@0: /* Left adjusting */ michael@0: if ((width > 0) && ((flags & _LEFT) != 0)) { michael@0: while (--width >= 0) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** Fill a number. The order is: optional-sign zero-filling conversion-digits michael@0: */ michael@0: static int fill_n(SprintfState *ss, const char16_t *src, int srclen, michael@0: int width, int prec, int type, int flags) michael@0: { michael@0: int zerowidth = 0; michael@0: int precwidth = 0; michael@0: int signwidth = 0; michael@0: int leftspaces = 0; michael@0: int rightspaces = 0; michael@0: int cvtwidth; michael@0: int rv; michael@0: char16_t sign; michael@0: char16_t space = ' '; michael@0: char16_t zero = '0'; michael@0: michael@0: if ((type & 1) == 0) { michael@0: if (flags & _NEG) { michael@0: sign = '-'; michael@0: signwidth = 1; michael@0: } else if (flags & _SIGNED) { michael@0: sign = '+'; michael@0: signwidth = 1; michael@0: } else if (flags & _SPACED) { michael@0: sign = ' '; michael@0: signwidth = 1; michael@0: } michael@0: } michael@0: cvtwidth = signwidth + srclen; michael@0: michael@0: if (prec > 0) { michael@0: if (prec > srclen) { michael@0: /* Need zero filling */ michael@0: precwidth = prec - srclen; michael@0: cvtwidth += precwidth; michael@0: } michael@0: } michael@0: michael@0: if ((flags & _ZEROS) && (prec < 0)) { michael@0: if (width > cvtwidth) { michael@0: /* Zero filling */ michael@0: zerowidth = width - cvtwidth; michael@0: cvtwidth += zerowidth; michael@0: } michael@0: } michael@0: michael@0: if (flags & _LEFT) { michael@0: if (width > cvtwidth) { michael@0: /* Space filling on the right (i.e. left adjusting) */ michael@0: rightspaces = width - cvtwidth; michael@0: } michael@0: } else { michael@0: if (width > cvtwidth) { michael@0: /* Space filling on the left (i.e. right adjusting) */ michael@0: leftspaces = width - cvtwidth; michael@0: } michael@0: } michael@0: while (--leftspaces >= 0) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: if (signwidth) { michael@0: rv = (*ss->stuff)(ss, &sign, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: while (--precwidth >= 0) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: while (--zerowidth >= 0) { michael@0: rv = (*ss->stuff)(ss, &zero, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: rv = (*ss->stuff)(ss, src, srclen); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: while (--rightspaces >= 0) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** Convert a long into its printable form michael@0: */ michael@0: static int cvt_l(SprintfState *ss, long num, int width, int prec, michael@0: int radix, int type, int flags, const char16_t *hexp) michael@0: { michael@0: char16_t cvtbuf[100]; michael@0: char16_t *cvt; michael@0: int digits; michael@0: michael@0: /* according to the man page this needs to happen */ michael@0: if ((prec == 0) && (num == 0)) { michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** Converting decimal is a little tricky. In the unsigned case we michael@0: ** need to stop when we hit 10 digits. In the signed case, we can michael@0: ** stop when the number is zero. michael@0: */ michael@0: cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf); michael@0: digits = 0; michael@0: while (num) { michael@0: int digit = (((unsigned long)num) % radix) & 0xF; michael@0: *--cvt = hexp[digit]; michael@0: digits++; michael@0: num = (long)(((unsigned long)num) / radix); michael@0: } michael@0: if (digits == 0) { michael@0: *--cvt = '0'; michael@0: digits++; michael@0: } michael@0: michael@0: /* michael@0: ** Now that we have the number converted without its sign, deal with michael@0: ** the sign and zero padding. michael@0: */ michael@0: return fill_n(ss, cvt, digits, width, prec, type, flags); michael@0: } michael@0: michael@0: /* michael@0: ** Convert a 64-bit integer into its printable form michael@0: */ michael@0: static int cvt_ll(SprintfState *ss, int64_t num, int width, int prec, michael@0: int radix, int type, int flags, const char16_t *hexp) michael@0: { michael@0: char16_t cvtbuf[100]; michael@0: char16_t *cvt; michael@0: int digits; michael@0: int64_t rad; michael@0: michael@0: /* according to the man page this needs to happen */ michael@0: if (prec == 0 && num == 0) { michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** Converting decimal is a little tricky. In the unsigned case we michael@0: ** need to stop when we hit 10 digits. In the signed case, we can michael@0: ** stop when the number is zero. michael@0: */ michael@0: rad = radix; michael@0: cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf); michael@0: digits = 0; michael@0: while (num != 0) { michael@0: *--cvt = hexp[int32_t(num % rad) & 0xf]; michael@0: digits++; michael@0: num /= rad; michael@0: } michael@0: if (digits == 0) { michael@0: *--cvt = '0'; michael@0: digits++; michael@0: } michael@0: michael@0: /* michael@0: ** Now that we have the number converted without its sign, deal with michael@0: ** the sign and zero padding. michael@0: */ michael@0: return fill_n(ss, cvt, digits, width, prec, type, flags); michael@0: } michael@0: michael@0: /* michael@0: ** Convert a double precision floating point number into its printable michael@0: ** form. michael@0: */ michael@0: static int cvt_f(SprintfState *ss, double d, int width, int prec, michael@0: const char16_t type, int flags) michael@0: { michael@0: int mode = 2; michael@0: int decpt; michael@0: int sign; michael@0: char buf[256]; michael@0: char * bufp = buf; michael@0: int bufsz = 256; michael@0: char num[256]; michael@0: char * nump; michael@0: char * endnum; michael@0: int numdigits = 0; michael@0: char exp = 'e'; michael@0: michael@0: if (prec == -1) { michael@0: prec = 6; michael@0: } else if (prec > 50) { michael@0: // limit precision to avoid PR_dtoa bug 108335 michael@0: // and to prevent buffers overflows michael@0: prec = 50; michael@0: } michael@0: michael@0: switch (type) { michael@0: case 'f': michael@0: numdigits = prec; michael@0: mode = 3; michael@0: break; michael@0: case 'E': michael@0: exp = 'E'; michael@0: // no break michael@0: case 'e': michael@0: numdigits = prec + 1; michael@0: mode = 2; michael@0: break; michael@0: case 'G': michael@0: exp = 'E'; michael@0: // no break michael@0: case 'g': michael@0: if (prec == 0) { michael@0: prec = 1; michael@0: } michael@0: numdigits = prec; michael@0: mode = 2; michael@0: break; michael@0: default: michael@0: NS_ERROR("invalid type passed to cvt_f"); michael@0: } michael@0: michael@0: if (PR_dtoa(d, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) == PR_FAILURE) { michael@0: buf[0] = '\0'; michael@0: return -1; michael@0: } michael@0: numdigits = endnum - num; michael@0: nump = num; michael@0: michael@0: if (sign) { michael@0: *bufp++ = '-'; michael@0: } else if (flags & _SIGNED) { michael@0: *bufp++ = '+'; michael@0: } michael@0: michael@0: if (decpt == 9999) { michael@0: while ((*bufp++ = *nump++)) { } michael@0: } else { michael@0: michael@0: switch (type) { michael@0: michael@0: case 'E': michael@0: case 'e': michael@0: michael@0: *bufp++ = *nump++; michael@0: if (prec > 0) { michael@0: *bufp++ = '.'; michael@0: while (*nump) { michael@0: *bufp++ = *nump++; michael@0: prec--; michael@0: } michael@0: while (prec-- > 0) { michael@0: *bufp++ = '0'; michael@0: } michael@0: } michael@0: *bufp++ = exp; michael@0: PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1); michael@0: break; michael@0: michael@0: case 'f': michael@0: michael@0: if (decpt < 1) { michael@0: *bufp++ = '0'; michael@0: if (prec > 0) { michael@0: *bufp++ = '.'; michael@0: while (decpt++ && prec-- > 0) { michael@0: *bufp++ = '0'; michael@0: } michael@0: while (*nump && prec-- > 0) { michael@0: *bufp++ = *nump++; michael@0: } michael@0: while (prec-- > 0) { michael@0: *bufp++ = '0'; michael@0: } michael@0: } michael@0: } else { michael@0: while (*nump && decpt-- > 0) { michael@0: *bufp++ = *nump++; michael@0: } michael@0: while (decpt-- > 0) { michael@0: *bufp++ = '0'; michael@0: } michael@0: if (prec > 0) { michael@0: *bufp++ = '.'; michael@0: while (*nump && prec-- > 0) { michael@0: *bufp++ = *nump++; michael@0: } michael@0: while (prec-- > 0) { michael@0: *bufp++ = '0'; michael@0: } michael@0: } michael@0: } michael@0: *bufp = '\0'; michael@0: break; michael@0: michael@0: case 'G': michael@0: case 'g': michael@0: michael@0: if ((decpt < -3) || ((decpt - 1) >= prec)) { michael@0: *bufp++ = *nump++; michael@0: numdigits--; michael@0: if (numdigits > 0) { michael@0: *bufp++ = '.'; michael@0: while (*nump) { michael@0: *bufp++ = *nump++; michael@0: } michael@0: } michael@0: *bufp++ = exp; michael@0: PR_snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt-1); michael@0: } else { michael@0: if (decpt < 1) { michael@0: *bufp++ = '0'; michael@0: if (prec > 0) { michael@0: *bufp++ = '.'; michael@0: while (decpt++) { michael@0: *bufp++ = '0'; michael@0: } michael@0: while (*nump) { michael@0: *bufp++ = *nump++; michael@0: } michael@0: } michael@0: } else { michael@0: while (*nump && decpt-- > 0) { michael@0: *bufp++ = *nump++; michael@0: numdigits--; michael@0: } michael@0: while (decpt-- > 0) { michael@0: *bufp++ = '0'; michael@0: } michael@0: if (numdigits > 0) { michael@0: *bufp++ = '.'; michael@0: while (*nump) { michael@0: *bufp++ = *nump++; michael@0: } michael@0: } michael@0: } michael@0: *bufp = '\0'; michael@0: } michael@0: } michael@0: } michael@0: michael@0: char16_t rbuf[256]; michael@0: char16_t *rbufp = rbuf; michael@0: bufp = buf; michael@0: // cast to char16_t michael@0: while ((*rbufp++ = *bufp++)) { } michael@0: *rbufp = '\0'; michael@0: michael@0: return fill2(ss, rbuf, NS_strlen(rbuf), width, flags); michael@0: } michael@0: michael@0: /* michael@0: ** Convert a string into its printable form. "width" is the output michael@0: ** width. "prec" is the maximum number of characters of "s" to output, michael@0: ** where -1 means until NUL. michael@0: */ michael@0: static int cvt_S(SprintfState *ss, const char16_t *s, int width, michael@0: int prec, int flags) michael@0: { michael@0: int slen; michael@0: michael@0: if (prec == 0) { michael@0: return 0; michael@0: } michael@0: michael@0: /* Limit string length by precision value */ michael@0: slen = s ? NS_strlen(s) : 6; michael@0: if (prec > 0) { michael@0: if (prec < slen) { michael@0: slen = prec; michael@0: } michael@0: } michael@0: michael@0: /* and away we go */ michael@0: return fill2(ss, s ? s : MOZ_UTF16("(null)"), slen, width, flags); michael@0: } michael@0: michael@0: /* michael@0: ** Convert a string into its printable form. "width" is the output michael@0: ** width. "prec" is the maximum number of characters of "s" to output, michael@0: ** where -1 means until NUL. michael@0: */ michael@0: static int cvt_s(SprintfState *ss, const char *s, int width, michael@0: int prec, int flags) michael@0: { michael@0: NS_ConvertUTF8toUTF16 utf16Val(s); michael@0: return cvt_S(ss, utf16Val.get(), width, prec, flags); michael@0: } michael@0: michael@0: /* michael@0: ** BuildArgArray stands for Numbered Argument list Sprintf michael@0: ** for example, michael@0: ** fmp = "%4$i, %2$d, %3s, %1d"; michael@0: ** the number must start from 1, and no gap among them michael@0: */ michael@0: michael@0: static struct NumArgState* BuildArgArray(const char16_t *fmt, michael@0: va_list ap, int * rv, michael@0: struct NumArgState * nasArray) michael@0: { michael@0: int number = 0, cn = 0, i; michael@0: const char16_t* p; michael@0: char16_t c; michael@0: struct NumArgState* nas; michael@0: michael@0: /* michael@0: ** first pass: michael@0: ** detemine how many legal % I have got, then allocate space michael@0: */ michael@0: p = fmt; michael@0: *rv = 0; michael@0: i = 0; michael@0: while ((c = *p++) != 0) { michael@0: if (c != '%') { michael@0: continue; michael@0: } michael@0: /* skip %% case */ michael@0: if ((c = *p++) == '%') { michael@0: continue; michael@0: } michael@0: michael@0: while( c != 0 ){ michael@0: if (c > '9' || c < '0') { michael@0: /* numbered argument csae */ michael@0: if (c == '$') { michael@0: if (i > 0) { michael@0: *rv = -1; michael@0: return nullptr; michael@0: } michael@0: number++; michael@0: break; michael@0: michael@0: } else { michael@0: /* non-numbered argument case */ michael@0: if (number > 0) { michael@0: *rv = -1; michael@0: return nullptr; michael@0: } michael@0: i = 1; michael@0: break; michael@0: } michael@0: } michael@0: c = *p++; michael@0: } michael@0: } michael@0: michael@0: if (number == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (number > NAS_DEFAULT_NUM) { michael@0: nas = (struct NumArgState*)nsMemory::Alloc(number * sizeof(struct NumArgState)); michael@0: if (!nas) { michael@0: *rv = -1; michael@0: return nullptr; michael@0: } michael@0: } else { michael@0: nas = nasArray; michael@0: } michael@0: michael@0: for (i = 0; i < number; i++) { michael@0: nas[i].type = NumArgState::UNKNOWN; michael@0: } michael@0: michael@0: /* michael@0: ** second pass: michael@0: ** set nas[].type michael@0: */ michael@0: p = fmt; michael@0: while ((c = *p++) != 0) { michael@0: if (c != '%') { michael@0: continue; michael@0: } michael@0: c = *p++; michael@0: if (c == '%') { michael@0: continue; michael@0: } michael@0: cn = 0; michael@0: /* should imporve error check later */ michael@0: while (c && c != '$') { michael@0: cn = cn*10 + c - '0'; michael@0: c = *p++; michael@0: } michael@0: michael@0: if (!c || cn < 1 || cn > number) { michael@0: *rv = -1; michael@0: break; michael@0: } michael@0: michael@0: /* nas[cn] starts from 0, and make sure michael@0: nas[cn].type is not assigned */ michael@0: cn--; michael@0: if (nas[cn].type != NumArgState::UNKNOWN) { michael@0: continue; michael@0: } michael@0: michael@0: c = *p++; michael@0: michael@0: /* width */ michael@0: if (c == '*') { michael@0: /* not supported feature, for the argument is not numbered */ michael@0: *rv = -1; michael@0: break; michael@0: } else { michael@0: while ((c >= '0') && (c <= '9')) { michael@0: c = *p++; michael@0: } michael@0: } michael@0: michael@0: /* precision */ michael@0: if (c == '.') { michael@0: c = *p++; michael@0: if (c == '*') { michael@0: /* not supported feature, for the argument is not numbered */ michael@0: *rv = -1; michael@0: break; michael@0: } else { michael@0: while ((c >= '0') && (c <= '9')) { michael@0: c = *p++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* size */ michael@0: nas[cn].type = NumArgState::INTN; michael@0: if (c == 'h') { michael@0: nas[cn].type = NumArgState::INT16; michael@0: c = *p++; michael@0: } else if (c == 'L') { michael@0: /* XXX not quite sure here */ michael@0: nas[cn].type = NumArgState::INT64; michael@0: c = *p++; michael@0: } else if (c == 'l') { michael@0: nas[cn].type = NumArgState::INT32; michael@0: c = *p++; michael@0: if (c == 'l') { michael@0: nas[cn].type = NumArgState::INT64; michael@0: c = *p++; michael@0: } michael@0: } michael@0: michael@0: /* format */ michael@0: switch (c) { michael@0: case 'd': michael@0: case 'c': michael@0: case 'i': michael@0: case 'o': michael@0: case 'u': michael@0: case 'x': michael@0: case 'X': michael@0: break; michael@0: michael@0: case 'e': michael@0: case 'f': michael@0: case 'g': michael@0: nas[cn].type = NumArgState::DOUBLE; michael@0: break; michael@0: michael@0: case 'p': michael@0: /* XXX should use cpp */ michael@0: if (sizeof(void *) == sizeof(int32_t)) { michael@0: nas[cn].type = NumArgState::UINT32; michael@0: } else if (sizeof(void *) == sizeof(int64_t)) { michael@0: nas[cn].type = NumArgState::UINT64; michael@0: } else if (sizeof(void *) == sizeof(int)) { michael@0: nas[cn].type = NumArgState::UINTN; michael@0: } else { michael@0: nas[cn].type = NumArgState::UNKNOWN; michael@0: } michael@0: break; michael@0: michael@0: case 'C': michael@0: /* XXX not supported I suppose */ michael@0: PR_ASSERT(0); michael@0: nas[cn].type = NumArgState::UNKNOWN; michael@0: break; michael@0: michael@0: case 'S': michael@0: nas[cn].type = NumArgState::UNISTRING; michael@0: break; michael@0: michael@0: case 's': michael@0: nas[cn].type = NumArgState::STRING; michael@0: break; michael@0: michael@0: case 'n': michael@0: nas[cn].type = NumArgState::INTSTR; michael@0: break; michael@0: michael@0: default: michael@0: PR_ASSERT(0); michael@0: nas[cn].type = NumArgState::UNKNOWN; michael@0: break; michael@0: } michael@0: michael@0: /* get a legal para. */ michael@0: if (nas[cn].type == NumArgState::UNKNOWN) { michael@0: *rv = -1; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: ** third pass michael@0: ** fill the nas[cn].ap michael@0: */ michael@0: if (*rv < 0) { michael@0: if( nas != nasArray ) { michael@0: PR_DELETE(nas); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: cn = 0; michael@0: while (cn < number) { michael@0: if (nas[cn].type == NumArgState::UNKNOWN) { michael@0: cn++; michael@0: continue; michael@0: } michael@0: michael@0: VARARGS_ASSIGN(nas[cn].ap, ap); michael@0: michael@0: switch (nas[cn].type) { michael@0: case NumArgState::INT16: michael@0: case NumArgState::UINT16: michael@0: case NumArgState::INTN: michael@0: case NumArgState::UINTN: (void)va_arg(ap, int); break; michael@0: michael@0: case NumArgState::INT32: (void)va_arg(ap, int32_t); break; michael@0: michael@0: case NumArgState::UINT32: (void)va_arg(ap, uint32_t); break; michael@0: michael@0: case NumArgState::INT64: (void)va_arg(ap, int64_t); break; michael@0: michael@0: case NumArgState::UINT64: (void)va_arg(ap, uint64_t); break; michael@0: michael@0: case NumArgState::STRING: (void)va_arg(ap, char*); break; michael@0: michael@0: case NumArgState::INTSTR: (void)va_arg(ap, int*); break; michael@0: michael@0: case NumArgState::DOUBLE: (void)va_arg(ap, double); break; michael@0: michael@0: case NumArgState::UNISTRING: (void)va_arg(ap, char16_t*); break; michael@0: michael@0: default: michael@0: if( nas != nasArray ) { michael@0: PR_DELETE( nas ); michael@0: } michael@0: *rv = -1; michael@0: return nullptr; michael@0: } michael@0: cn++; michael@0: } michael@0: return nas; michael@0: } michael@0: michael@0: /* michael@0: ** The workhorse sprintf code. michael@0: */ michael@0: static int dosprintf(SprintfState *ss, const char16_t *fmt, va_list ap) michael@0: { michael@0: char16_t c; michael@0: int flags, width, prec, radix, type; michael@0: union { michael@0: char16_t ch; michael@0: int i; michael@0: long l; michael@0: int64_t ll; michael@0: double d; michael@0: const char *s; michael@0: const char16_t *S; michael@0: int *ip; michael@0: } u; michael@0: char16_t space = ' '; michael@0: michael@0: nsAutoString hex; michael@0: hex.AssignLiteral("0123456789abcdef"); michael@0: michael@0: nsAutoString HEX; michael@0: HEX.AssignLiteral("0123456789ABCDEF"); michael@0: michael@0: const char16_t *hexp; michael@0: int rv, i; michael@0: struct NumArgState* nas = nullptr; michael@0: struct NumArgState nasArray[NAS_DEFAULT_NUM]; michael@0: michael@0: michael@0: /* michael@0: ** build an argument array, IF the fmt is numbered argument michael@0: ** list style, to contain the Numbered Argument list pointers michael@0: */ michael@0: nas = BuildArgArray (fmt, ap, &rv, nasArray); michael@0: if (rv < 0) { michael@0: /* the fmt contains error Numbered Argument format, jliu@netscape.com */ michael@0: PR_ASSERT(0); michael@0: return rv; michael@0: } michael@0: michael@0: while ((c = *fmt++) != 0) { michael@0: if (c != '%') { michael@0: rv = (*ss->stuff)(ss, fmt - 1, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: continue; michael@0: } michael@0: michael@0: /* michael@0: ** Gobble up the % format string. Hopefully we have handled all michael@0: ** of the strange cases! michael@0: */ michael@0: flags = 0; michael@0: c = *fmt++; michael@0: if (c == '%') { michael@0: /* quoting a % with %% */ michael@0: rv = (*ss->stuff)(ss, fmt - 1, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: continue; michael@0: } michael@0: michael@0: if (nas != nullptr) { michael@0: /* the fmt contains the Numbered Arguments feature */ michael@0: i = 0; michael@0: /* should imporve error check later */ michael@0: while (c && c != '$') { michael@0: i = (i * 10) + (c - '0'); michael@0: c = *fmt++; michael@0: } michael@0: michael@0: if (nas[i-1].type == NumArgState::UNKNOWN) { michael@0: if (nas && (nas != nasArray)) { michael@0: PR_DELETE(nas); michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: VARARGS_ASSIGN(ap, nas[i-1].ap); michael@0: c = *fmt++; michael@0: } michael@0: michael@0: /* michael@0: * Examine optional flags. Note that we do not implement the michael@0: * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is michael@0: * somewhat ambiguous and not ideal, which is perhaps why michael@0: * the various sprintf() implementations are inconsistent michael@0: * on this feature. michael@0: */ michael@0: while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { michael@0: if (c == '-') flags |= _LEFT; michael@0: if (c == '+') flags |= _SIGNED; michael@0: if (c == ' ') flags |= _SPACED; michael@0: if (c == '0') flags |= _ZEROS; michael@0: c = *fmt++; michael@0: } michael@0: if (flags & _SIGNED) flags &= ~_SPACED; michael@0: if (flags & _LEFT) flags &= ~_ZEROS; michael@0: michael@0: /* width */ michael@0: if (c == '*') { michael@0: c = *fmt++; michael@0: width = va_arg(ap, int); michael@0: } else { michael@0: width = 0; michael@0: while ((c >= '0') && (c <= '9')) { michael@0: width = (width * 10) + (c - '0'); michael@0: c = *fmt++; michael@0: } michael@0: } michael@0: michael@0: /* precision */ michael@0: prec = -1; michael@0: if (c == '.') { michael@0: c = *fmt++; michael@0: if (c == '*') { michael@0: c = *fmt++; michael@0: prec = va_arg(ap, int); michael@0: } else { michael@0: prec = 0; michael@0: while ((c >= '0') && (c <= '9')) { michael@0: prec = (prec * 10) + (c - '0'); michael@0: c = *fmt++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* size */ michael@0: type = NumArgState::INTN; michael@0: if (c == 'h') { michael@0: type = NumArgState::INT16; michael@0: c = *fmt++; michael@0: } else if (c == 'L') { michael@0: /* XXX not quite sure here */ michael@0: type = NumArgState::INT64; michael@0: c = *fmt++; michael@0: } else if (c == 'l') { michael@0: type = NumArgState::INT32; michael@0: c = *fmt++; michael@0: if (c == 'l') { michael@0: type = NumArgState::INT64; michael@0: c = *fmt++; michael@0: } michael@0: } michael@0: michael@0: /* format */ michael@0: hexp = hex.get(); michael@0: switch (c) { michael@0: case 'd': michael@0: case 'i': /* decimal/integer */ michael@0: radix = 10; michael@0: goto fetch_and_convert; michael@0: michael@0: case 'o': /* octal */ michael@0: radix = 8; michael@0: type |= 1; michael@0: goto fetch_and_convert; michael@0: michael@0: case 'u': /* unsigned decimal */ michael@0: radix = 10; michael@0: type |= 1; michael@0: goto fetch_and_convert; michael@0: michael@0: case 'x': /* unsigned hex */ michael@0: radix = 16; michael@0: type |= 1; michael@0: goto fetch_and_convert; michael@0: michael@0: case 'X': /* unsigned HEX */ michael@0: radix = 16; michael@0: hexp = HEX.get(); michael@0: type |= 1; michael@0: goto fetch_and_convert; michael@0: michael@0: fetch_and_convert: michael@0: switch (type) { michael@0: case NumArgState::INT16: michael@0: u.l = va_arg(ap, int); michael@0: if (u.l < 0) { michael@0: u.l = -u.l; michael@0: flags |= _NEG; michael@0: } michael@0: goto do_long; michael@0: case NumArgState::UINT16: michael@0: u.l = va_arg(ap, int) & 0xffff; michael@0: goto do_long; michael@0: case NumArgState::INTN: michael@0: u.l = va_arg(ap, int); michael@0: if (u.l < 0) { michael@0: u.l = -u.l; michael@0: flags |= _NEG; michael@0: } michael@0: goto do_long; michael@0: case NumArgState::UINTN: michael@0: u.l = (long)va_arg(ap, unsigned int); michael@0: goto do_long; michael@0: michael@0: case NumArgState::INT32: michael@0: u.l = va_arg(ap, int32_t); michael@0: if (u.l < 0) { michael@0: u.l = -u.l; michael@0: flags |= _NEG; michael@0: } michael@0: goto do_long; michael@0: case NumArgState::UINT32: michael@0: u.l = (long)va_arg(ap, uint32_t); michael@0: do_long: michael@0: rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: break; michael@0: michael@0: case NumArgState::INT64: michael@0: u.ll = va_arg(ap, int64_t); michael@0: if (u.ll < 0) { michael@0: u.ll = -u.ll; michael@0: flags |= _NEG; michael@0: } michael@0: goto do_longlong; michael@0: case NumArgState::UINT64: michael@0: u.ll = va_arg(ap, uint64_t); michael@0: do_longlong: michael@0: rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case 'e': michael@0: case 'E': michael@0: case 'f': michael@0: case 'g': michael@0: case 'G': michael@0: u.d = va_arg(ap, double); michael@0: rv = cvt_f(ss, u.d, width, prec, c, flags); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: break; michael@0: michael@0: case 'c': michael@0: u.ch = va_arg(ap, int); michael@0: if ((flags & _LEFT) == 0) { michael@0: while (width-- > 1) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: rv = (*ss->stuff)(ss, &u.ch, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: if (flags & _LEFT) { michael@0: while (width-- > 1) { michael@0: rv = (*ss->stuff)(ss, &space, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case 'p': michael@0: if (sizeof(void *) == sizeof(int32_t)) { michael@0: type = NumArgState::UINT32; michael@0: } else if (sizeof(void *) == sizeof(int64_t)) { michael@0: type = NumArgState::UINT64; michael@0: } else if (sizeof(void *) == sizeof(int)) { michael@0: type = NumArgState::UINTN; michael@0: } else { michael@0: PR_ASSERT(0); michael@0: break; michael@0: } michael@0: radix = 16; michael@0: goto fetch_and_convert; michael@0: michael@0: #if 0 michael@0: case 'C': michael@0: /* XXX not supported I suppose */ michael@0: PR_ASSERT(0); michael@0: break; michael@0: #endif michael@0: michael@0: case 'S': michael@0: u.S = va_arg(ap, const char16_t*); michael@0: rv = cvt_S(ss, u.S, width, prec, flags); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: break; michael@0: michael@0: case 's': michael@0: u.s = va_arg(ap, const char*); michael@0: rv = cvt_s(ss, u.s, width, prec, flags); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: break; michael@0: michael@0: case 'n': michael@0: u.ip = va_arg(ap, int*); michael@0: if (u.ip) { michael@0: *u.ip = ss->cur - ss->base; michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: /* Not a % token after all... skip it */ michael@0: #if 0 michael@0: PR_ASSERT(0); michael@0: #endif michael@0: char16_t perct = '%'; michael@0: rv = (*ss->stuff)(ss, &perct, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: rv = (*ss->stuff)(ss, fmt - 1, 1); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* Stuff trailing NUL */ michael@0: char16_t null = '\0'; michael@0: michael@0: rv = (*ss->stuff)(ss, &null, 1); michael@0: michael@0: if( nas && ( nas != nasArray ) ){ michael@0: PR_DELETE( nas ); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: static int michael@0: StringStuff(SprintfState* ss, const char16_t* sp, uint32_t len) michael@0: { michael@0: if (*sp == '\0') michael@0: return 0; michael@0: michael@0: ptrdiff_t off = ss->cur - ss->base; michael@0: michael@0: nsAString* str = static_cast(ss->stuffclosure); michael@0: str->Append(sp, len); michael@0: michael@0: ss->base = str->BeginWriting(); michael@0: ss->cur = ss->base + off; michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** Stuff routine that automatically grows the malloc'd output buffer michael@0: ** before it overflows. michael@0: */ michael@0: static int GrowStuff(SprintfState *ss, const char16_t *sp, uint32_t len) michael@0: { michael@0: ptrdiff_t off; michael@0: char16_t *newbase; michael@0: uint32_t newlen; michael@0: michael@0: off = ss->cur - ss->base; michael@0: if (off + len >= ss->maxlen) { michael@0: /* Grow the buffer */ michael@0: newlen = ss->maxlen + ((len > 32) ? len : 32); michael@0: if (ss->base) { michael@0: newbase = (char16_t*) nsMemory::Realloc(ss->base, newlen*sizeof(char16_t)); michael@0: } else { michael@0: newbase = (char16_t*) nsMemory::Alloc(newlen*sizeof(char16_t)); michael@0: } michael@0: if (!newbase) { michael@0: /* Ran out of memory */ michael@0: return -1; michael@0: } michael@0: ss->base = newbase; michael@0: ss->maxlen = newlen; michael@0: ss->cur = ss->base + off; michael@0: } michael@0: michael@0: /* Copy data */ michael@0: while (len) { michael@0: --len; michael@0: *ss->cur++ = *sp++; michael@0: } michael@0: PR_ASSERT((uint32_t)(ss->cur - ss->base) <= ss->maxlen); michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** sprintf into a malloc'd buffer michael@0: */ michael@0: char16_t * nsTextFormatter::smprintf(const char16_t *fmt, ...) michael@0: { michael@0: va_list ap; michael@0: char16_t *rv; michael@0: michael@0: va_start(ap, fmt); michael@0: rv = nsTextFormatter::vsmprintf(fmt, ap); michael@0: va_end(ap); michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t nsTextFormatter::ssprintf(nsAString& out, const char16_t* fmt, ...) michael@0: { michael@0: va_list ap; michael@0: uint32_t rv; michael@0: michael@0: va_start(ap, fmt); michael@0: rv = nsTextFormatter::vssprintf(out, fmt, ap); michael@0: va_end(ap); michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t nsTextFormatter::vssprintf(nsAString& out, const char16_t* fmt, va_list ap) michael@0: { michael@0: SprintfState ss; michael@0: ss.stuff = StringStuff; michael@0: ss.base = 0; michael@0: ss.cur = 0; michael@0: ss.maxlen = 0; michael@0: ss.stuffclosure = &out; michael@0: michael@0: out.Truncate(); michael@0: int n = dosprintf(&ss, fmt, ap); michael@0: return n ? n - 1 : n; michael@0: } michael@0: michael@0: char16_t * nsTextFormatter::vsmprintf(const char16_t *fmt, va_list ap) michael@0: { michael@0: SprintfState ss; michael@0: int rv; michael@0: michael@0: ss.stuff = GrowStuff; michael@0: ss.base = 0; michael@0: ss.cur = 0; michael@0: ss.maxlen = 0; michael@0: rv = dosprintf(&ss, fmt, ap); michael@0: if (rv < 0) { michael@0: if (ss.base) { michael@0: PR_DELETE(ss.base); michael@0: } michael@0: return 0; michael@0: } michael@0: return ss.base; michael@0: } michael@0: michael@0: /* michael@0: ** Stuff routine that discards overflow data michael@0: */ michael@0: static int LimitStuff(SprintfState *ss, const char16_t *sp, uint32_t len) michael@0: { michael@0: uint32_t limit = ss->maxlen - (ss->cur - ss->base); michael@0: michael@0: if (len > limit) { michael@0: len = limit; michael@0: } michael@0: while (len) { michael@0: --len; michael@0: *ss->cur++ = *sp++; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: ** sprintf into a fixed size buffer. Make sure there is a NUL at the end michael@0: ** when finished. michael@0: */ michael@0: uint32_t nsTextFormatter::snprintf(char16_t *out, uint32_t outlen, const char16_t *fmt, ...) michael@0: { michael@0: va_list ap; michael@0: uint32_t rv; michael@0: michael@0: PR_ASSERT((int32_t)outlen > 0); michael@0: if ((int32_t)outlen <= 0) { michael@0: return 0; michael@0: } michael@0: michael@0: va_start(ap, fmt); michael@0: rv = nsTextFormatter::vsnprintf(out, outlen, fmt, ap); michael@0: va_end(ap); michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t nsTextFormatter::vsnprintf(char16_t *out, uint32_t outlen,const char16_t *fmt, michael@0: va_list ap) michael@0: { michael@0: SprintfState ss; michael@0: uint32_t n; michael@0: michael@0: PR_ASSERT((int32_t)outlen > 0); michael@0: if ((int32_t)outlen <= 0) { michael@0: return 0; michael@0: } michael@0: michael@0: ss.stuff = LimitStuff; michael@0: ss.base = out; michael@0: ss.cur = out; michael@0: ss.maxlen = outlen; michael@0: (void) dosprintf(&ss, fmt, ap); michael@0: michael@0: /* If we added chars, and we didn't append a null, do it now. */ michael@0: if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') ) michael@0: *(--ss.cur) = '\0'; michael@0: michael@0: n = ss.cur - ss.base; michael@0: return n ? n - 1 : n; michael@0: } michael@0: michael@0: /* michael@0: * Free memory allocated, for the caller, by smprintf michael@0: */ michael@0: void nsTextFormatter::smprintf_free(char16_t *mem) michael@0: { michael@0: nsMemory::Free(mem); michael@0: } michael@0: