summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/io/prprf.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /nsprpub/pr/src/io/prprf.c
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nsprpub/pr/src/io/prprf.c')
-rw-r--r--nsprpub/pr/src/io/prprf.c1316
1 files changed, 1316 insertions, 0 deletions
diff --git a/nsprpub/pr/src/io/prprf.c b/nsprpub/pr/src/io/prprf.c
new file mode 100644
index 0000000000..9b6072e268
--- /dev/null
+++ b/nsprpub/pr/src/io/prprf.c
@@ -0,0 +1,1316 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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.
+**
+** Author: Kipp E.B. Hickman
+*/
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "primpl.h"
+#include "prprf.h"
+#include "prlong.h"
+#include "prlog.h"
+#include "prmem.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+/*
+** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
+*/
+
+/*
+** XXX This needs to be internationalized!
+*/
+
+typedef struct SprintfStateStr SprintfState;
+
+struct SprintfStateStr {
+ int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len);
+
+ char *base;
+ char *cur;
+ PRUint32 maxlen; /* Must not exceed PR_INT32_MAX. */
+
+ int (*func)(void *arg, const char *sp, PRUint32 len);
+ void *arg;
+};
+
+/*
+** Numbered Argument
+*/
+struct NumArg {
+ int type; /* type of the numbered argument */
+ union { /* the numbered argument */
+ int i;
+ unsigned int ui;
+ PRInt32 i32;
+ PRUint32 ui32;
+ PRInt64 ll;
+ PRUint64 ull;
+ double d;
+ const char *s;
+ int *ip;
+#ifdef WIN32
+ const WCHAR *ws;
+#endif
+ } u;
+};
+
+#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
+
+/*
+** For numeric types, the signed versions must have even values,
+** and their corresponding unsigned versions must have the subsequent
+** odd value.
+*/
+#define TYPE_INT16 0
+#define TYPE_UINT16 1
+#define TYPE_INTN 2
+#define TYPE_UINTN 3
+#define TYPE_INT32 4
+#define TYPE_UINT32 5
+#define TYPE_INT64 6
+#define TYPE_UINT64 7
+#define TYPE_STRING 8
+#define TYPE_DOUBLE 9
+#define TYPE_INTSTR 10
+#ifdef WIN32
+#define TYPE_WSTRING 11
+#endif
+#define TYPE_UNKNOWN 20
+
+#define FLAG_LEFT 0x1
+#define FLAG_SIGNED 0x2
+#define FLAG_SPACED 0x4
+#define FLAG_ZEROS 0x8
+#define FLAG_NEG 0x10
+
+/*
+** Fill into the buffer using the data in src
+*/
+static int fill2(SprintfState *ss, const char *src, int srclen, int width,
+ int flags)
+{
+ char space = ' ';
+ int rv;
+
+ width -= srclen;
+ if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
+ if (flags & FLAG_ZEROS) {
+ space = '0';
+ }
+ while (--width >= 0) {
+ rv = (*ss->stuff)(ss, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Copy out the source data */
+ rv = (*ss->stuff)(ss, src, srclen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
+ while (--width >= 0) {
+ rv = (*ss->stuff)(ss, &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 *ss, const char *src, int srclen, int width,
+ int prec, int type, int flags)
+{
+ int zerowidth = 0;
+ int precwidth = 0;
+ int signwidth = 0;
+ int leftspaces = 0;
+ int rightspaces = 0;
+ int cvtwidth;
+ int rv;
+ char sign;
+
+ if ((type & 1) == 0) {
+ if (flags & FLAG_NEG) {
+ sign = '-';
+ signwidth = 1;
+ } else if (flags & FLAG_SIGNED) {
+ sign = '+';
+ signwidth = 1;
+ } else if (flags & FLAG_SPACED) {
+ sign = ' ';
+ signwidth = 1;
+ }
+ }
+ cvtwidth = signwidth + srclen;
+
+ if (prec > 0) {
+ if (prec > srclen) {
+ precwidth = prec - srclen; /* Need zero filling */
+ cvtwidth += precwidth;
+ }
+ }
+
+ if ((flags & FLAG_ZEROS) && (prec < 0)) {
+ if (width > cvtwidth) {
+ zerowidth = width - cvtwidth; /* Zero filling */
+ cvtwidth += zerowidth;
+ }
+ }
+
+ if (flags & FLAG_LEFT) {
+ if (width > cvtwidth) {
+ /* Space filling on the right (i.e. left adjusting) */
+ rightspaces = width - cvtwidth;
+ }
+ } else {
+ if (width > cvtwidth) {
+ /* Space filling on the left (i.e. right adjusting) */
+ leftspaces = width - cvtwidth;
+ }
+ }
+ while (--leftspaces >= 0) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ if (signwidth) {
+ rv = (*ss->stuff)(ss, &sign, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--precwidth >= 0) {
+ rv = (*ss->stuff)(ss, "0", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--zerowidth >= 0) {
+ rv = (*ss->stuff)(ss, "0", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ rv = (*ss->stuff)(ss, src, srclen);
+ if (rv < 0) {
+ return rv;
+ }
+ while (--rightspaces >= 0) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ return 0;
+}
+
+/*
+** Convert a long into its printable form
+*/
+static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
+ int type, int flags, const char *hexp)
+{
+ char cvtbuf[100];
+ char *cvt;
+ int digits;
+
+ /* according to the man page this needs to happen */
+ if ((prec == 0) && (num == 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 + sizeof(cvtbuf);
+ digits = 0;
+ while (num) {
+ int digit = (((unsigned long)num) % radix) & 0xF;
+ *--cvt = hexp[digit];
+ digits++;
+ num = (long)(((unsigned long)num) / radix);
+ }
+ 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(ss, cvt, digits, width, prec, type, flags);
+}
+
+/*
+** Convert a 64-bit integer into its printable form
+*/
+static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix,
+ int type, int flags, const char *hexp)
+{
+ char cvtbuf[100];
+ char *cvt;
+ int digits;
+ PRInt64 rad;
+
+ /* according to the man page this needs to happen */
+ if ((prec == 0) && (LL_IS_ZERO(num))) {
+ 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.
+ */
+ LL_I2L(rad, radix);
+ cvt = cvtbuf + sizeof(cvtbuf);
+ digits = 0;
+ while (!LL_IS_ZERO(num)) {
+ PRInt32 digit;
+ PRInt64 quot, rem;
+ LL_UDIVMOD(&quot, &rem, num, rad);
+ LL_L2I(digit, rem);
+ *--cvt = hexp[digit & 0xf];
+ digits++;
+ num = quot;
+ }
+ 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(ss, cvt, digits, width, prec, type, flags);
+}
+
+/*
+** Convert a double precision floating point number into its printable
+** form.
+**
+** XXX stop using snprintf to convert floating point
+*/
+static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
+{
+ char fin[20];
+ char fout[300];
+ int amount = fmt1 - fmt0;
+
+ if (amount <= 0 || amount >= sizeof(fin)) {
+ /* Totally bogus % command to snprintf. Just ignore it */
+ return 0;
+ }
+ memcpy(fin, fmt0, amount);
+ fin[amount] = 0;
+
+ /* Convert floating point using the native snprintf code */
+#ifdef DEBUG
+ {
+ const char *p = fin;
+ while (*p) {
+ PR_ASSERT(*p != 'L');
+ p++;
+ }
+ }
+#endif
+ memset(fout, 0, sizeof(fout));
+ snprintf(fout, sizeof(fout), fin, d);
+ /* Explicitly null-terminate fout because on Windows snprintf doesn't
+ * append a null-terminator if the buffer is too small. */
+ fout[sizeof(fout) - 1] = '\0';
+
+ return (*ss->stuff)(ss, fout, strlen(fout));
+}
+
+/*
+** Convert a string into its printable form. "width" is the output
+** width. "prec" is the maximum number of characters of "s" to output,
+** where -1 means until NUL.
+*/
+static int cvt_s(SprintfState *ss, const char *str, int width, int prec,
+ int flags)
+{
+ int slen;
+
+ if (prec == 0) {
+ return 0;
+ }
+
+ /* Limit string length by precision value */
+ if (!str) {
+ str = "(null)";
+ }
+ if (prec > 0) {
+ /* this is: slen = strnlen(str, prec); */
+ register const char *s;
+
+ for(s = str; prec && *s; s++, prec-- )
+ ;
+ slen = s - str;
+ } else {
+ slen = strlen(str);
+ }
+
+ /* and away we go */
+ return fill2(ss, str, slen, width, flags);
+}
+
+/*
+** BuildArgArray stands for Numbered Argument list Sprintf
+** for example,
+** fmt = "%4$i, %2$d, %3s, %1d";
+** the number must start from 1, and no gap among them
+*/
+
+static struct NumArg* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArg* nasArray )
+{
+ int number = 0, cn = 0, i;
+ const char* p;
+ char c;
+ struct NumArg* nas;
+
+
+ /*
+ ** first pass:
+ ** determine how many legal % I have got, then allocate space
+ */
+
+ p = fmt;
+ *rv = 0;
+ i = 0;
+ while( ( c = *p++ ) != 0 ) {
+ if( c != '%' ) {
+ continue;
+ }
+ if( ( c = *p++ ) == '%' ) { /* skip %% case */
+ continue;
+ }
+
+ while( c != 0 ) {
+ if( c > '9' || c < '0' ) {
+ if( c == '$' ) { /* numbered argument case */
+ if( i > 0 ) {
+ *rv = -1;
+ return NULL;
+ }
+ number++;
+ } else { /* non-numbered argument case */
+ if( number > 0 ) {
+ *rv = -1;
+ return NULL;
+ }
+ i = 1;
+ }
+ break;
+ }
+
+ c = *p++;
+ }
+ }
+
+ if( number == 0 ) {
+ return NULL;
+ }
+
+
+ if( number > NAS_DEFAULT_NUM ) {
+ nas = (struct NumArg*)PR_MALLOC( number * sizeof( struct NumArg ) );
+ if( !nas ) {
+ *rv = -1;
+ return NULL;
+ }
+ } else {
+ nas = nasArray;
+ }
+
+ for( i = 0; i < number; i++ ) {
+ nas[i].type = TYPE_UNKNOWN;
+ }
+
+
+ /*
+ ** second pass:
+ ** set nas[].type
+ */
+
+ p = fmt;
+ while( ( c = *p++ ) != 0 ) {
+ if( c != '%' ) {
+ continue;
+ }
+ c = *p++;
+ if( c == '%' ) {
+ continue;
+ }
+
+ cn = 0;
+ while( c && c != '$' ) { /* should improve error check later */
+ cn = cn*10 + c - '0';
+ c = *p++;
+ }
+
+ if( !c || cn < 1 || cn > number ) {
+ *rv = -1;
+ break;
+ }
+
+ /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
+ cn--;
+ if( nas[cn].type != TYPE_UNKNOWN ) {
+ continue;
+ }
+
+ c = *p++;
+
+ /* width */
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *rv = -1;
+ break;
+ }
+
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+
+ /* precision */
+ if (c == '.') {
+ c = *p++;
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *rv = -1;
+ break;
+ }
+
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+
+ /* size */
+ nas[cn].type = TYPE_INTN;
+ if (c == 'h') {
+ nas[cn].type = TYPE_INT16;
+ c = *p++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ nas[cn].type = TYPE_INT64;
+ c = *p++;
+ } else if (c == 'l') {
+ nas[cn].type = TYPE_INT32;
+ c = *p++;
+ if (c == 'l') {
+ nas[cn].type = TYPE_INT64;
+ c = *p++;
+ }
+ } else if (c == 'z') {
+ if (sizeof(size_t) == sizeof(PRInt32)) {
+ nas[ cn ].type = TYPE_INT32;
+ } else if (sizeof(size_t) == sizeof(PRInt64)) {
+ nas[ cn ].type = TYPE_INT64;
+ } else {
+ nas[ cn ].type = TYPE_UNKNOWN;
+ }
+ 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 = TYPE_DOUBLE;
+ break;
+
+ case 'p':
+ /* XXX should use cpp */
+ if (sizeof(void *) == sizeof(PRInt32)) {
+ nas[ cn ].type = TYPE_UINT32;
+ } else if (sizeof(void *) == sizeof(PRInt64)) {
+ nas[ cn ].type = TYPE_UINT64;
+ } else if (sizeof(void *) == sizeof(PRIntn)) {
+ nas[ cn ].type = TYPE_UINTN;
+ } else {
+ nas[ cn ].type = TYPE_UNKNOWN;
+ }
+ break;
+
+ case 'S':
+#ifdef WIN32
+ nas[ cn ].type = TYPE_WSTRING;
+ break;
+#endif
+ case 'C':
+ case 'E':
+ case 'G':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ nas[ cn ].type = TYPE_UNKNOWN;
+ break;
+
+ case 's':
+ nas[ cn ].type = TYPE_STRING;
+ break;
+
+ case 'n':
+ nas[ cn ].type = TYPE_INTSTR;
+ break;
+
+ default:
+ PR_ASSERT(0);
+ nas[ cn ].type = TYPE_UNKNOWN;
+ break;
+ }
+
+ /* get a legal para. */
+ if( nas[ cn ].type == TYPE_UNKNOWN ) {
+ *rv = -1;
+ break;
+ }
+ }
+
+
+ /*
+ ** third pass
+ ** fill the nas[cn].ap
+ */
+
+ if( *rv < 0 ) {
+ if( nas != nasArray ) {
+ PR_DELETE( nas );
+ }
+ return NULL;
+ }
+
+ cn = 0;
+ while( cn < number ) {
+ if( nas[cn].type == TYPE_UNKNOWN ) {
+ cn++;
+ continue;
+ }
+
+ switch( nas[cn].type ) {
+ case TYPE_INT16:
+ case TYPE_UINT16:
+ case TYPE_INTN:
+ nas[cn].u.i = va_arg( ap, int );
+ break;
+
+ case TYPE_UINTN:
+ nas[cn].u.ui = va_arg( ap, unsigned int );
+ break;
+
+ case TYPE_INT32:
+ nas[cn].u.i32 = va_arg( ap, PRInt32 );
+ break;
+
+ case TYPE_UINT32:
+ nas[cn].u.ui32 = va_arg( ap, PRUint32 );
+ break;
+
+ case TYPE_INT64:
+ nas[cn].u.ll = va_arg( ap, PRInt64 );
+ break;
+
+ case TYPE_UINT64:
+ nas[cn].u.ull = va_arg( ap, PRUint64 );
+ break;
+
+ case TYPE_STRING:
+ nas[cn].u.s = va_arg( ap, char* );
+ break;
+
+#ifdef WIN32
+ case TYPE_WSTRING:
+ nas[cn].u.ws = va_arg( ap, WCHAR* );
+ break;
+#endif
+
+ case TYPE_INTSTR:
+ nas[cn].u.ip = va_arg( ap, int* );
+ break;
+
+ case TYPE_DOUBLE:
+ nas[cn].u.d = va_arg( ap, double );
+ break;
+
+ default:
+ if( nas != nasArray ) {
+ PR_DELETE( nas );
+ }
+ *rv = -1;
+ return NULL;
+ }
+
+ cn++;
+ }
+
+
+ return nas;
+}
+
+/*
+** The workhorse sprintf code.
+*/
+static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
+{
+ char c;
+ int flags, width, prec, radix, type;
+ union {
+ char ch;
+ int i;
+ long l;
+ PRInt64 ll;
+ double d;
+ const char *s;
+ int *ip;
+#ifdef WIN32
+ const WCHAR *ws;
+#endif
+ } u;
+ const char *fmt0;
+ static char *hex = "0123456789abcdef";
+ static char *HEX = "0123456789ABCDEF";
+ char *hexp;
+ int rv, i;
+ struct NumArg* nas = NULL;
+ struct NumArg* nap = NULL;
+ struct NumArg nasArray[ NAS_DEFAULT_NUM ];
+ char pattern[20];
+ const char* dolPt = NULL; /* in "%4$.2f", dolPt will point to . */
+#ifdef WIN32
+ char *pBuf = NULL;
+#endif
+
+ /*
+ ** build an argument array, IF the fmt is numbered argument
+ ** list style, to contain the Numbered Argument list pointers
+ */
+
+ nas = BuildArgArray( fmt, ap, &rv, nasArray );
+ if( rv < 0 ) {
+ /* the fmt contains error Numbered Argument format, jliu@netscape.com */
+ PR_ASSERT(0);
+ return rv;
+ }
+
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ continue;
+ }
+ fmt0 = fmt - 1;
+
+ /*
+ ** Gobble up the % format string. Hopefully we have handled all
+ ** of the strange cases!
+ */
+ flags = 0;
+ c = *fmt++;
+ if (c == '%') {
+ /* quoting a % with %% */
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ continue;
+ }
+
+ if( nas != NULL ) {
+ /* the fmt contains the Numbered Arguments feature */
+ i = 0;
+ while( c && c != '$' ) { /* should improve error check later */
+ i = ( i * 10 ) + ( c - '0' );
+ c = *fmt++;
+ }
+
+ if( nas[i-1].type == TYPE_UNKNOWN ) {
+ if( nas && ( nas != nasArray ) ) {
+ PR_DELETE( nas );
+ }
+ return -1;
+ }
+
+ nap = &nas[i-1];
+ dolPt = fmt;
+ c = *fmt++;
+ }
+
+ /*
+ * 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 |= FLAG_LEFT;
+ }
+ if (c == '+') {
+ flags |= FLAG_SIGNED;
+ }
+ if (c == ' ') {
+ flags |= FLAG_SPACED;
+ }
+ if (c == '0') {
+ flags |= FLAG_ZEROS;
+ }
+ c = *fmt++;
+ }
+ if (flags & FLAG_SIGNED) {
+ flags &= ~FLAG_SPACED;
+ }
+ if (flags & FLAG_LEFT) {
+ flags &= ~FLAG_ZEROS;
+ }
+
+ /* width */
+ if (c == '*') {
+ c = *fmt++;
+ width = va_arg(ap, int);
+ } else {
+ width = 0;
+ while ((c >= '0') && (c <= '9')) {
+ width = (width * 10) + (c - '0');
+ c = *fmt++;
+ }
+ }
+
+ /* precision */
+ prec = -1;
+ if (c == '.') {
+ c = *fmt++;
+ if (c == '*') {
+ c = *fmt++;
+ prec = va_arg(ap, int);
+ } else {
+ prec = 0;
+ while ((c >= '0') && (c <= '9')) {
+ prec = (prec * 10) + (c - '0');
+ c = *fmt++;
+ }
+ }
+ }
+
+ /* size */
+ type = TYPE_INTN;
+ if (c == 'h') {
+ type = TYPE_INT16;
+ c = *fmt++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ type = TYPE_INT64;
+ c = *fmt++;
+ } else if (c == 'l') {
+ type = TYPE_INT32;
+ c = *fmt++;
+ if (c == 'l') {
+ type = TYPE_INT64;
+ c = *fmt++;
+ }
+ } else if (c == 'z') {
+ if (sizeof(size_t) == sizeof(PRInt32)) {
+ type = TYPE_INT32;
+ } else if (sizeof(size_t) == sizeof(PRInt64)) {
+ type = TYPE_INT64;
+ }
+ c = *fmt++;
+ }
+
+ /* format */
+ hexp = hex;
+ 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;
+ type |= 1;
+ goto fetch_and_convert;
+
+fetch_and_convert:
+ switch (type) {
+ case TYPE_INT16:
+ u.l = nas ? nap->u.i : va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINT16:
+ u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff;
+ goto do_long;
+ case TYPE_INTN:
+ u.l = nas ? nap->u.i : va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINTN:
+ u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int));
+ goto do_long;
+
+ case TYPE_INT32:
+ u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINT32:
+ u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32));
+do_long:
+ rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case TYPE_INT64:
+ u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64);
+ if (!LL_GE_ZERO(u.ll)) {
+ LL_NEG(u.ll, u.ll);
+ flags |= FLAG_NEG;
+ }
+ goto do_longlong;
+ case TYPE_UINT64:
+ u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64);
+do_longlong:
+ rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ u.d = nas ? nap->u.d : va_arg(ap, double);
+ if( nas != NULL ) {
+ i = fmt - dolPt;
+ if( i < sizeof( pattern ) ) {
+ pattern[0] = '%';
+ memcpy( &pattern[1], dolPt, i );
+ rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
+ }
+ } else {
+ rv = cvt_f(ss, u.d, fmt0, fmt);
+ }
+
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'c':
+ u.ch = nas ? nap->u.i : va_arg(ap, int);
+ if ((flags & FLAG_LEFT) == 0) {
+ while (width-- > 1) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ rv = (*ss->stuff)(ss, &u.ch, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ if (flags & FLAG_LEFT) {
+ while (width-- > 1) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ break;
+
+ case 'p':
+ if (sizeof(void *) == sizeof(PRInt32)) {
+ type = TYPE_UINT32;
+ } else if (sizeof(void *) == sizeof(PRInt64)) {
+ type = TYPE_UINT64;
+ } else if (sizeof(void *) == sizeof(int)) {
+ type = TYPE_UINTN;
+ } else {
+ PR_ASSERT(0);
+ break;
+ }
+ radix = 16;
+ goto fetch_and_convert;
+
+#ifndef WIN32
+ case 'S':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+#if 0
+ case 'C':
+ case 'E':
+ case 'G':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+#ifdef WIN32
+ case 'S':
+ u.ws = nas ? nap->u.ws : va_arg(ap, const WCHAR*);
+
+ /* Get the required size in rv */
+ rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL);
+ if (rv == 0) {
+ rv = 1;
+ }
+ pBuf = PR_MALLOC(rv);
+ WideCharToMultiByte(CP_ACP, 0, u.ws, -1, pBuf, (int)rv, NULL, NULL);
+ pBuf[rv-1] = '\0';
+
+ rv = cvt_s(ss, pBuf, width, prec, flags);
+
+ /* We don't need the allocated buffer anymore */
+ PR_Free(pBuf);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+#endif
+
+ case 's':
+ u.s = nas ? nap->u.s : va_arg(ap, const char*);
+ rv = cvt_s(ss, u.s, width, prec, flags);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'n':
+ u.ip = nas ? nap->u.ip : va_arg(ap, int*);
+ if (u.ip) {
+ *u.ip = ss->cur - ss->base;
+ }
+ break;
+
+ default:
+ /* Not a % token after all... skip it */
+#if 0
+ PR_ASSERT(0);
+#endif
+ rv = (*ss->stuff)(ss, "%", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Stuff trailing NUL */
+ rv = (*ss->stuff)(ss, "\0", 1);
+
+ if( nas && ( nas != nasArray ) ) {
+ PR_DELETE( nas );
+ }
+
+ return rv;
+}
+
+/************************************************************************/
+
+static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ int rv;
+
+ /*
+ ** We will add len to ss->maxlen at the end of the function. First check
+ ** if ss->maxlen + len would overflow or be greater than PR_INT32_MAX.
+ */
+ if (PR_UINT32_MAX - ss->maxlen < len || ss->maxlen + len > PR_INT32_MAX) {
+ return -1;
+ }
+ rv = (*ss->func)(ss->arg, sp, len);
+ if (rv < 0) {
+ return rv;
+ }
+ ss->maxlen += len;
+ return 0;
+}
+
+PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg,
+ const char *fmt, ...)
+{
+ va_list ap;
+ PRUint32 rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsxprintf(func, arg, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg,
+ const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = FuncStuff;
+ ss.func = func;
+ ss.arg = arg;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, fmt, ap);
+ return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
+}
+
+/*
+** Stuff routine that automatically grows the malloc'd output buffer
+** before it overflows.
+*/
+static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ ptrdiff_t off;
+ char *newbase;
+ PRUint32 newlen;
+
+ off = ss->cur - ss->base;
+ if (PR_UINT32_MAX - len < off) {
+ /* off + len would be too big. */
+ return -1;
+ }
+ if (off + len >= ss->maxlen) {
+ /* Grow the buffer */
+ PRUint32 increment = (len > 32) ? len : 32;
+ if (PR_UINT32_MAX - ss->maxlen < increment) {
+ /* ss->maxlen + increment would overflow. */
+ return -1;
+ }
+ newlen = ss->maxlen + increment;
+ if (newlen > PR_INT32_MAX) {
+ return -1;
+ }
+ if (ss->base) {
+ newbase = (char*) PR_REALLOC(ss->base, newlen);
+ } else {
+ newbase = (char*) PR_MALLOC(newlen);
+ }
+ if (!newbase) {
+ /* Ran out of memory */
+ return -1;
+ }
+ ss->base = newbase;
+ ss->maxlen = newlen;
+ ss->cur = ss->base + off;
+ }
+
+ /* Copy data */
+ while (len) {
+ --len;
+ *ss->cur++ = *sp++;
+ }
+ PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
+ return 0;
+}
+
+/*
+** sprintf into a malloc'd buffer
+*/
+PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsmprintf(fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+/*
+** Free memory allocated, for the caller, by PR_smprintf
+*/
+PR_IMPLEMENT(void) PR_smprintf_free(char *mem)
+{
+ PR_DELETE(mem);
+}
+
+PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, fmt, ap);
+ 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 *ss, const char *sp, PRUint32 len)
+{
+ PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
+
+ if (len > limit) {
+ len = limit;
+ }
+ while (len) {
+ --len;
+ *ss->cur++ = *sp++;
+ }
+ return 0;
+}
+
+/*
+** sprintf into a fixed size buffer. Make sure there is a NUL at the end
+** when finished.
+*/
+PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...)
+{
+ va_list ap;
+ PRUint32 rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsnprintf(out, outlen, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt,
+ va_list ap)
+{
+ SprintfState ss;
+ PRUint32 n;
+
+ PR_ASSERT(outlen != 0 && outlen <= PR_INT32_MAX);
+ if (outlen == 0 || outlen > PR_INT32_MAX) {
+ return 0;
+ }
+
+ ss.stuff = LimitStuff;
+ ss.base = out;
+ ss.cur = out;
+ ss.maxlen = outlen;
+ (void) dosprintf(&ss, fmt, ap);
+
+ /* 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 - 1) = '\0';
+ }
+
+ n = ss.cur - ss.base;
+ return n ? n - 1 : n;
+}
+
+PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...)
+{
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsprintf_append(last, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ if (last) {
+ size_t lastlen = strlen(last);
+ if (lastlen > PR_INT32_MAX) {
+ return 0;
+ }
+ ss.base = last;
+ ss.cur = last + lastlen;
+ ss.maxlen = lastlen;
+ } else {
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ }
+ rv = dosprintf(&ss, fmt, ap);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+