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