1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsprf.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1157 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * Portable safe sprintf code. 1.12 + * 1.13 + * Author: Kipp E.B. Hickman 1.14 + */ 1.15 + 1.16 +#include "jsprf.h" 1.17 + 1.18 +#include <stdarg.h> 1.19 +#include <stdio.h> 1.20 +#include <stdlib.h> 1.21 +#include <string.h> 1.22 + 1.23 +#include "jspubtd.h" 1.24 +#include "jsstr.h" 1.25 +#include "jsutil.h" 1.26 + 1.27 +using namespace js; 1.28 + 1.29 +/* 1.30 + * Note: on some platforms va_list is defined as an array, 1.31 + * and requires array notation. 1.32 + */ 1.33 +#ifdef HAVE_VA_COPY 1.34 +#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo, bar) 1.35 +#elif defined(HAVE_VA_LIST_AS_ARRAY) 1.36 +#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] 1.37 +#else 1.38 +#define VARARGS_ASSIGN(foo, bar) (foo) = (bar) 1.39 +#endif 1.40 + 1.41 +struct SprintfState 1.42 +{ 1.43 + int (*stuff)(SprintfState *ss, const char *sp, size_t len); 1.44 + 1.45 + char *base; 1.46 + char *cur; 1.47 + size_t maxlen; 1.48 + 1.49 + int (*func)(void *arg, const char *sp, uint32_t len); 1.50 + void *arg; 1.51 +}; 1.52 + 1.53 +/* 1.54 + * Numbered Argument State 1.55 + */ 1.56 +struct NumArgState 1.57 +{ 1.58 + int type; // type of the current ap 1.59 + va_list ap; // point to the corresponding position on ap 1.60 +}; 1.61 + 1.62 +const size_t NAS_DEFAULT_NUM = 20; // default number of NumberedArgumentState array 1.63 + 1.64 + 1.65 +#define TYPE_INT16 0 1.66 +#define TYPE_UINT16 1 1.67 +#define TYPE_INTN 2 1.68 +#define TYPE_UINTN 3 1.69 +#define TYPE_INT32 4 1.70 +#define TYPE_UINT32 5 1.71 +#define TYPE_INT64 6 1.72 +#define TYPE_UINT64 7 1.73 +#define TYPE_STRING 8 1.74 +#define TYPE_DOUBLE 9 1.75 +#define TYPE_INTSTR 10 1.76 +#define TYPE_WSTRING 11 1.77 +#define TYPE_UNKNOWN 20 1.78 + 1.79 +#define FLAG_LEFT 0x1 1.80 +#define FLAG_SIGNED 0x2 1.81 +#define FLAG_SPACED 0x4 1.82 +#define FLAG_ZEROS 0x8 1.83 +#define FLAG_NEG 0x10 1.84 + 1.85 +inline int 1.86 +generic_write(SprintfState *ss, const char *src, size_t srclen) 1.87 +{ 1.88 + return (*ss->stuff)(ss, src, srclen); 1.89 +} 1.90 + 1.91 +inline int 1.92 +generic_write(SprintfState *ss, const jschar *src, size_t srclen) 1.93 +{ 1.94 + const size_t CHUNK_SIZE = 64; 1.95 + char chunk[CHUNK_SIZE]; 1.96 + 1.97 + int rv = 0; 1.98 + size_t j = 0; 1.99 + size_t i = 0; 1.100 + while (i < srclen) { 1.101 + // FIXME: truncates characters to 8 bits 1.102 + chunk[j++] = char(src[i++]); 1.103 + 1.104 + if (j == CHUNK_SIZE || i == srclen) { 1.105 + rv = (*ss->stuff)(ss, chunk, j); 1.106 + if (rv != 0) 1.107 + return rv; 1.108 + j = 0; 1.109 + } 1.110 + } 1.111 + return 0; 1.112 +} 1.113 + 1.114 +// Fill into the buffer using the data in src 1.115 +template <typename Char> 1.116 +static int 1.117 +fill2(SprintfState *ss, const Char *src, int srclen, int width, int flags) 1.118 +{ 1.119 + char space = ' '; 1.120 + int rv; 1.121 + 1.122 + width -= srclen; 1.123 + if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting 1.124 + if (flags & FLAG_ZEROS) 1.125 + space = '0'; 1.126 + while (--width >= 0) { 1.127 + rv = (*ss->stuff)(ss, &space, 1); 1.128 + if (rv < 0) 1.129 + return rv; 1.130 + } 1.131 + } 1.132 + 1.133 + // Copy out the source data 1.134 + rv = generic_write(ss, src, srclen); 1.135 + if (rv < 0) 1.136 + return rv; 1.137 + 1.138 + if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting 1.139 + while (--width >= 0) { 1.140 + rv = (*ss->stuff)(ss, &space, 1); 1.141 + if (rv < 0) 1.142 + return rv; 1.143 + } 1.144 + } 1.145 + return 0; 1.146 +} 1.147 + 1.148 +/* 1.149 + * Fill a number. The order is: optional-sign zero-filling conversion-digits 1.150 + */ 1.151 +static int 1.152 +fill_n(SprintfState *ss, const char *src, int srclen, int width, int prec, int type, int flags) 1.153 +{ 1.154 + int zerowidth = 0; 1.155 + int precwidth = 0; 1.156 + int signwidth = 0; 1.157 + int leftspaces = 0; 1.158 + int rightspaces = 0; 1.159 + int cvtwidth; 1.160 + int rv; 1.161 + char sign; 1.162 + 1.163 + if ((type & 1) == 0) { 1.164 + if (flags & FLAG_NEG) { 1.165 + sign = '-'; 1.166 + signwidth = 1; 1.167 + } else if (flags & FLAG_SIGNED) { 1.168 + sign = '+'; 1.169 + signwidth = 1; 1.170 + } else if (flags & FLAG_SPACED) { 1.171 + sign = ' '; 1.172 + signwidth = 1; 1.173 + } 1.174 + } 1.175 + cvtwidth = signwidth + srclen; 1.176 + 1.177 + if (prec > 0) { 1.178 + if (prec > srclen) { 1.179 + precwidth = prec - srclen; // Need zero filling 1.180 + cvtwidth += precwidth; 1.181 + } 1.182 + } 1.183 + 1.184 + if ((flags & FLAG_ZEROS) && (prec < 0)) { 1.185 + if (width > cvtwidth) { 1.186 + zerowidth = width - cvtwidth; // Zero filling 1.187 + cvtwidth += zerowidth; 1.188 + } 1.189 + } 1.190 + 1.191 + if (flags & FLAG_LEFT) { 1.192 + if (width > cvtwidth) { 1.193 + // Space filling on the right (i.e. left adjusting) 1.194 + rightspaces = width - cvtwidth; 1.195 + } 1.196 + } else { 1.197 + if (width > cvtwidth) { 1.198 + // Space filling on the left (i.e. right adjusting) 1.199 + leftspaces = width - cvtwidth; 1.200 + } 1.201 + } 1.202 + while (--leftspaces >= 0) { 1.203 + rv = (*ss->stuff)(ss, " ", 1); 1.204 + if (rv < 0) { 1.205 + return rv; 1.206 + } 1.207 + } 1.208 + if (signwidth) { 1.209 + rv = (*ss->stuff)(ss, &sign, 1); 1.210 + if (rv < 0) { 1.211 + return rv; 1.212 + } 1.213 + } 1.214 + while (--precwidth >= 0) { 1.215 + rv = (*ss->stuff)(ss, "0", 1); 1.216 + if (rv < 0) { 1.217 + return rv; 1.218 + } 1.219 + } 1.220 + while (--zerowidth >= 0) { 1.221 + rv = (*ss->stuff)(ss, "0", 1); 1.222 + if (rv < 0) { 1.223 + return rv; 1.224 + } 1.225 + } 1.226 + rv = (*ss->stuff)(ss, src, uint32_t(srclen)); 1.227 + if (rv < 0) { 1.228 + return rv; 1.229 + } 1.230 + while (--rightspaces >= 0) { 1.231 + rv = (*ss->stuff)(ss, " ", 1); 1.232 + if (rv < 0) { 1.233 + return rv; 1.234 + } 1.235 + } 1.236 + return 0; 1.237 +} 1.238 + 1.239 +/* Convert a long into its printable form. */ 1.240 +static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, 1.241 + int type, int flags, const char *hexp) 1.242 +{ 1.243 + char cvtbuf[100]; 1.244 + char *cvt; 1.245 + int digits; 1.246 + 1.247 + // according to the man page this needs to happen 1.248 + if ((prec == 0) && (num == 0)) { 1.249 + return 0; 1.250 + } 1.251 + 1.252 + // Converting decimal is a little tricky. In the unsigned case we 1.253 + // need to stop when we hit 10 digits. In the signed case, we can 1.254 + // stop when the number is zero. 1.255 + cvt = cvtbuf + sizeof(cvtbuf); 1.256 + digits = 0; 1.257 + while (num) { 1.258 + int digit = (((unsigned long)num) % radix) & 0xF; 1.259 + *--cvt = hexp[digit]; 1.260 + digits++; 1.261 + num = (long)(((unsigned long)num) / radix); 1.262 + } 1.263 + if (digits == 0) { 1.264 + *--cvt = '0'; 1.265 + digits++; 1.266 + } 1.267 + 1.268 + // Now that we have the number converted without its sign, deal with 1.269 + // the sign and zero padding. 1.270 + return fill_n(ss, cvt, digits, width, prec, type, flags); 1.271 +} 1.272 + 1.273 +/* Convert a 64-bit integer into its printable form. */ 1.274 +static int cvt_ll(SprintfState *ss, int64_t num, int width, int prec, int radix, 1.275 + int type, int flags, const char *hexp) 1.276 +{ 1.277 + // According to the man page, this needs to happen. 1.278 + if (prec == 0 && num == 0) 1.279 + return 0; 1.280 + 1.281 + // Converting decimal is a little tricky. In the unsigned case we 1.282 + // need to stop when we hit 10 digits. In the signed case, we can 1.283 + // stop when the number is zero. 1.284 + int64_t rad = int64_t(radix); 1.285 + char cvtbuf[100]; 1.286 + char *cvt = cvtbuf + sizeof(cvtbuf); 1.287 + int digits = 0; 1.288 + while (num != 0) { 1.289 + int64_t quot = uint64_t(num) / rad; 1.290 + int64_t rem = uint64_t(num) % rad; 1.291 + int32_t digit = int32_t(rem); 1.292 + *--cvt = hexp[digit & 0xf]; 1.293 + digits++; 1.294 + num = quot; 1.295 + } 1.296 + if (digits == 0) { 1.297 + *--cvt = '0'; 1.298 + digits++; 1.299 + } 1.300 + 1.301 + // Now that we have the number converted without its sign, deal with 1.302 + // the sign and zero padding. 1.303 + return fill_n(ss, cvt, digits, width, prec, type, flags); 1.304 +} 1.305 + 1.306 +/* 1.307 + * Convert a double precision floating point number into its printable 1.308 + * form. 1.309 + * 1.310 + * XXX stop using sprintf to convert floating point 1.311 + */ 1.312 +static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) 1.313 +{ 1.314 + char fin[20]; 1.315 + char fout[300]; 1.316 + int amount = fmt1 - fmt0; 1.317 + 1.318 + JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin))); 1.319 + if (amount >= (int)sizeof(fin)) { 1.320 + // Totally bogus % command to sprintf. Just ignore it 1.321 + return 0; 1.322 + } 1.323 + js_memcpy(fin, fmt0, (size_t)amount); 1.324 + fin[amount] = 0; 1.325 + 1.326 + // Convert floating point using the native sprintf code 1.327 +#ifdef DEBUG 1.328 + { 1.329 + const char *p = fin; 1.330 + while (*p) { 1.331 + JS_ASSERT(*p != 'L'); 1.332 + p++; 1.333 + } 1.334 + } 1.335 +#endif 1.336 + sprintf(fout, fin, d); 1.337 + 1.338 + // This assert will catch overflow's of fout, when building with 1.339 + // debugging on. At least this way we can track down the evil piece 1.340 + // of calling code and fix it! 1.341 + JS_ASSERT(strlen(fout) < sizeof(fout)); 1.342 + 1.343 + return (*ss->stuff)(ss, fout, strlen(fout)); 1.344 +} 1.345 + 1.346 +static inline const char *generic_null_str(const char *) { return "(null)"; } 1.347 +static inline const jschar *generic_null_str(const jschar *) { return MOZ_UTF16("(null)"); } 1.348 + 1.349 +static inline size_t generic_strlen(const char *s) { return strlen(s); } 1.350 +static inline size_t generic_strlen(const jschar *s) { return js_strlen(s); } 1.351 + 1.352 +/* 1.353 + * Convert a string into its printable form. "width" is the output 1.354 + * width. "prec" is the maximum number of characters of "s" to output, 1.355 + * where -1 means until NUL. 1.356 + */ 1.357 +template <typename Char> 1.358 +static int 1.359 +cvt_s(SprintfState *ss, const Char *s, int width, int prec, int flags) 1.360 +{ 1.361 + if (prec == 0) 1.362 + return 0; 1.363 + if (!s) 1.364 + s = generic_null_str(s); 1.365 + 1.366 + // Limit string length by precision value 1.367 + int slen = int(generic_strlen(s)); 1.368 + if (0 < prec && prec < slen) 1.369 + slen = prec; 1.370 + 1.371 + // and away we go 1.372 + return fill2(ss, s, slen, width, flags); 1.373 +} 1.374 + 1.375 +/* 1.376 + * BuildArgArray stands for Numbered Argument list Sprintf 1.377 + * for example, 1.378 + * fmp = "%4$i, %2$d, %3s, %1d"; 1.379 + * the number must start from 1, and no gap among them 1.380 + */ 1.381 +static NumArgState * 1.382 +BuildArgArray(const char *fmt, va_list ap, int *rv, NumArgState *nasArray) 1.383 +{ 1.384 + size_t number = 0, cn = 0, i; 1.385 + const char *p; 1.386 + char c; 1.387 + NumArgState *nas; 1.388 + 1.389 + 1.390 + // First pass: 1.391 + // Detemine how many legal % I have got, then allocate space. 1.392 + 1.393 + p = fmt; 1.394 + *rv = 0; 1.395 + i = 0; 1.396 + while ((c = *p++) != 0) { 1.397 + if (c != '%') 1.398 + continue; 1.399 + if ((c = *p++) == '%') // skip %% case 1.400 + continue; 1.401 + 1.402 + while (c != 0) { 1.403 + if (c > '9' || c < '0') { 1.404 + if (c == '$') { // numbered argument case 1.405 + if (i > 0) { 1.406 + *rv = -1; 1.407 + return nullptr; 1.408 + } 1.409 + number++; 1.410 + } else { // non-numbered argument case 1.411 + if (number > 0) { 1.412 + *rv = -1; 1.413 + return nullptr; 1.414 + } 1.415 + i = 1; 1.416 + } 1.417 + break; 1.418 + } 1.419 + 1.420 + c = *p++; 1.421 + } 1.422 + } 1.423 + 1.424 + if (number == 0) 1.425 + return nullptr; 1.426 + 1.427 + if (number > NAS_DEFAULT_NUM) { 1.428 + nas = (NumArgState *) js_malloc(number * sizeof(NumArgState)); 1.429 + if (!nas) { 1.430 + *rv = -1; 1.431 + return nullptr; 1.432 + } 1.433 + } else { 1.434 + nas = nasArray; 1.435 + } 1.436 + 1.437 + for (i = 0; i < number; i++) 1.438 + nas[i].type = TYPE_UNKNOWN; 1.439 + 1.440 + 1.441 + // Second pass: 1.442 + // Set nas[].type. 1.443 + 1.444 + p = fmt; 1.445 + while ((c = *p++) != 0) { 1.446 + if (c != '%') 1.447 + continue; 1.448 + c = *p++; 1.449 + if (c == '%') 1.450 + continue; 1.451 + 1.452 + cn = 0; 1.453 + while (c && c != '$') { // should improve error check later 1.454 + cn = cn*10 + c - '0'; 1.455 + c = *p++; 1.456 + } 1.457 + 1.458 + if (!c || cn < 1 || cn > number) { 1.459 + *rv = -1; 1.460 + break; 1.461 + } 1.462 + 1.463 + // nas[cn] starts from 0, and make sure nas[cn].type is not assigned. 1.464 + cn--; 1.465 + if (nas[cn].type != TYPE_UNKNOWN) 1.466 + continue; 1.467 + 1.468 + c = *p++; 1.469 + 1.470 + // width 1.471 + if (c == '*') { 1.472 + // not supported feature, for the argument is not numbered 1.473 + *rv = -1; 1.474 + break; 1.475 + } 1.476 + 1.477 + while ((c >= '0') && (c <= '9')) { 1.478 + c = *p++; 1.479 + } 1.480 + 1.481 + // precision 1.482 + if (c == '.') { 1.483 + c = *p++; 1.484 + if (c == '*') { 1.485 + // not supported feature, for the argument is not numbered 1.486 + *rv = -1; 1.487 + break; 1.488 + } 1.489 + 1.490 + while ((c >= '0') && (c <= '9')) { 1.491 + c = *p++; 1.492 + } 1.493 + } 1.494 + 1.495 + // size 1.496 + nas[cn].type = TYPE_INTN; 1.497 + if (c == 'h') { 1.498 + nas[cn].type = TYPE_INT16; 1.499 + c = *p++; 1.500 + } else if (c == 'L') { 1.501 + // XXX not quite sure here 1.502 + nas[cn].type = TYPE_INT64; 1.503 + c = *p++; 1.504 + } else if (c == 'l') { 1.505 + nas[cn].type = TYPE_INT32; 1.506 + c = *p++; 1.507 + if (c == 'l') { 1.508 + nas[cn].type = TYPE_INT64; 1.509 + c = *p++; 1.510 + } 1.511 + } 1.512 + 1.513 + // format 1.514 + switch (c) { 1.515 + case 'd': 1.516 + case 'c': 1.517 + case 'i': 1.518 + case 'o': 1.519 + case 'u': 1.520 + case 'x': 1.521 + case 'X': 1.522 + break; 1.523 + 1.524 + case 'e': 1.525 + case 'f': 1.526 + case 'g': 1.527 + nas[cn].type = TYPE_DOUBLE; 1.528 + break; 1.529 + 1.530 + case 'p': 1.531 + // XXX should use cpp 1.532 + if (sizeof(void *) == sizeof(int32_t)) { 1.533 + nas[cn].type = TYPE_UINT32; 1.534 + } else if (sizeof(void *) == sizeof(int64_t)) { 1.535 + nas[cn].type = TYPE_UINT64; 1.536 + } else if (sizeof(void *) == sizeof(int)) { 1.537 + nas[cn].type = TYPE_UINTN; 1.538 + } else { 1.539 + nas[cn].type = TYPE_UNKNOWN; 1.540 + } 1.541 + break; 1.542 + 1.543 + case 'C': 1.544 + case 'S': 1.545 + case 'E': 1.546 + case 'G': 1.547 + // XXX not supported I suppose 1.548 + JS_ASSERT(0); 1.549 + nas[cn].type = TYPE_UNKNOWN; 1.550 + break; 1.551 + 1.552 + case 's': 1.553 + nas[cn].type = (nas[cn].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING; 1.554 + break; 1.555 + 1.556 + case 'n': 1.557 + nas[cn].type = TYPE_INTSTR; 1.558 + break; 1.559 + 1.560 + default: 1.561 + JS_ASSERT(0); 1.562 + nas[cn].type = TYPE_UNKNOWN; 1.563 + break; 1.564 + } 1.565 + 1.566 + // get a legal para. 1.567 + if (nas[cn].type == TYPE_UNKNOWN) { 1.568 + *rv = -1; 1.569 + break; 1.570 + } 1.571 + } 1.572 + 1.573 + 1.574 + // Third pass: 1.575 + // Fill nas[].ap. 1.576 + 1.577 + if (*rv < 0) { 1.578 + if (nas != nasArray) 1.579 + js_free(nas); 1.580 + return nullptr; 1.581 + } 1.582 + 1.583 + cn = 0; 1.584 + while (cn < number) { 1.585 + if (nas[cn].type == TYPE_UNKNOWN) { 1.586 + cn++; 1.587 + continue; 1.588 + } 1.589 + 1.590 + VARARGS_ASSIGN(nas[cn].ap, ap); 1.591 + 1.592 + switch (nas[cn].type) { 1.593 + case TYPE_INT16: 1.594 + case TYPE_UINT16: 1.595 + case TYPE_INTN: 1.596 + case TYPE_UINTN: (void) va_arg(ap, int); break; 1.597 + case TYPE_INT32: (void) va_arg(ap, int32_t); break; 1.598 + case TYPE_UINT32: (void) va_arg(ap, uint32_t); break; 1.599 + case TYPE_INT64: (void) va_arg(ap, int64_t); break; 1.600 + case TYPE_UINT64: (void) va_arg(ap, uint64_t); break; 1.601 + case TYPE_STRING: (void) va_arg(ap, char*); break; 1.602 + case TYPE_WSTRING: (void) va_arg(ap, jschar*); break; 1.603 + case TYPE_INTSTR: (void) va_arg(ap, int*); break; 1.604 + case TYPE_DOUBLE: (void) va_arg(ap, double); break; 1.605 + 1.606 + default: 1.607 + if (nas != nasArray) 1.608 + js_free(nas); 1.609 + *rv = -1; 1.610 + return nullptr; 1.611 + } 1.612 + 1.613 + cn++; 1.614 + } 1.615 + 1.616 + 1.617 + return nas; 1.618 +} 1.619 + 1.620 +/* 1.621 + * The workhorse sprintf code. 1.622 + */ 1.623 +static int 1.624 +dosprintf(SprintfState *ss, const char *fmt, va_list ap) 1.625 +{ 1.626 + char c; 1.627 + int flags, width, prec, radix, type; 1.628 + union { 1.629 + char ch; 1.630 + jschar wch; 1.631 + int i; 1.632 + long l; 1.633 + int64_t ll; 1.634 + double d; 1.635 + const char *s; 1.636 + const jschar* ws; 1.637 + int *ip; 1.638 + } u; 1.639 + const char *fmt0; 1.640 + static const char hex[] = "0123456789abcdef"; 1.641 + static const char HEX[] = "0123456789ABCDEF"; 1.642 + const char *hexp; 1.643 + int rv, i; 1.644 + NumArgState *nas = nullptr; 1.645 + NumArgState nasArray[NAS_DEFAULT_NUM]; 1.646 + char pattern[20]; 1.647 + const char *dolPt = nullptr; // in "%4$.2f", dolPt will point to '.' 1.648 + 1.649 + // Build an argument array, IF the fmt is numbered argument 1.650 + // list style, to contain the Numbered Argument list pointers. 1.651 + 1.652 + nas = BuildArgArray(fmt, ap, &rv, nasArray); 1.653 + if (rv < 0) { 1.654 + // the fmt contains error Numbered Argument format, jliu@netscape.com 1.655 + JS_ASSERT(0); 1.656 + return rv; 1.657 + } 1.658 + 1.659 + while ((c = *fmt++) != 0) { 1.660 + if (c != '%') { 1.661 + rv = (*ss->stuff)(ss, fmt - 1, 1); 1.662 + if (rv < 0) { 1.663 + return rv; 1.664 + } 1.665 + continue; 1.666 + } 1.667 + fmt0 = fmt - 1; 1.668 + 1.669 + // Gobble up the % format string. Hopefully we have handled all 1.670 + // of the strange cases! 1.671 + flags = 0; 1.672 + c = *fmt++; 1.673 + if (c == '%') { 1.674 + // quoting a % with %% 1.675 + rv = (*ss->stuff)(ss, fmt - 1, 1); 1.676 + if (rv < 0) { 1.677 + return rv; 1.678 + } 1.679 + continue; 1.680 + } 1.681 + 1.682 + if (nas != nullptr) { 1.683 + // the fmt contains the Numbered Arguments feature 1.684 + i = 0; 1.685 + while (c && c != '$') { // should improve error check later 1.686 + i = (i * 10) + (c - '0'); 1.687 + c = *fmt++; 1.688 + } 1.689 + 1.690 + if (nas[i-1].type == TYPE_UNKNOWN) { 1.691 + if (nas && nas != nasArray) 1.692 + js_free(nas); 1.693 + return -1; 1.694 + } 1.695 + 1.696 + ap = nas[i-1].ap; 1.697 + dolPt = fmt; 1.698 + c = *fmt++; 1.699 + } 1.700 + 1.701 + // Examine optional flags. Note that we do not implement the 1.702 + // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is 1.703 + // somewhat ambiguous and not ideal, which is perhaps why 1.704 + // the various sprintf() implementations are inconsistent 1.705 + // on this feature. 1.706 + while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { 1.707 + if (c == '-') flags |= FLAG_LEFT; 1.708 + if (c == '+') flags |= FLAG_SIGNED; 1.709 + if (c == ' ') flags |= FLAG_SPACED; 1.710 + if (c == '0') flags |= FLAG_ZEROS; 1.711 + c = *fmt++; 1.712 + } 1.713 + if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED; 1.714 + if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS; 1.715 + 1.716 + // width 1.717 + if (c == '*') { 1.718 + c = *fmt++; 1.719 + width = va_arg(ap, int); 1.720 + } else { 1.721 + width = 0; 1.722 + while ((c >= '0') && (c <= '9')) { 1.723 + width = (width * 10) + (c - '0'); 1.724 + c = *fmt++; 1.725 + } 1.726 + } 1.727 + 1.728 + // precision 1.729 + prec = -1; 1.730 + if (c == '.') { 1.731 + c = *fmt++; 1.732 + if (c == '*') { 1.733 + c = *fmt++; 1.734 + prec = va_arg(ap, int); 1.735 + } else { 1.736 + prec = 0; 1.737 + while ((c >= '0') && (c <= '9')) { 1.738 + prec = (prec * 10) + (c - '0'); 1.739 + c = *fmt++; 1.740 + } 1.741 + } 1.742 + } 1.743 + 1.744 + // size 1.745 + type = TYPE_INTN; 1.746 + if (c == 'h') { 1.747 + type = TYPE_INT16; 1.748 + c = *fmt++; 1.749 + } else if (c == 'L') { 1.750 + // XXX not quite sure here 1.751 + type = TYPE_INT64; 1.752 + c = *fmt++; 1.753 + } else if (c == 'l') { 1.754 + type = TYPE_INT32; 1.755 + c = *fmt++; 1.756 + if (c == 'l') { 1.757 + type = TYPE_INT64; 1.758 + c = *fmt++; 1.759 + } 1.760 + } 1.761 + 1.762 + // format 1.763 + hexp = hex; 1.764 + switch (c) { 1.765 + case 'd': case 'i': // decimal/integer 1.766 + radix = 10; 1.767 + goto fetch_and_convert; 1.768 + 1.769 + case 'o': // octal 1.770 + radix = 8; 1.771 + type |= 1; 1.772 + goto fetch_and_convert; 1.773 + 1.774 + case 'u': // unsigned decimal 1.775 + radix = 10; 1.776 + type |= 1; 1.777 + goto fetch_and_convert; 1.778 + 1.779 + case 'x': // unsigned hex 1.780 + radix = 16; 1.781 + type |= 1; 1.782 + goto fetch_and_convert; 1.783 + 1.784 + case 'X': // unsigned HEX 1.785 + radix = 16; 1.786 + hexp = HEX; 1.787 + type |= 1; 1.788 + goto fetch_and_convert; 1.789 + 1.790 + fetch_and_convert: 1.791 + switch (type) { 1.792 + case TYPE_INT16: 1.793 + u.l = va_arg(ap, int); 1.794 + if (u.l < 0) { 1.795 + u.l = -u.l; 1.796 + flags |= FLAG_NEG; 1.797 + } 1.798 + goto do_long; 1.799 + case TYPE_UINT16: 1.800 + u.l = va_arg(ap, int) & 0xffff; 1.801 + goto do_long; 1.802 + case TYPE_INTN: 1.803 + u.l = va_arg(ap, int); 1.804 + if (u.l < 0) { 1.805 + u.l = -u.l; 1.806 + flags |= FLAG_NEG; 1.807 + } 1.808 + goto do_long; 1.809 + case TYPE_UINTN: 1.810 + u.l = (long)va_arg(ap, unsigned int); 1.811 + goto do_long; 1.812 + 1.813 + case TYPE_INT32: 1.814 + u.l = va_arg(ap, int32_t); 1.815 + if (u.l < 0) { 1.816 + u.l = -u.l; 1.817 + flags |= FLAG_NEG; 1.818 + } 1.819 + goto do_long; 1.820 + case TYPE_UINT32: 1.821 + u.l = (long)va_arg(ap, uint32_t); 1.822 + do_long: 1.823 + rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); 1.824 + if (rv < 0) { 1.825 + return rv; 1.826 + } 1.827 + break; 1.828 + 1.829 + case TYPE_INT64: 1.830 + u.ll = va_arg(ap, int64_t); 1.831 + if (u.ll < 0) { 1.832 + u.ll = -u.ll; 1.833 + flags |= FLAG_NEG; 1.834 + } 1.835 + goto do_longlong; 1.836 + case TYPE_UINT64: 1.837 + u.ll = va_arg(ap, uint64_t); 1.838 + do_longlong: 1.839 + rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); 1.840 + if (rv < 0) { 1.841 + return rv; 1.842 + } 1.843 + break; 1.844 + } 1.845 + break; 1.846 + 1.847 + case 'e': 1.848 + case 'E': 1.849 + case 'f': 1.850 + case 'g': 1.851 + u.d = va_arg(ap, double); 1.852 + if (nas != nullptr) { 1.853 + i = fmt - dolPt; 1.854 + if (i < int(sizeof(pattern))) { 1.855 + pattern[0] = '%'; 1.856 + js_memcpy(&pattern[1], dolPt, size_t(i)); 1.857 + rv = cvt_f(ss, u.d, pattern, &pattern[i + 1]); 1.858 + } 1.859 + } else 1.860 + rv = cvt_f(ss, u.d, fmt0, fmt); 1.861 + 1.862 + if (rv < 0) { 1.863 + return rv; 1.864 + } 1.865 + break; 1.866 + 1.867 + case 'c': 1.868 + if ((flags & FLAG_LEFT) == 0) { 1.869 + while (width-- > 1) { 1.870 + rv = (*ss->stuff)(ss, " ", 1); 1.871 + if (rv < 0) { 1.872 + return rv; 1.873 + } 1.874 + } 1.875 + } 1.876 + switch (type) { 1.877 + case TYPE_INT16: 1.878 + case TYPE_INTN: 1.879 + u.ch = va_arg(ap, int); 1.880 + rv = (*ss->stuff)(ss, &u.ch, 1); 1.881 + break; 1.882 + } 1.883 + if (rv < 0) { 1.884 + return rv; 1.885 + } 1.886 + if (flags & FLAG_LEFT) { 1.887 + while (width-- > 1) { 1.888 + rv = (*ss->stuff)(ss, " ", 1); 1.889 + if (rv < 0) { 1.890 + return rv; 1.891 + } 1.892 + } 1.893 + } 1.894 + break; 1.895 + 1.896 + case 'p': 1.897 + if (sizeof(void *) == sizeof(int32_t)) { 1.898 + type = TYPE_UINT32; 1.899 + } else if (sizeof(void *) == sizeof(int64_t)) { 1.900 + type = TYPE_UINT64; 1.901 + } else if (sizeof(void *) == sizeof(int)) { 1.902 + type = TYPE_UINTN; 1.903 + } else { 1.904 + JS_ASSERT(0); 1.905 + break; 1.906 + } 1.907 + radix = 16; 1.908 + goto fetch_and_convert; 1.909 + 1.910 +#if 0 1.911 + case 'C': 1.912 + case 'S': 1.913 + case 'E': 1.914 + case 'G': 1.915 + // XXX not supported I suppose 1.916 + JS_ASSERT(0); 1.917 + break; 1.918 +#endif 1.919 + 1.920 + case 's': 1.921 + if(type == TYPE_INT16) { 1.922 + u.ws = va_arg(ap, const jschar*); 1.923 + rv = cvt_s(ss, u.ws, width, prec, flags); 1.924 + } else { 1.925 + u.s = va_arg(ap, const char*); 1.926 + rv = cvt_s(ss, u.s, width, prec, flags); 1.927 + } 1.928 + if (rv < 0) { 1.929 + return rv; 1.930 + } 1.931 + break; 1.932 + 1.933 + case 'n': 1.934 + u.ip = va_arg(ap, int*); 1.935 + if (u.ip) { 1.936 + *u.ip = ss->cur - ss->base; 1.937 + } 1.938 + break; 1.939 + 1.940 + default: 1.941 + // Not a % token after all... skip it 1.942 +#if 0 1.943 + JS_ASSERT(0); 1.944 +#endif 1.945 + rv = (*ss->stuff)(ss, "%", 1); 1.946 + if (rv < 0) { 1.947 + return rv; 1.948 + } 1.949 + rv = (*ss->stuff)(ss, fmt - 1, 1); 1.950 + if (rv < 0) { 1.951 + return rv; 1.952 + } 1.953 + } 1.954 + } 1.955 + 1.956 + // Stuff trailing NUL 1.957 + rv = (*ss->stuff)(ss, "\0", 1); 1.958 + 1.959 + if (nas && nas != nasArray) 1.960 + js_free(nas); 1.961 + 1.962 + return rv; 1.963 +} 1.964 + 1.965 +/************************************************************************/ 1.966 + 1.967 +/* 1.968 + * Stuff routine that automatically grows the js_malloc'd output buffer 1.969 + * before it overflows. 1.970 + */ 1.971 +static int 1.972 +GrowStuff(SprintfState *ss, const char *sp, size_t len) 1.973 +{ 1.974 + ptrdiff_t off; 1.975 + char *newbase; 1.976 + size_t newlen; 1.977 + 1.978 + off = ss->cur - ss->base; 1.979 + if (off + len >= ss->maxlen) { 1.980 + /* Grow the buffer */ 1.981 + newlen = ss->maxlen + ((len > 32) ? len : 32); 1.982 + newbase = static_cast<char *>(js_realloc(ss->base, newlen)); 1.983 + if (!newbase) { 1.984 + /* Ran out of memory */ 1.985 + return -1; 1.986 + } 1.987 + ss->base = newbase; 1.988 + ss->maxlen = newlen; 1.989 + ss->cur = ss->base + off; 1.990 + } 1.991 + 1.992 + /* Copy data */ 1.993 + while (len) { 1.994 + --len; 1.995 + *ss->cur++ = *sp++; 1.996 + } 1.997 + MOZ_ASSERT(size_t(ss->cur - ss->base) <= ss->maxlen); 1.998 + return 0; 1.999 +} 1.1000 + 1.1001 +/* 1.1002 + * sprintf into a js_malloc'd buffer 1.1003 + */ 1.1004 +JS_PUBLIC_API(char *) 1.1005 +JS_smprintf(const char *fmt, ...) 1.1006 +{ 1.1007 + va_list ap; 1.1008 + char *rv; 1.1009 + 1.1010 + va_start(ap, fmt); 1.1011 + rv = JS_vsmprintf(fmt, ap); 1.1012 + va_end(ap); 1.1013 + return rv; 1.1014 +} 1.1015 + 1.1016 +/* 1.1017 + * Free memory allocated, for the caller, by JS_smprintf 1.1018 + */ 1.1019 +JS_PUBLIC_API(void) 1.1020 +JS_smprintf_free(char *mem) 1.1021 +{ 1.1022 + js_free(mem); 1.1023 +} 1.1024 + 1.1025 +JS_PUBLIC_API(char *) 1.1026 +JS_vsmprintf(const char *fmt, va_list ap) 1.1027 +{ 1.1028 + SprintfState ss; 1.1029 + int rv; 1.1030 + 1.1031 + ss.stuff = GrowStuff; 1.1032 + ss.base = 0; 1.1033 + ss.cur = 0; 1.1034 + ss.maxlen = 0; 1.1035 + rv = dosprintf(&ss, fmt, ap); 1.1036 + if (rv < 0) { 1.1037 + js_free(ss.base); 1.1038 + return 0; 1.1039 + } 1.1040 + return ss.base; 1.1041 +} 1.1042 + 1.1043 +/* 1.1044 + * Stuff routine that discards overflow data 1.1045 + */ 1.1046 +static int 1.1047 +LimitStuff(SprintfState *ss, const char *sp, size_t len) 1.1048 +{ 1.1049 + size_t limit = ss->maxlen - (ss->cur - ss->base); 1.1050 + 1.1051 + if (len > limit) 1.1052 + len = limit; 1.1053 + while (len) { 1.1054 + --len; 1.1055 + *ss->cur++ = *sp++; 1.1056 + } 1.1057 + return 0; 1.1058 +} 1.1059 + 1.1060 +/* 1.1061 + * sprintf into a fixed size buffer. Make sure there is a NUL at the end 1.1062 + * when finished. 1.1063 + */ 1.1064 +JS_PUBLIC_API(uint32_t) 1.1065 +JS_snprintf(char *out, uint32_t outlen, const char *fmt, ...) 1.1066 +{ 1.1067 + va_list ap; 1.1068 + int rv; 1.1069 + 1.1070 + JS_ASSERT(int32_t(outlen) > 0); 1.1071 + if (int32_t(outlen) <= 0) 1.1072 + return 0; 1.1073 + 1.1074 + va_start(ap, fmt); 1.1075 + rv = JS_vsnprintf(out, outlen, fmt, ap); 1.1076 + va_end(ap); 1.1077 + return rv; 1.1078 +} 1.1079 + 1.1080 +JS_PUBLIC_API(uint32_t) 1.1081 +JS_vsnprintf(char *out, uint32_t outlen, const char *fmt, va_list ap) 1.1082 +{ 1.1083 + SprintfState ss; 1.1084 + uint32_t n; 1.1085 + 1.1086 + JS_ASSERT(int32_t(outlen) > 0); 1.1087 + if (int32_t(outlen) <= 0) { 1.1088 + return 0; 1.1089 + } 1.1090 + 1.1091 + ss.stuff = LimitStuff; 1.1092 + ss.base = out; 1.1093 + ss.cur = out; 1.1094 + ss.maxlen = outlen; 1.1095 + (void) dosprintf(&ss, fmt, ap); 1.1096 + 1.1097 + /* If we added chars, and we didn't append a null, do it now. */ 1.1098 + if (ss.cur != ss.base && ss.cur[-1] != '\0') 1.1099 + ss.cur[-1] = '\0'; 1.1100 + 1.1101 + n = ss.cur - ss.base; 1.1102 + return n ? n - 1 : n; 1.1103 +} 1.1104 + 1.1105 +JS_PUBLIC_API(char *) 1.1106 +JS_sprintf_append(char *last, const char *fmt, ...) 1.1107 +{ 1.1108 + va_list ap; 1.1109 + char *rv; 1.1110 + 1.1111 + va_start(ap, fmt); 1.1112 + rv = JS_vsprintf_append(last, fmt, ap); 1.1113 + va_end(ap); 1.1114 + return rv; 1.1115 +} 1.1116 + 1.1117 +JS_PUBLIC_API(char *) 1.1118 +JS_vsprintf_append(char *last, const char *fmt, va_list ap) 1.1119 +{ 1.1120 + SprintfState ss; 1.1121 + int rv; 1.1122 + 1.1123 + ss.stuff = GrowStuff; 1.1124 + if (last) { 1.1125 + size_t lastlen = strlen(last); 1.1126 + ss.base = last; 1.1127 + ss.cur = last + lastlen; 1.1128 + ss.maxlen = lastlen; 1.1129 + } else { 1.1130 + ss.base = 0; 1.1131 + ss.cur = 0; 1.1132 + ss.maxlen = 0; 1.1133 + } 1.1134 + rv = dosprintf(&ss, fmt, ap); 1.1135 + if (rv < 0) { 1.1136 + js_free(ss.base); 1.1137 + return 0; 1.1138 + } 1.1139 + return ss.base; 1.1140 +} 1.1141 + 1.1142 +#undef TYPE_INT16 1.1143 +#undef TYPE_UINT16 1.1144 +#undef TYPE_INTN 1.1145 +#undef TYPE_UINTN 1.1146 +#undef TYPE_INT32 1.1147 +#undef TYPE_UINT32 1.1148 +#undef TYPE_INT64 1.1149 +#undef TYPE_UINT64 1.1150 +#undef TYPE_STRING 1.1151 +#undef TYPE_DOUBLE 1.1152 +#undef TYPE_INTSTR 1.1153 +#undef TYPE_WSTRING 1.1154 +#undef TYPE_UNKNOWN 1.1155 + 1.1156 +#undef FLAG_LEFT 1.1157 +#undef FLAG_SIGNED 1.1158 +#undef FLAG_SPACED 1.1159 +#undef FLAG_ZEROS 1.1160 +#undef FLAG_NEG