--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -7,863 +7,22 @@
/*
* Portable safe sprintf code.
*
* Author: Kipp E.B. Hickman
*/
#include "jsprf.h"
-#include "mozilla/Sprintf.h"
-#include "mozilla/Vector.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "mozilla/Printf.h"
#include "jsalloc.h"
-#include "jspubtd.h"
-#include "jsstr.h"
-#include "jsutil.h"
using namespace js;
-/*
- * 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
-
-/*
- * Numbered Argument State
- */
-struct NumArgState
-{
- int type; // type of the current ap
- va_list ap; // point to the corresponding position on ap
-};
-
-typedef mozilla::Vector<NumArgState, 20, js::SystemAllocPolicy> NumArgStateVector;
-
-
-#define TYPE_SHORT 0
-#define TYPE_USHORT 1
-#define TYPE_INTN 2
-#define TYPE_UINTN 3
-#define TYPE_LONG 4
-#define TYPE_ULONG 5
-#define TYPE_LONGLONG 6
-#define TYPE_ULONGLONG 7
-#define TYPE_STRING 8
-#define TYPE_DOUBLE 9
-#define TYPE_INTSTR 10
-#define TYPE_POINTER 11
-#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
-bool
-mozilla::PrintfTarget::fill2(const char* src, int srclen, int width, int flags)
-{
- char space = ' ';
-
- width -= srclen;
- if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting
- if (flags & FLAG_ZEROS)
- space = '0';
- while (--width >= 0) {
- if (!emit(&space, 1))
- return false;
- }
- }
-
- // Copy out the source data
- if (!emit(src, srclen))
- return false;
-
- if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting
- while (--width >= 0) {
- if (!emit(&space, 1))
- return false;
- }
- }
- return true;
-}
-
-/*
- * Fill a number. The order is: optional-sign zero-filling conversion-digits
- */
-bool
-mozilla::PrintfTarget::fill_n(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;
- 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) {
- if (!emit(" ", 1))
- return false;
- }
- if (signwidth) {
- if (!emit(&sign, 1))
- return false;
- }
- while (--precwidth >= 0) {
- if (!emit("0", 1))
- return false;
- }
- while (--zerowidth >= 0) {
- if (!emit("0", 1))
- return false;
- }
- if (!emit(src, uint32_t(srclen)))
- return false;
- while (--rightspaces >= 0) {
- if (!emit(" ", 1))
- return false;
- }
- return true;
-}
-
-/* Convert a long into its printable form. */
-bool
-mozilla::PrintfTarget::cvt_l(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 true;
-
- // 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(cvt, digits, width, prec, type, flags);
-}
-
-/* Convert a 64-bit integer into its printable form. */
-bool
-mozilla::PrintfTarget::cvt_ll(int64_t num, int width, int prec, int radix,
- int type, int flags, const char* hexp)
-{
- // According to the man page, this needs to happen.
- if (prec == 0 && num == 0)
- return true;
-
- // 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.
- int64_t rad = int64_t(radix);
- char cvtbuf[100];
- char* cvt = cvtbuf + sizeof(cvtbuf);
- int digits = 0;
- while (num != 0) {
- int64_t quot = uint64_t(num) / rad;
- int64_t rem = uint64_t(num) % rad;
- int32_t digit = int32_t(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(cvt, digits, width, prec, type, flags);
-}
-
-/*
- * Convert a double precision floating point number into its printable
- * form.
- */
-bool
-mozilla::PrintfTarget::cvt_f(double d, const char* fmt0, const char* fmt1)
-{
- char fin[20];
- char fout[300];
- int amount = fmt1 - fmt0;
-
- MOZ_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
- if (amount >= (int)sizeof(fin)) {
- // Totally bogus % command to sprintf. Just ignore it
- return true;
- }
- js_memcpy(fin, fmt0, (size_t)amount);
- fin[amount] = 0;
-
- // Convert floating point using the native snprintf code
-#ifdef DEBUG
- {
- const char* p = fin;
- while (*p) {
- MOZ_ASSERT(*p != 'L');
- p++;
- }
- }
-#endif
- SprintfLiteral(fout, fin, d);
-
- return emit(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.
- */
-bool
-mozilla::PrintfTarget::cvt_s(const char* s, int width, int prec, int flags)
-{
- if (prec == 0)
- return true;
- if (!s)
- s = "(null)";
-
- // Limit string length by precision value
- int slen = int(strlen(s));
- if (0 < prec && prec < slen)
- slen = prec;
-
- // and away we go
- return fill2(s, slen, width, flags);
-}
-
-/*
- * 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 bool
-BuildArgArray(const char* fmt, va_list ap, NumArgStateVector& nas)
-{
- size_t number = 0, cn = 0, i;
- const char* p;
- char c;
-
-
- // First pass:
- // Detemine how many legal % I have got, then allocate space.
-
- p = fmt;
- 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)
- MOZ_CRASH("Bad format string");
- number++;
- } else { // non-numbered argument case
- if (number > 0)
- MOZ_CRASH("Bad format string");
- i = 1;
- }
- break;
- }
-
- c = *p++;
- }
- }
-
- if (number == 0)
- return true;
-
- if (!nas.growByUninitialized(number))
- return false;
-
- 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)
- MOZ_CRASH("Bad format string");
-
- // 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
- MOZ_CRASH("Bad format string");
- }
-
- while ((c >= '0') && (c <= '9')) {
- c = *p++;
- }
-
- // precision
- if (c == '.') {
- c = *p++;
- if (c == '*') {
- // not supported feature, for the argument is not numbered
- MOZ_CRASH("Bad format string");
- }
-
- while ((c >= '0') && (c <= '9')) {
- c = *p++;
- }
- }
-
- // size
- nas[cn].type = TYPE_INTN;
- if (c == 'h') {
- nas[cn].type = TYPE_SHORT;
- c = *p++;
- } else if (c == 'L') {
- nas[cn].type = TYPE_LONGLONG;
- c = *p++;
- } else if (c == 'l') {
- nas[cn].type = TYPE_LONG;
- c = *p++;
- if (c == 'l') {
- nas[cn].type = TYPE_LONGLONG;
- c = *p++;
- }
- } else if (c == 'z' || c == 'I') {
- static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
- sizeof(size_t) == sizeof(long long),
- "size_t is not one of the expected sizes");
- nas[cn].type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
- sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
- 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':
- nas[cn].type = TYPE_POINTER;
- break;
-
- case 'C':
- case 'S':
- case 'E':
- case 'G':
- // XXX not supported I suppose
- MOZ_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:
- MOZ_ASSERT(0);
- nas[cn].type = TYPE_UNKNOWN;
- break;
- }
-
- // get a legal para.
- if (nas[cn].type == TYPE_UNKNOWN)
- MOZ_CRASH("Bad format string");
- }
-
-
- // Third pass:
- // Fill nas[].ap.
-
- cn = 0;
- while (cn < number) {
- if (nas[cn].type == TYPE_UNKNOWN) {
- cn++;
- continue;
- }
-
- VARARGS_ASSIGN(nas[cn].ap, ap);
-
- switch (nas[cn].type) {
- case TYPE_SHORT:
- case TYPE_USHORT:
- case TYPE_INTN:
- case TYPE_UINTN: (void) va_arg(ap, int); break;
- case TYPE_LONG: (void) va_arg(ap, long); break;
- case TYPE_ULONG: (void) va_arg(ap, unsigned long); break;
- case TYPE_LONGLONG: (void) va_arg(ap, long long); break;
- case TYPE_ULONGLONG: (void) va_arg(ap, unsigned long long); break;
- case TYPE_STRING: (void) va_arg(ap, char*); break;
- case TYPE_INTSTR: (void) va_arg(ap, int*); break;
- case TYPE_DOUBLE: (void) va_arg(ap, double); break;
- case TYPE_POINTER: (void) va_arg(ap, void*); break;
-
- default: MOZ_CRASH();
- }
-
- cn++;
- }
-
- return true;
-}
-
-bool
-mozilla::PrintfTarget::vprint(const char* fmt, va_list ap)
-{
- char c;
- int flags, width, prec, radix, type;
- union {
- char ch;
- int i;
- long l;
- long long ll;
- double d;
- const char* s;
- int* ip;
- void* p;
- } u;
- const char* fmt0;
- static const char hex[] = "0123456789abcdef";
- static const char HEX[] = "0123456789ABCDEF";
- const char* hexp;
- int i;
- char pattern[20];
- const char* dolPt = nullptr; // in "%4$.2f", dolPt will point to '.'
-
- // Build an argument array, IF the fmt is numbered argument
- // list style, to contain the Numbered Argument list pointers.
-
- NumArgStateVector nas;
- if (!BuildArgArray(fmt, ap, nas)) {
- // the fmt contains error Numbered Argument format, jliu@netscape.com
- MOZ_CRASH("Bad format string");
- }
-
- while ((c = *fmt++) != 0) {
- if (c != '%') {
- if (!emit(fmt - 1, 1))
- return false;
-
- 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 %%
- if (!emit(fmt - 1, 1))
- return false;
-
- continue;
- }
-
- if (!nas.empty()) {
- // 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)
- MOZ_CRASH("Bad format string");
-
- ap = nas[i - 1].ap;
- 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_SHORT;
- c = *fmt++;
- } else if (c == 'L') {
- type = TYPE_LONGLONG;
- c = *fmt++;
- } else if (c == 'l') {
- type = TYPE_LONG;
- c = *fmt++;
- if (c == 'l') {
- type = TYPE_LONGLONG;
- c = *fmt++;
- }
- } else if (c == 'z' || c == 'I') {
- static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
- sizeof(size_t) == sizeof(long long),
- "size_t is not one of the expected sizes");
- type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
- sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
- 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_SHORT:
- u.l = va_arg(ap, int);
- if (u.l < 0) {
- u.l = -u.l;
- flags |= FLAG_NEG;
- }
- goto do_long;
- case TYPE_USHORT:
- u.l = (unsigned short) va_arg(ap, unsigned int);
- goto do_long;
- case TYPE_INTN:
- u.l = va_arg(ap, int);
- if (u.l < 0) {
- u.l = -u.l;
- flags |= FLAG_NEG;
- }
- goto do_long;
- case TYPE_UINTN:
- u.l = (long)va_arg(ap, unsigned int);
- goto do_long;
-
- case TYPE_LONG:
- u.l = va_arg(ap, long);
- if (u.l < 0) {
- u.l = -u.l;
- flags |= FLAG_NEG;
- }
- goto do_long;
- case TYPE_ULONG:
- u.l = (long)va_arg(ap, unsigned long);
- do_long:
- if (!cvt_l(u.l, width, prec, radix, type, flags, hexp))
- return false;
-
- break;
-
- case TYPE_LONGLONG:
- u.ll = va_arg(ap, long long);
- if (u.ll < 0) {
- u.ll = -u.ll;
- flags |= FLAG_NEG;
- }
- goto do_longlong;
- case TYPE_POINTER:
- u.ll = (uintptr_t)va_arg(ap, void*);
- goto do_longlong;
- case TYPE_ULONGLONG:
- u.ll = va_arg(ap, unsigned long long);
- do_longlong:
- if (!cvt_ll(u.ll, width, prec, radix, type, flags, hexp))
- return false;
-
- break;
- }
- break;
-
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- u.d = va_arg(ap, double);
- if (!nas.empty()) {
- i = fmt - dolPt;
- if (i < int(sizeof(pattern))) {
- pattern[0] = '%';
- memcpy(&pattern[1], dolPt, size_t(i));
- if (!cvt_f(u.d, pattern, &pattern[i + 1]))
- return false;
- }
- } else {
- if (!cvt_f(u.d, fmt0, fmt))
- return false;
- }
-
- break;
-
- case 'c':
- if ((flags & FLAG_LEFT) == 0) {
- while (width-- > 1) {
- if (!emit(" ", 1))
- return false;
- }
- }
- switch (type) {
- case TYPE_SHORT:
- case TYPE_INTN:
- u.ch = va_arg(ap, int);
- if (!emit(&u.ch, 1))
- return false;
- break;
- }
- if (flags & FLAG_LEFT) {
- while (width-- > 1) {
- if (!emit(" ", 1))
- return false;
- }
- }
- break;
-
- case 'p':
- type = TYPE_POINTER;
- radix = 16;
- goto fetch_and_convert;
-
-#if 0
- case 'C':
- case 'S':
- case 'E':
- case 'G':
- // XXX not supported I suppose
- MOZ_ASSERT(0);
- break;
-#endif
-
- case 's':
- u.s = va_arg(ap, const char*);
- if (!cvt_s(u.s, width, prec, flags))
- return false;
- break;
-
- case 'n':
- u.ip = va_arg(ap, int*);
- if (u.ip) {
- *u.ip = mEmitted;
- }
- break;
-
- default:
- // Not a % token after all... skip it
-#if 0
- MOZ_ASSERT(0);
-#endif
- if (!emit("%", 1))
- return false;
- if (!emit(fmt - 1, 1))
- return false;
- }
- }
-
- // Stuff trailing NUL
- if (!emit("\0", 1))
- return false;
-
- return true;
-}
-
-/************************************************************************/
-
-bool
-mozilla::PrintfTarget::print(const char* format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- bool result = vprint(format, ap);
- va_end(ap);
- return result;
-}
-
-#undef TYPE_SHORT
-#undef TYPE_USHORT
-#undef TYPE_INTN
-#undef TYPE_UINTN
-#undef TYPE_LONG
-#undef TYPE_ULONG
-#undef TYPE_LONGLONG
-#undef TYPE_ULONGLONG
-#undef TYPE_STRING
-#undef TYPE_DOUBLE
-#undef TYPE_INTSTR
-#undef TYPE_POINTER
-#undef TYPE_UNKNOWN
-
-#undef FLAG_LEFT
-#undef FLAG_SIGNED
-#undef FLAG_SPACED
-#undef FLAG_ZEROS
-#undef FLAG_NEG
-
JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char* result = mozilla::Vsmprintf<js::SystemAllocPolicy>(fmt, ap);
va_end(ap);
return result;
}
--- a/js/src/jsprf.h
+++ b/js/src/jsprf.h
@@ -2,224 +2,22 @@
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef jsprf_h
#define jsprf_h
-/*
-** API for PR printf like routines. Supports the following formats
-** %d - decimal
-** %u - unsigned decimal
-** %x - unsigned hex
-** %X - unsigned uppercase hex
-** %o - unsigned octal
-** %hd, %hu, %hx, %hX, %ho - "short" versions of above
-** %ld, %lu, %lx, %lX, %lo - "long" versions of above
-** %lld, %llu, %llx, %llX, %llo - "long long" versions of above
-** %zd, %zo, %zu, %zx, %zX - size_t versions of above
-** %Id, %Io, %Iu, %Ix, %IX - size_t versions of above (for Windows compat)
-** You should use PRI*SIZE macros instead
-** %s - string
-** %c - character
-** %p - pointer (deals with machine dependent pointer size)
-** %f - float
-** %g - float
-*/
-
-#include "mozilla/AllocPolicy.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/IntegerPrintfMacros.h"
-#include "mozilla/SizePrintfMacros.h"
-#include "mozilla/Types.h"
+#include "mozilla/Printf.h"
#include <stdarg.h>
-#include <string.h>
#include "jstypes.h"
-namespace mozilla {
-
-/*
- * This class may be subclassed to provide a way to get the output of
- * a printf-like call, as the output is generated.
- */
-class PrintfTarget
-{
-public:
- /* The Printf-like interface. */
- bool MFBT_API print(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
-
- /* The Vprintf-like interface. */
- bool MFBT_API vprint(const char* format, va_list);
-
-protected:
- MFBT_API PrintfTarget() : mEmitted(0) { }
- virtual ~PrintfTarget() { }
-
- /* Subclasses override this. It is called when more output is
- available. It may be called with len==0. This should return
- true on success, or false on failure. */
- virtual bool append(const char* sp, size_t len) = 0;
-
-private:
-
- /* Number of bytes emitted so far. */
- size_t mEmitted;
-
- /* The implementation calls this to emit bytes and update
- mEmitted. */
- bool emit(const char* sp, size_t len) {
- mEmitted += len;
- return append(sp, len);
- }
-
- bool fill2(const char* src, int srclen, int width, int flags);
- bool fill_n(const char* src, int srclen, int width, int prec, int type, int flags);
- bool cvt_l(long num, int width, int prec, int radix, int type, int flags, const char* hxp);
- bool cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags, const char* hexp);
- bool cvt_f(double d, const char* fmt0, const char* fmt1);
- bool cvt_s(const char* s, int width, int prec, int flags);
-};
-
-// Used in the implementation of Smprintf et al.
-template<typename AllocPolicy>
-class MOZ_STACK_CLASS SprintfState final : public mozilla::PrintfTarget, private AllocPolicy
-{
- public:
- explicit SprintfState(char* base)
- : mMaxlen(base ? strlen(base) : 0)
- , mBase(base)
- , mCur(base ? base + mMaxlen : 0)
- {
- }
-
- ~SprintfState() {
- this->free_(mBase);
- }
-
- char* release() {
- char* result = mBase;
- mBase = nullptr;
- return result;
- }
-
- protected:
-
- bool append(const char* sp, size_t len) override {
- ptrdiff_t off;
- char* newbase;
- size_t newlen;
-
- off = mCur - mBase;
- if (off + len >= mMaxlen) {
- /* Grow the buffer */
- newlen = mMaxlen + ((len > 32) ? len : 32);
- newbase = static_cast<char*>(this->maybe_pod_realloc(mBase, mMaxlen, newlen));
- if (!newbase) {
- /* Ran out of memory */
- return false;
- }
- mBase = newbase;
- mMaxlen = newlen;
- mCur = mBase + off;
- }
-
- /* Copy data */
- memcpy(mCur, sp, len);
- mCur += len;
- MOZ_ASSERT(size_t(mCur - mBase) <= mMaxlen);
- return true;
- }
-
- private:
-
- size_t mMaxlen;
- char* mBase;
- char* mCur;
-};
-
-/*
-** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
-** buffer on success, nullptr on failure. Call AllocPolicy::free_ to release
-** the memory returned.
-*/
-template<typename AllocPolicy = mozilla::MallocAllocPolicy>
-MOZ_FORMAT_PRINTF(1, 2)
-char* Smprintf(const char* fmt, ...)
-{
- SprintfState<AllocPolicy> ss(nullptr);
- va_list ap;
- va_start(ap, fmt);
- bool r = ss.vprint(fmt, ap);
- va_end(ap);
- if (!r) {
- return nullptr;
- }
- return ss.release();
-}
-
-/*
-** "append" sprintf into a malloc'd buffer. "last" is the last value of
-** the malloc'd buffer. sprintf will append data to the end of last,
-** growing it as necessary using realloc. If last is nullptr, SmprintfAppend
-** will allocate the initial string. The return value is the new value of
-** last for subsequent calls, or nullptr if there is a malloc failure.
-*/
-template<typename AllocPolicy = mozilla::MallocAllocPolicy>
-MOZ_FORMAT_PRINTF(2, 3)
-char* SmprintfAppend(char* last, const char* fmt, ...)
-{
- SprintfState<AllocPolicy> ss(last);
- va_list ap;
- va_start(ap, fmt);
- bool r = ss.vprint(fmt, ap);
- va_end(ap);
- if (!r) {
- return nullptr;
- }
- return ss.release();
-}
-
-/*
-** va_list forms of the above.
-*/
-template<typename AllocPolicy = mozilla::MallocAllocPolicy>
-char* Vsmprintf(const char* fmt, va_list ap)
-{
- SprintfState<AllocPolicy> ss(nullptr);
- if (!ss.vprint(fmt, ap))
- return nullptr;
- return ss.release();
-}
-
-template<typename AllocPolicy = mozilla::MallocAllocPolicy>
-char* VsmprintfAppend(char* last, const char* fmt, va_list ap)
-{
- SprintfState<AllocPolicy> ss(last);
- if (!ss.vprint(fmt, ap))
- return nullptr;
- return ss.release();
-}
-
-/*
-** Free the memory allocated, for the caller, by Smprintf.
-*/
-template<typename AllocPolicy = mozilla::MallocAllocPolicy>
-void SmprintfFree(char* mem)
-{
- AllocPolicy allocator;
- allocator.free_(mem);
-}
-
-} // namespace mozilla
-
/* Wrappers for mozilla::Smprintf and friends that are used throughout
JS. */
extern JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
MOZ_FORMAT_PRINTF(1, 2);
extern JS_PUBLIC_API(void) JS_smprintf_free(char* mem);
new file mode 100644
--- /dev/null
+++ b/mozglue/misc/Printf.cpp
@@ -0,0 +1,856 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "mozilla/AllocPolicy.h"
+#include "mozilla/Printf.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Vector.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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
+
+/*
+ * Numbered Argument State
+ */
+struct NumArgState
+{
+ int type; // type of the current ap
+ va_list ap; // point to the corresponding position on ap
+};
+
+typedef mozilla::Vector<NumArgState, 20, mozilla::MallocAllocPolicy> NumArgStateVector;
+
+
+#define TYPE_SHORT 0
+#define TYPE_USHORT 1
+#define TYPE_INTN 2
+#define TYPE_UINTN 3
+#define TYPE_LONG 4
+#define TYPE_ULONG 5
+#define TYPE_LONGLONG 6
+#define TYPE_ULONGLONG 7
+#define TYPE_STRING 8
+#define TYPE_DOUBLE 9
+#define TYPE_INTSTR 10
+#define TYPE_POINTER 11
+#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
+bool
+mozilla::PrintfTarget::fill2(const char* src, int srclen, int width, int flags)
+{
+ char space = ' ';
+
+ width -= srclen;
+ if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting
+ if (flags & FLAG_ZEROS)
+ space = '0';
+ while (--width >= 0) {
+ if (!emit(&space, 1))
+ return false;
+ }
+ }
+
+ // Copy out the source data
+ if (!emit(src, srclen))
+ return false;
+
+ if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting
+ while (--width >= 0) {
+ if (!emit(&space, 1))
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Fill a number. The order is: optional-sign zero-filling conversion-digits
+ */
+bool
+mozilla::PrintfTarget::fill_n(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;
+ 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) {
+ if (!emit(" ", 1))
+ return false;
+ }
+ if (signwidth) {
+ if (!emit(&sign, 1))
+ return false;
+ }
+ while (--precwidth >= 0) {
+ if (!emit("0", 1))
+ return false;
+ }
+ while (--zerowidth >= 0) {
+ if (!emit("0", 1))
+ return false;
+ }
+ if (!emit(src, uint32_t(srclen)))
+ return false;
+ while (--rightspaces >= 0) {
+ if (!emit(" ", 1))
+ return false;
+ }
+ return true;
+}
+
+/* Convert a long into its printable form. */
+bool
+mozilla::PrintfTarget::cvt_l(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 true;
+
+ // 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(cvt, digits, width, prec, type, flags);
+}
+
+/* Convert a 64-bit integer into its printable form. */
+bool
+mozilla::PrintfTarget::cvt_ll(int64_t num, int width, int prec, int radix,
+ int type, int flags, const char* hexp)
+{
+ // According to the man page, this needs to happen.
+ if (prec == 0 && num == 0)
+ return true;
+
+ // 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.
+ int64_t rad = int64_t(radix);
+ char cvtbuf[100];
+ char* cvt = cvtbuf + sizeof(cvtbuf);
+ int digits = 0;
+ while (num != 0) {
+ int64_t quot = uint64_t(num) / rad;
+ int64_t rem = uint64_t(num) % rad;
+ int32_t digit = int32_t(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(cvt, digits, width, prec, type, flags);
+}
+
+/*
+ * Convert a double precision floating point number into its printable
+ * form.
+ */
+bool
+mozilla::PrintfTarget::cvt_f(double d, const char* fmt0, const char* fmt1)
+{
+ char fin[20];
+ char fout[300];
+ int amount = fmt1 - fmt0;
+
+ MOZ_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
+ if (amount >= (int)sizeof(fin)) {
+ // Totally bogus % command to sprintf. Just ignore it
+ return true;
+ }
+ memcpy(fin, fmt0, (size_t)amount);
+ fin[amount] = 0;
+
+ // Convert floating point using the native snprintf code
+#ifdef DEBUG
+ {
+ const char* p = fin;
+ while (*p) {
+ MOZ_ASSERT(*p != 'L');
+ p++;
+ }
+ }
+#endif
+ SprintfLiteral(fout, fin, d);
+
+ return emit(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.
+ */
+bool
+mozilla::PrintfTarget::cvt_s(const char* s, int width, int prec, int flags)
+{
+ if (prec == 0)
+ return true;
+ if (!s)
+ s = "(null)";
+
+ // Limit string length by precision value
+ int slen = int(strlen(s));
+ if (0 < prec && prec < slen)
+ slen = prec;
+
+ // and away we go
+ return fill2(s, slen, width, flags);
+}
+
+/*
+ * 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 bool
+BuildArgArray(const char* fmt, va_list ap, NumArgStateVector& nas)
+{
+ size_t number = 0, cn = 0, i;
+ const char* p;
+ char c;
+
+
+ // First pass:
+ // Detemine how many legal % I have got, then allocate space.
+
+ p = fmt;
+ 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)
+ MOZ_CRASH("Bad format string");
+ number++;
+ } else { // non-numbered argument case
+ if (number > 0)
+ MOZ_CRASH("Bad format string");
+ i = 1;
+ }
+ break;
+ }
+
+ c = *p++;
+ }
+ }
+
+ if (number == 0)
+ return true;
+
+ // Only allow a limited number of arguments.
+ MOZ_RELEASE_ASSERT(number <= 20);
+
+ if (!nas.growByUninitialized(number))
+ return false;
+
+ 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)
+ MOZ_CRASH("Bad format string");
+
+ // 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
+ MOZ_CRASH("Bad format string");
+ }
+
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+
+ // precision
+ if (c == '.') {
+ c = *p++;
+ if (c == '*') {
+ // not supported feature, for the argument is not numbered
+ MOZ_CRASH("Bad format string");
+ }
+
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+
+ // size
+ nas[cn].type = TYPE_INTN;
+ if (c == 'h') {
+ nas[cn].type = TYPE_SHORT;
+ c = *p++;
+ } else if (c == 'L') {
+ nas[cn].type = TYPE_LONGLONG;
+ c = *p++;
+ } else if (c == 'l') {
+ nas[cn].type = TYPE_LONG;
+ c = *p++;
+ if (c == 'l') {
+ nas[cn].type = TYPE_LONGLONG;
+ c = *p++;
+ }
+ } else if (c == 'z' || c == 'I') {
+ static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
+ sizeof(size_t) == sizeof(long long),
+ "size_t is not one of the expected sizes");
+ nas[cn].type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
+ sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
+ 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':
+ nas[cn].type = TYPE_POINTER;
+ break;
+
+ case 'C':
+ case 'S':
+ case 'E':
+ case 'G':
+ // XXX not supported I suppose
+ MOZ_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:
+ MOZ_ASSERT(0);
+ nas[cn].type = TYPE_UNKNOWN;
+ break;
+ }
+
+ // get a legal para.
+ if (nas[cn].type == TYPE_UNKNOWN)
+ MOZ_CRASH("Bad format string");
+ }
+
+
+ // Third pass:
+ // Fill nas[].ap.
+
+ cn = 0;
+ while (cn < number) {
+ if (nas[cn].type == TYPE_UNKNOWN) {
+ cn++;
+ continue;
+ }
+
+ VARARGS_ASSIGN(nas[cn].ap, ap);
+
+ switch (nas[cn].type) {
+ case TYPE_SHORT:
+ case TYPE_USHORT:
+ case TYPE_INTN:
+ case TYPE_UINTN: (void) va_arg(ap, int); break;
+ case TYPE_LONG: (void) va_arg(ap, long); break;
+ case TYPE_ULONG: (void) va_arg(ap, unsigned long); break;
+ case TYPE_LONGLONG: (void) va_arg(ap, long long); break;
+ case TYPE_ULONGLONG: (void) va_arg(ap, unsigned long long); break;
+ case TYPE_STRING: (void) va_arg(ap, char*); break;
+ case TYPE_INTSTR: (void) va_arg(ap, int*); break;
+ case TYPE_DOUBLE: (void) va_arg(ap, double); break;
+ case TYPE_POINTER: (void) va_arg(ap, void*); break;
+
+ default: MOZ_CRASH();
+ }
+
+ cn++;
+ }
+
+ return true;
+}
+
+bool
+mozilla::PrintfTarget::vprint(const char* fmt, va_list ap)
+{
+ char c;
+ int flags, width, prec, radix, type;
+ union {
+ char ch;
+ int i;
+ long l;
+ long long ll;
+ double d;
+ const char* s;
+ int* ip;
+ void* p;
+ } u;
+ const char* fmt0;
+ static const char hex[] = "0123456789abcdef";
+ static const char HEX[] = "0123456789ABCDEF";
+ const char* hexp;
+ int i;
+ char pattern[20];
+ const char* dolPt = nullptr; // in "%4$.2f", dolPt will point to '.'
+
+ // Build an argument array, IF the fmt is numbered argument
+ // list style, to contain the Numbered Argument list pointers.
+
+ NumArgStateVector nas;
+ if (!BuildArgArray(fmt, ap, nas)) {
+ // the fmt contains error Numbered Argument format, jliu@netscape.com
+ MOZ_CRASH("Bad format string");
+ }
+
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ if (!emit(fmt - 1, 1))
+ return false;
+
+ 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 %%
+ if (!emit(fmt - 1, 1))
+ return false;
+
+ continue;
+ }
+
+ if (!nas.empty()) {
+ // 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)
+ MOZ_CRASH("Bad format string");
+
+ ap = nas[i - 1].ap;
+ 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_SHORT;
+ c = *fmt++;
+ } else if (c == 'L') {
+ type = TYPE_LONGLONG;
+ c = *fmt++;
+ } else if (c == 'l') {
+ type = TYPE_LONG;
+ c = *fmt++;
+ if (c == 'l') {
+ type = TYPE_LONGLONG;
+ c = *fmt++;
+ }
+ } else if (c == 'z' || c == 'I') {
+ static_assert(sizeof(size_t) == sizeof(int) || sizeof(size_t) == sizeof(long) ||
+ sizeof(size_t) == sizeof(long long),
+ "size_t is not one of the expected sizes");
+ type = sizeof(size_t) == sizeof(int) ? TYPE_INTN :
+ sizeof(size_t) == sizeof(long) ? TYPE_LONG : TYPE_LONGLONG;
+ 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_SHORT:
+ u.l = va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_USHORT:
+ u.l = (unsigned short) va_arg(ap, unsigned int);
+ goto do_long;
+ case TYPE_INTN:
+ u.l = va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINTN:
+ u.l = (long)va_arg(ap, unsigned int);
+ goto do_long;
+
+ case TYPE_LONG:
+ u.l = va_arg(ap, long);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_ULONG:
+ u.l = (long)va_arg(ap, unsigned long);
+ do_long:
+ if (!cvt_l(u.l, width, prec, radix, type, flags, hexp))
+ return false;
+
+ break;
+
+ case TYPE_LONGLONG:
+ u.ll = va_arg(ap, long long);
+ if (u.ll < 0) {
+ u.ll = -u.ll;
+ flags |= FLAG_NEG;
+ }
+ goto do_longlong;
+ case TYPE_POINTER:
+ u.ll = (uintptr_t)va_arg(ap, void*);
+ goto do_longlong;
+ case TYPE_ULONGLONG:
+ u.ll = va_arg(ap, unsigned long long);
+ do_longlong:
+ if (!cvt_ll(u.ll, width, prec, radix, type, flags, hexp))
+ return false;
+
+ break;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ u.d = va_arg(ap, double);
+ if (!nas.empty()) {
+ i = fmt - dolPt;
+ if (i < int(sizeof(pattern))) {
+ pattern[0] = '%';
+ memcpy(&pattern[1], dolPt, size_t(i));
+ if (!cvt_f(u.d, pattern, &pattern[i + 1]))
+ return false;
+ }
+ } else {
+ if (!cvt_f(u.d, fmt0, fmt))
+ return false;
+ }
+
+ break;
+
+ case 'c':
+ if ((flags & FLAG_LEFT) == 0) {
+ while (width-- > 1) {
+ if (!emit(" ", 1))
+ return false;
+ }
+ }
+ switch (type) {
+ case TYPE_SHORT:
+ case TYPE_INTN:
+ u.ch = va_arg(ap, int);
+ if (!emit(&u.ch, 1))
+ return false;
+ break;
+ }
+ if (flags & FLAG_LEFT) {
+ while (width-- > 1) {
+ if (!emit(" ", 1))
+ return false;
+ }
+ }
+ break;
+
+ case 'p':
+ type = TYPE_POINTER;
+ radix = 16;
+ goto fetch_and_convert;
+
+#if 0
+ case 'C':
+ case 'S':
+ case 'E':
+ case 'G':
+ // XXX not supported I suppose
+ MOZ_ASSERT(0);
+ break;
+#endif
+
+ case 's':
+ u.s = va_arg(ap, const char*);
+ if (!cvt_s(u.s, width, prec, flags))
+ return false;
+ break;
+
+ case 'n':
+ u.ip = va_arg(ap, int*);
+ if (u.ip) {
+ *u.ip = mEmitted;
+ }
+ break;
+
+ default:
+ // Not a % token after all... skip it
+#if 0
+ MOZ_ASSERT(0);
+#endif
+ if (!emit("%", 1))
+ return false;
+ if (!emit(fmt - 1, 1))
+ return false;
+ }
+ }
+
+ // Stuff trailing NUL
+ if (!emit("\0", 1))
+ return false;
+
+ return true;
+}
+
+/************************************************************************/
+
+bool
+mozilla::PrintfTarget::print(const char* format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ bool result = vprint(format, ap);
+ va_end(ap);
+ return result;
+}
+
+#undef TYPE_SHORT
+#undef TYPE_USHORT
+#undef TYPE_INTN
+#undef TYPE_UINTN
+#undef TYPE_LONG
+#undef TYPE_ULONG
+#undef TYPE_LONGLONG
+#undef TYPE_ULONGLONG
+#undef TYPE_STRING
+#undef TYPE_DOUBLE
+#undef TYPE_INTSTR
+#undef TYPE_POINTER
+#undef TYPE_UNKNOWN
+
+#undef FLAG_LEFT
+#undef FLAG_SIGNED
+#undef FLAG_SPACED
+#undef FLAG_ZEROS
+#undef FLAG_NEG
new file mode 100644
--- /dev/null
+++ b/mozglue/misc/Printf.h
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef mozilla_Printf_h
+#define mozilla_Printf_h
+
+/*
+** API for PR printf like routines. Supports the following formats
+** %d - decimal
+** %u - unsigned decimal
+** %x - unsigned hex
+** %X - unsigned uppercase hex
+** %o - unsigned octal
+** %hd, %hu, %hx, %hX, %ho - "short" versions of above
+** %ld, %lu, %lx, %lX, %lo - "long" versions of above
+** %lld, %llu, %llx, %llX, %llo - "long long" versions of above
+** %zd, %zo, %zu, %zx, %zX - size_t versions of above
+** %Id, %Io, %Iu, %Ix, %IX - size_t versions of above (for Windows compat)
+** You should use PRI*SIZE macros instead
+** %s - string
+** %c - character
+** %p - pointer (deals with machine dependent pointer size)
+** %f - float
+** %g - float
+*/
+
+#include "mozilla/AllocPolicy.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/SizePrintfMacros.h"
+#include "mozilla/Types.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+namespace mozilla {
+
+/*
+ * This class may be subclassed to provide a way to get the output of
+ * a printf-like call, as the output is generated.
+ */
+class PrintfTarget
+{
+public:
+ /* The Printf-like interface. */
+ bool MFBT_API print(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
+
+ /* The Vprintf-like interface. */
+ bool MFBT_API vprint(const char* format, va_list);
+
+protected:
+ MFBT_API PrintfTarget() : mEmitted(0) { }
+ virtual ~PrintfTarget() { }
+
+ /* Subclasses override this. It is called when more output is
+ available. It may be called with len==0. This should return
+ true on success, or false on failure. */
+ virtual bool append(const char* sp, size_t len) = 0;
+
+private:
+
+ /* Number of bytes emitted so far. */
+ size_t mEmitted;
+
+ /* The implementation calls this to emit bytes and update
+ mEmitted. */
+ bool emit(const char* sp, size_t len) {
+ mEmitted += len;
+ return append(sp, len);
+ }
+
+ bool fill2(const char* src, int srclen, int width, int flags);
+ bool fill_n(const char* src, int srclen, int width, int prec, int type, int flags);
+ bool cvt_l(long num, int width, int prec, int radix, int type, int flags, const char* hxp);
+ bool cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags, const char* hexp);
+ bool cvt_f(double d, const char* fmt0, const char* fmt1);
+ bool cvt_s(const char* s, int width, int prec, int flags);
+};
+
+// Used in the implementation of Smprintf et al.
+template<typename AllocPolicy>
+class MOZ_STACK_CLASS SprintfState final : public mozilla::PrintfTarget, private AllocPolicy
+{
+ public:
+ explicit SprintfState(char* base)
+ : mMaxlen(base ? strlen(base) : 0)
+ , mBase(base)
+ , mCur(base ? base + mMaxlen : 0)
+ {
+ }
+
+ ~SprintfState() {
+ this->free_(mBase);
+ }
+
+ char* release() {
+ char* result = mBase;
+ mBase = nullptr;
+ return result;
+ }
+
+ protected:
+
+ bool append(const char* sp, size_t len) override {
+ ptrdiff_t off;
+ char* newbase;
+ size_t newlen;
+
+ off = mCur - mBase;
+ if (off + len >= mMaxlen) {
+ /* Grow the buffer */
+ newlen = mMaxlen + ((len > 32) ? len : 32);
+ newbase = static_cast<char*>(this->maybe_pod_realloc(mBase, mMaxlen, newlen));
+ if (!newbase) {
+ /* Ran out of memory */
+ return false;
+ }
+ mBase = newbase;
+ mMaxlen = newlen;
+ mCur = mBase + off;
+ }
+
+ /* Copy data */
+ memcpy(mCur, sp, len);
+ mCur += len;
+ MOZ_ASSERT(size_t(mCur - mBase) <= mMaxlen);
+ return true;
+ }
+
+ private:
+
+ size_t mMaxlen;
+ char* mBase;
+ char* mCur;
+};
+
+/*
+** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
+** buffer on success, nullptr on failure. Call AllocPolicy::free_ to release
+** the memory returned.
+*/
+template<typename AllocPolicy = mozilla::MallocAllocPolicy>
+MOZ_FORMAT_PRINTF(1, 2)
+char* Smprintf(const char* fmt, ...)
+{
+ SprintfState<AllocPolicy> ss(nullptr);
+ va_list ap;
+ va_start(ap, fmt);
+ bool r = ss.vprint(fmt, ap);
+ va_end(ap);
+ if (!r) {
+ return nullptr;
+ }
+ return ss.release();
+}
+
+/*
+** "append" sprintf into a malloc'd buffer. "last" is the last value of
+** the malloc'd buffer. sprintf will append data to the end of last,
+** growing it as necessary using realloc. If last is nullptr, SmprintfAppend
+** will allocate the initial string. The return value is the new value of
+** last for subsequent calls, or nullptr if there is a malloc failure.
+*/
+template<typename AllocPolicy = mozilla::MallocAllocPolicy>
+MOZ_FORMAT_PRINTF(2, 3)
+char* SmprintfAppend(char* last, const char* fmt, ...)
+{
+ SprintfState<AllocPolicy> ss(last);
+ va_list ap;
+ va_start(ap, fmt);
+ bool r = ss.vprint(fmt, ap);
+ va_end(ap);
+ if (!r) {
+ return nullptr;
+ }
+ return ss.release();
+}
+
+/*
+** va_list forms of the above.
+*/
+template<typename AllocPolicy = mozilla::MallocAllocPolicy>
+char* Vsmprintf(const char* fmt, va_list ap)
+{
+ SprintfState<AllocPolicy> ss(nullptr);
+ if (!ss.vprint(fmt, ap))
+ return nullptr;
+ return ss.release();
+}
+
+template<typename AllocPolicy = mozilla::MallocAllocPolicy>
+char* VsmprintfAppend(char* last, const char* fmt, va_list ap)
+{
+ SprintfState<AllocPolicy> ss(last);
+ if (!ss.vprint(fmt, ap))
+ return nullptr;
+ return ss.release();
+}
+
+/*
+** Free the memory allocated, for the caller, by Smprintf.
+*/
+template<typename AllocPolicy = mozilla::MallocAllocPolicy>
+void SmprintfFree(char* mem)
+{
+ AllocPolicy allocator;
+ allocator.free_(mem);
+}
+
+} // namespace mozilla
+
+#endif /* mozilla_Printf_h */
--- a/mozglue/misc/moz.build
+++ b/mozglue/misc/moz.build
@@ -4,27 +4,29 @@
# 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/.
FINAL_LIBRARY = 'mozglue'
EXPORTS.mozilla += [
'PlatformConditionVariable.h',
'PlatformMutex.h',
+ 'Printf.h',
'StackWalk.h',
'TimeStamp.h',
]
if CONFIG['OS_ARCH'] == 'WINNT':
EXPORTS.mozilla += [
'StackWalk_windows.h',
'TimeStamp_windows.h',
]
SOURCES += [
+ 'Printf.cpp',
'StackWalk.cpp',
'TimeStamp.cpp',
]
OS_LIBS += CONFIG['REALTIME_LIBS']
DEFINES['IMPL_MFBT'] = True