js/src/jsprf.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * Portable safe sprintf code.
michael@0 9 *
michael@0 10 * Author: Kipp E.B. Hickman
michael@0 11 */
michael@0 12
michael@0 13 #include "jsprf.h"
michael@0 14
michael@0 15 #include <stdarg.h>
michael@0 16 #include <stdio.h>
michael@0 17 #include <stdlib.h>
michael@0 18 #include <string.h>
michael@0 19
michael@0 20 #include "jspubtd.h"
michael@0 21 #include "jsstr.h"
michael@0 22 #include "jsutil.h"
michael@0 23
michael@0 24 using namespace js;
michael@0 25
michael@0 26 /*
michael@0 27 * Note: on some platforms va_list is defined as an array,
michael@0 28 * and requires array notation.
michael@0 29 */
michael@0 30 #ifdef HAVE_VA_COPY
michael@0 31 #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo, bar)
michael@0 32 #elif defined(HAVE_VA_LIST_AS_ARRAY)
michael@0 33 #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
michael@0 34 #else
michael@0 35 #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
michael@0 36 #endif
michael@0 37
michael@0 38 struct SprintfState
michael@0 39 {
michael@0 40 int (*stuff)(SprintfState *ss, const char *sp, size_t len);
michael@0 41
michael@0 42 char *base;
michael@0 43 char *cur;
michael@0 44 size_t maxlen;
michael@0 45
michael@0 46 int (*func)(void *arg, const char *sp, uint32_t len);
michael@0 47 void *arg;
michael@0 48 };
michael@0 49
michael@0 50 /*
michael@0 51 * Numbered Argument State
michael@0 52 */
michael@0 53 struct NumArgState
michael@0 54 {
michael@0 55 int type; // type of the current ap
michael@0 56 va_list ap; // point to the corresponding position on ap
michael@0 57 };
michael@0 58
michael@0 59 const size_t NAS_DEFAULT_NUM = 20; // default number of NumberedArgumentState array
michael@0 60
michael@0 61
michael@0 62 #define TYPE_INT16 0
michael@0 63 #define TYPE_UINT16 1
michael@0 64 #define TYPE_INTN 2
michael@0 65 #define TYPE_UINTN 3
michael@0 66 #define TYPE_INT32 4
michael@0 67 #define TYPE_UINT32 5
michael@0 68 #define TYPE_INT64 6
michael@0 69 #define TYPE_UINT64 7
michael@0 70 #define TYPE_STRING 8
michael@0 71 #define TYPE_DOUBLE 9
michael@0 72 #define TYPE_INTSTR 10
michael@0 73 #define TYPE_WSTRING 11
michael@0 74 #define TYPE_UNKNOWN 20
michael@0 75
michael@0 76 #define FLAG_LEFT 0x1
michael@0 77 #define FLAG_SIGNED 0x2
michael@0 78 #define FLAG_SPACED 0x4
michael@0 79 #define FLAG_ZEROS 0x8
michael@0 80 #define FLAG_NEG 0x10
michael@0 81
michael@0 82 inline int
michael@0 83 generic_write(SprintfState *ss, const char *src, size_t srclen)
michael@0 84 {
michael@0 85 return (*ss->stuff)(ss, src, srclen);
michael@0 86 }
michael@0 87
michael@0 88 inline int
michael@0 89 generic_write(SprintfState *ss, const jschar *src, size_t srclen)
michael@0 90 {
michael@0 91 const size_t CHUNK_SIZE = 64;
michael@0 92 char chunk[CHUNK_SIZE];
michael@0 93
michael@0 94 int rv = 0;
michael@0 95 size_t j = 0;
michael@0 96 size_t i = 0;
michael@0 97 while (i < srclen) {
michael@0 98 // FIXME: truncates characters to 8 bits
michael@0 99 chunk[j++] = char(src[i++]);
michael@0 100
michael@0 101 if (j == CHUNK_SIZE || i == srclen) {
michael@0 102 rv = (*ss->stuff)(ss, chunk, j);
michael@0 103 if (rv != 0)
michael@0 104 return rv;
michael@0 105 j = 0;
michael@0 106 }
michael@0 107 }
michael@0 108 return 0;
michael@0 109 }
michael@0 110
michael@0 111 // Fill into the buffer using the data in src
michael@0 112 template <typename Char>
michael@0 113 static int
michael@0 114 fill2(SprintfState *ss, const Char *src, int srclen, int width, int flags)
michael@0 115 {
michael@0 116 char space = ' ';
michael@0 117 int rv;
michael@0 118
michael@0 119 width -= srclen;
michael@0 120 if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting
michael@0 121 if (flags & FLAG_ZEROS)
michael@0 122 space = '0';
michael@0 123 while (--width >= 0) {
michael@0 124 rv = (*ss->stuff)(ss, &space, 1);
michael@0 125 if (rv < 0)
michael@0 126 return rv;
michael@0 127 }
michael@0 128 }
michael@0 129
michael@0 130 // Copy out the source data
michael@0 131 rv = generic_write(ss, src, srclen);
michael@0 132 if (rv < 0)
michael@0 133 return rv;
michael@0 134
michael@0 135 if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting
michael@0 136 while (--width >= 0) {
michael@0 137 rv = (*ss->stuff)(ss, &space, 1);
michael@0 138 if (rv < 0)
michael@0 139 return rv;
michael@0 140 }
michael@0 141 }
michael@0 142 return 0;
michael@0 143 }
michael@0 144
michael@0 145 /*
michael@0 146 * Fill a number. The order is: optional-sign zero-filling conversion-digits
michael@0 147 */
michael@0 148 static int
michael@0 149 fill_n(SprintfState *ss, const char *src, int srclen, int width, int prec, int type, int flags)
michael@0 150 {
michael@0 151 int zerowidth = 0;
michael@0 152 int precwidth = 0;
michael@0 153 int signwidth = 0;
michael@0 154 int leftspaces = 0;
michael@0 155 int rightspaces = 0;
michael@0 156 int cvtwidth;
michael@0 157 int rv;
michael@0 158 char sign;
michael@0 159
michael@0 160 if ((type & 1) == 0) {
michael@0 161 if (flags & FLAG_NEG) {
michael@0 162 sign = '-';
michael@0 163 signwidth = 1;
michael@0 164 } else if (flags & FLAG_SIGNED) {
michael@0 165 sign = '+';
michael@0 166 signwidth = 1;
michael@0 167 } else if (flags & FLAG_SPACED) {
michael@0 168 sign = ' ';
michael@0 169 signwidth = 1;
michael@0 170 }
michael@0 171 }
michael@0 172 cvtwidth = signwidth + srclen;
michael@0 173
michael@0 174 if (prec > 0) {
michael@0 175 if (prec > srclen) {
michael@0 176 precwidth = prec - srclen; // Need zero filling
michael@0 177 cvtwidth += precwidth;
michael@0 178 }
michael@0 179 }
michael@0 180
michael@0 181 if ((flags & FLAG_ZEROS) && (prec < 0)) {
michael@0 182 if (width > cvtwidth) {
michael@0 183 zerowidth = width - cvtwidth; // Zero filling
michael@0 184 cvtwidth += zerowidth;
michael@0 185 }
michael@0 186 }
michael@0 187
michael@0 188 if (flags & FLAG_LEFT) {
michael@0 189 if (width > cvtwidth) {
michael@0 190 // Space filling on the right (i.e. left adjusting)
michael@0 191 rightspaces = width - cvtwidth;
michael@0 192 }
michael@0 193 } else {
michael@0 194 if (width > cvtwidth) {
michael@0 195 // Space filling on the left (i.e. right adjusting)
michael@0 196 leftspaces = width - cvtwidth;
michael@0 197 }
michael@0 198 }
michael@0 199 while (--leftspaces >= 0) {
michael@0 200 rv = (*ss->stuff)(ss, " ", 1);
michael@0 201 if (rv < 0) {
michael@0 202 return rv;
michael@0 203 }
michael@0 204 }
michael@0 205 if (signwidth) {
michael@0 206 rv = (*ss->stuff)(ss, &sign, 1);
michael@0 207 if (rv < 0) {
michael@0 208 return rv;
michael@0 209 }
michael@0 210 }
michael@0 211 while (--precwidth >= 0) {
michael@0 212 rv = (*ss->stuff)(ss, "0", 1);
michael@0 213 if (rv < 0) {
michael@0 214 return rv;
michael@0 215 }
michael@0 216 }
michael@0 217 while (--zerowidth >= 0) {
michael@0 218 rv = (*ss->stuff)(ss, "0", 1);
michael@0 219 if (rv < 0) {
michael@0 220 return rv;
michael@0 221 }
michael@0 222 }
michael@0 223 rv = (*ss->stuff)(ss, src, uint32_t(srclen));
michael@0 224 if (rv < 0) {
michael@0 225 return rv;
michael@0 226 }
michael@0 227 while (--rightspaces >= 0) {
michael@0 228 rv = (*ss->stuff)(ss, " ", 1);
michael@0 229 if (rv < 0) {
michael@0 230 return rv;
michael@0 231 }
michael@0 232 }
michael@0 233 return 0;
michael@0 234 }
michael@0 235
michael@0 236 /* Convert a long into its printable form. */
michael@0 237 static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
michael@0 238 int type, int flags, const char *hexp)
michael@0 239 {
michael@0 240 char cvtbuf[100];
michael@0 241 char *cvt;
michael@0 242 int digits;
michael@0 243
michael@0 244 // according to the man page this needs to happen
michael@0 245 if ((prec == 0) && (num == 0)) {
michael@0 246 return 0;
michael@0 247 }
michael@0 248
michael@0 249 // Converting decimal is a little tricky. In the unsigned case we
michael@0 250 // need to stop when we hit 10 digits. In the signed case, we can
michael@0 251 // stop when the number is zero.
michael@0 252 cvt = cvtbuf + sizeof(cvtbuf);
michael@0 253 digits = 0;
michael@0 254 while (num) {
michael@0 255 int digit = (((unsigned long)num) % radix) & 0xF;
michael@0 256 *--cvt = hexp[digit];
michael@0 257 digits++;
michael@0 258 num = (long)(((unsigned long)num) / radix);
michael@0 259 }
michael@0 260 if (digits == 0) {
michael@0 261 *--cvt = '0';
michael@0 262 digits++;
michael@0 263 }
michael@0 264
michael@0 265 // Now that we have the number converted without its sign, deal with
michael@0 266 // the sign and zero padding.
michael@0 267 return fill_n(ss, cvt, digits, width, prec, type, flags);
michael@0 268 }
michael@0 269
michael@0 270 /* Convert a 64-bit integer into its printable form. */
michael@0 271 static int cvt_ll(SprintfState *ss, int64_t num, int width, int prec, int radix,
michael@0 272 int type, int flags, const char *hexp)
michael@0 273 {
michael@0 274 // According to the man page, this needs to happen.
michael@0 275 if (prec == 0 && num == 0)
michael@0 276 return 0;
michael@0 277
michael@0 278 // Converting decimal is a little tricky. In the unsigned case we
michael@0 279 // need to stop when we hit 10 digits. In the signed case, we can
michael@0 280 // stop when the number is zero.
michael@0 281 int64_t rad = int64_t(radix);
michael@0 282 char cvtbuf[100];
michael@0 283 char *cvt = cvtbuf + sizeof(cvtbuf);
michael@0 284 int digits = 0;
michael@0 285 while (num != 0) {
michael@0 286 int64_t quot = uint64_t(num) / rad;
michael@0 287 int64_t rem = uint64_t(num) % rad;
michael@0 288 int32_t digit = int32_t(rem);
michael@0 289 *--cvt = hexp[digit & 0xf];
michael@0 290 digits++;
michael@0 291 num = quot;
michael@0 292 }
michael@0 293 if (digits == 0) {
michael@0 294 *--cvt = '0';
michael@0 295 digits++;
michael@0 296 }
michael@0 297
michael@0 298 // Now that we have the number converted without its sign, deal with
michael@0 299 // the sign and zero padding.
michael@0 300 return fill_n(ss, cvt, digits, width, prec, type, flags);
michael@0 301 }
michael@0 302
michael@0 303 /*
michael@0 304 * Convert a double precision floating point number into its printable
michael@0 305 * form.
michael@0 306 *
michael@0 307 * XXX stop using sprintf to convert floating point
michael@0 308 */
michael@0 309 static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
michael@0 310 {
michael@0 311 char fin[20];
michael@0 312 char fout[300];
michael@0 313 int amount = fmt1 - fmt0;
michael@0 314
michael@0 315 JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
michael@0 316 if (amount >= (int)sizeof(fin)) {
michael@0 317 // Totally bogus % command to sprintf. Just ignore it
michael@0 318 return 0;
michael@0 319 }
michael@0 320 js_memcpy(fin, fmt0, (size_t)amount);
michael@0 321 fin[amount] = 0;
michael@0 322
michael@0 323 // Convert floating point using the native sprintf code
michael@0 324 #ifdef DEBUG
michael@0 325 {
michael@0 326 const char *p = fin;
michael@0 327 while (*p) {
michael@0 328 JS_ASSERT(*p != 'L');
michael@0 329 p++;
michael@0 330 }
michael@0 331 }
michael@0 332 #endif
michael@0 333 sprintf(fout, fin, d);
michael@0 334
michael@0 335 // This assert will catch overflow's of fout, when building with
michael@0 336 // debugging on. At least this way we can track down the evil piece
michael@0 337 // of calling code and fix it!
michael@0 338 JS_ASSERT(strlen(fout) < sizeof(fout));
michael@0 339
michael@0 340 return (*ss->stuff)(ss, fout, strlen(fout));
michael@0 341 }
michael@0 342
michael@0 343 static inline const char *generic_null_str(const char *) { return "(null)"; }
michael@0 344 static inline const jschar *generic_null_str(const jschar *) { return MOZ_UTF16("(null)"); }
michael@0 345
michael@0 346 static inline size_t generic_strlen(const char *s) { return strlen(s); }
michael@0 347 static inline size_t generic_strlen(const jschar *s) { return js_strlen(s); }
michael@0 348
michael@0 349 /*
michael@0 350 * Convert a string into its printable form. "width" is the output
michael@0 351 * width. "prec" is the maximum number of characters of "s" to output,
michael@0 352 * where -1 means until NUL.
michael@0 353 */
michael@0 354 template <typename Char>
michael@0 355 static int
michael@0 356 cvt_s(SprintfState *ss, const Char *s, int width, int prec, int flags)
michael@0 357 {
michael@0 358 if (prec == 0)
michael@0 359 return 0;
michael@0 360 if (!s)
michael@0 361 s = generic_null_str(s);
michael@0 362
michael@0 363 // Limit string length by precision value
michael@0 364 int slen = int(generic_strlen(s));
michael@0 365 if (0 < prec && prec < slen)
michael@0 366 slen = prec;
michael@0 367
michael@0 368 // and away we go
michael@0 369 return fill2(ss, s, slen, width, flags);
michael@0 370 }
michael@0 371
michael@0 372 /*
michael@0 373 * BuildArgArray stands for Numbered Argument list Sprintf
michael@0 374 * for example,
michael@0 375 * fmp = "%4$i, %2$d, %3s, %1d";
michael@0 376 * the number must start from 1, and no gap among them
michael@0 377 */
michael@0 378 static NumArgState *
michael@0 379 BuildArgArray(const char *fmt, va_list ap, int *rv, NumArgState *nasArray)
michael@0 380 {
michael@0 381 size_t number = 0, cn = 0, i;
michael@0 382 const char *p;
michael@0 383 char c;
michael@0 384 NumArgState *nas;
michael@0 385
michael@0 386
michael@0 387 // First pass:
michael@0 388 // Detemine how many legal % I have got, then allocate space.
michael@0 389
michael@0 390 p = fmt;
michael@0 391 *rv = 0;
michael@0 392 i = 0;
michael@0 393 while ((c = *p++) != 0) {
michael@0 394 if (c != '%')
michael@0 395 continue;
michael@0 396 if ((c = *p++) == '%') // skip %% case
michael@0 397 continue;
michael@0 398
michael@0 399 while (c != 0) {
michael@0 400 if (c > '9' || c < '0') {
michael@0 401 if (c == '$') { // numbered argument case
michael@0 402 if (i > 0) {
michael@0 403 *rv = -1;
michael@0 404 return nullptr;
michael@0 405 }
michael@0 406 number++;
michael@0 407 } else { // non-numbered argument case
michael@0 408 if (number > 0) {
michael@0 409 *rv = -1;
michael@0 410 return nullptr;
michael@0 411 }
michael@0 412 i = 1;
michael@0 413 }
michael@0 414 break;
michael@0 415 }
michael@0 416
michael@0 417 c = *p++;
michael@0 418 }
michael@0 419 }
michael@0 420
michael@0 421 if (number == 0)
michael@0 422 return nullptr;
michael@0 423
michael@0 424 if (number > NAS_DEFAULT_NUM) {
michael@0 425 nas = (NumArgState *) js_malloc(number * sizeof(NumArgState));
michael@0 426 if (!nas) {
michael@0 427 *rv = -1;
michael@0 428 return nullptr;
michael@0 429 }
michael@0 430 } else {
michael@0 431 nas = nasArray;
michael@0 432 }
michael@0 433
michael@0 434 for (i = 0; i < number; i++)
michael@0 435 nas[i].type = TYPE_UNKNOWN;
michael@0 436
michael@0 437
michael@0 438 // Second pass:
michael@0 439 // Set nas[].type.
michael@0 440
michael@0 441 p = fmt;
michael@0 442 while ((c = *p++) != 0) {
michael@0 443 if (c != '%')
michael@0 444 continue;
michael@0 445 c = *p++;
michael@0 446 if (c == '%')
michael@0 447 continue;
michael@0 448
michael@0 449 cn = 0;
michael@0 450 while (c && c != '$') { // should improve error check later
michael@0 451 cn = cn*10 + c - '0';
michael@0 452 c = *p++;
michael@0 453 }
michael@0 454
michael@0 455 if (!c || cn < 1 || cn > number) {
michael@0 456 *rv = -1;
michael@0 457 break;
michael@0 458 }
michael@0 459
michael@0 460 // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
michael@0 461 cn--;
michael@0 462 if (nas[cn].type != TYPE_UNKNOWN)
michael@0 463 continue;
michael@0 464
michael@0 465 c = *p++;
michael@0 466
michael@0 467 // width
michael@0 468 if (c == '*') {
michael@0 469 // not supported feature, for the argument is not numbered
michael@0 470 *rv = -1;
michael@0 471 break;
michael@0 472 }
michael@0 473
michael@0 474 while ((c >= '0') && (c <= '9')) {
michael@0 475 c = *p++;
michael@0 476 }
michael@0 477
michael@0 478 // precision
michael@0 479 if (c == '.') {
michael@0 480 c = *p++;
michael@0 481 if (c == '*') {
michael@0 482 // not supported feature, for the argument is not numbered
michael@0 483 *rv = -1;
michael@0 484 break;
michael@0 485 }
michael@0 486
michael@0 487 while ((c >= '0') && (c <= '9')) {
michael@0 488 c = *p++;
michael@0 489 }
michael@0 490 }
michael@0 491
michael@0 492 // size
michael@0 493 nas[cn].type = TYPE_INTN;
michael@0 494 if (c == 'h') {
michael@0 495 nas[cn].type = TYPE_INT16;
michael@0 496 c = *p++;
michael@0 497 } else if (c == 'L') {
michael@0 498 // XXX not quite sure here
michael@0 499 nas[cn].type = TYPE_INT64;
michael@0 500 c = *p++;
michael@0 501 } else if (c == 'l') {
michael@0 502 nas[cn].type = TYPE_INT32;
michael@0 503 c = *p++;
michael@0 504 if (c == 'l') {
michael@0 505 nas[cn].type = TYPE_INT64;
michael@0 506 c = *p++;
michael@0 507 }
michael@0 508 }
michael@0 509
michael@0 510 // format
michael@0 511 switch (c) {
michael@0 512 case 'd':
michael@0 513 case 'c':
michael@0 514 case 'i':
michael@0 515 case 'o':
michael@0 516 case 'u':
michael@0 517 case 'x':
michael@0 518 case 'X':
michael@0 519 break;
michael@0 520
michael@0 521 case 'e':
michael@0 522 case 'f':
michael@0 523 case 'g':
michael@0 524 nas[cn].type = TYPE_DOUBLE;
michael@0 525 break;
michael@0 526
michael@0 527 case 'p':
michael@0 528 // XXX should use cpp
michael@0 529 if (sizeof(void *) == sizeof(int32_t)) {
michael@0 530 nas[cn].type = TYPE_UINT32;
michael@0 531 } else if (sizeof(void *) == sizeof(int64_t)) {
michael@0 532 nas[cn].type = TYPE_UINT64;
michael@0 533 } else if (sizeof(void *) == sizeof(int)) {
michael@0 534 nas[cn].type = TYPE_UINTN;
michael@0 535 } else {
michael@0 536 nas[cn].type = TYPE_UNKNOWN;
michael@0 537 }
michael@0 538 break;
michael@0 539
michael@0 540 case 'C':
michael@0 541 case 'S':
michael@0 542 case 'E':
michael@0 543 case 'G':
michael@0 544 // XXX not supported I suppose
michael@0 545 JS_ASSERT(0);
michael@0 546 nas[cn].type = TYPE_UNKNOWN;
michael@0 547 break;
michael@0 548
michael@0 549 case 's':
michael@0 550 nas[cn].type = (nas[cn].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
michael@0 551 break;
michael@0 552
michael@0 553 case 'n':
michael@0 554 nas[cn].type = TYPE_INTSTR;
michael@0 555 break;
michael@0 556
michael@0 557 default:
michael@0 558 JS_ASSERT(0);
michael@0 559 nas[cn].type = TYPE_UNKNOWN;
michael@0 560 break;
michael@0 561 }
michael@0 562
michael@0 563 // get a legal para.
michael@0 564 if (nas[cn].type == TYPE_UNKNOWN) {
michael@0 565 *rv = -1;
michael@0 566 break;
michael@0 567 }
michael@0 568 }
michael@0 569
michael@0 570
michael@0 571 // Third pass:
michael@0 572 // Fill nas[].ap.
michael@0 573
michael@0 574 if (*rv < 0) {
michael@0 575 if (nas != nasArray)
michael@0 576 js_free(nas);
michael@0 577 return nullptr;
michael@0 578 }
michael@0 579
michael@0 580 cn = 0;
michael@0 581 while (cn < number) {
michael@0 582 if (nas[cn].type == TYPE_UNKNOWN) {
michael@0 583 cn++;
michael@0 584 continue;
michael@0 585 }
michael@0 586
michael@0 587 VARARGS_ASSIGN(nas[cn].ap, ap);
michael@0 588
michael@0 589 switch (nas[cn].type) {
michael@0 590 case TYPE_INT16:
michael@0 591 case TYPE_UINT16:
michael@0 592 case TYPE_INTN:
michael@0 593 case TYPE_UINTN: (void) va_arg(ap, int); break;
michael@0 594 case TYPE_INT32: (void) va_arg(ap, int32_t); break;
michael@0 595 case TYPE_UINT32: (void) va_arg(ap, uint32_t); break;
michael@0 596 case TYPE_INT64: (void) va_arg(ap, int64_t); break;
michael@0 597 case TYPE_UINT64: (void) va_arg(ap, uint64_t); break;
michael@0 598 case TYPE_STRING: (void) va_arg(ap, char*); break;
michael@0 599 case TYPE_WSTRING: (void) va_arg(ap, jschar*); break;
michael@0 600 case TYPE_INTSTR: (void) va_arg(ap, int*); break;
michael@0 601 case TYPE_DOUBLE: (void) va_arg(ap, double); break;
michael@0 602
michael@0 603 default:
michael@0 604 if (nas != nasArray)
michael@0 605 js_free(nas);
michael@0 606 *rv = -1;
michael@0 607 return nullptr;
michael@0 608 }
michael@0 609
michael@0 610 cn++;
michael@0 611 }
michael@0 612
michael@0 613
michael@0 614 return nas;
michael@0 615 }
michael@0 616
michael@0 617 /*
michael@0 618 * The workhorse sprintf code.
michael@0 619 */
michael@0 620 static int
michael@0 621 dosprintf(SprintfState *ss, const char *fmt, va_list ap)
michael@0 622 {
michael@0 623 char c;
michael@0 624 int flags, width, prec, radix, type;
michael@0 625 union {
michael@0 626 char ch;
michael@0 627 jschar wch;
michael@0 628 int i;
michael@0 629 long l;
michael@0 630 int64_t ll;
michael@0 631 double d;
michael@0 632 const char *s;
michael@0 633 const jschar* ws;
michael@0 634 int *ip;
michael@0 635 } u;
michael@0 636 const char *fmt0;
michael@0 637 static const char hex[] = "0123456789abcdef";
michael@0 638 static const char HEX[] = "0123456789ABCDEF";
michael@0 639 const char *hexp;
michael@0 640 int rv, i;
michael@0 641 NumArgState *nas = nullptr;
michael@0 642 NumArgState nasArray[NAS_DEFAULT_NUM];
michael@0 643 char pattern[20];
michael@0 644 const char *dolPt = nullptr; // in "%4$.2f", dolPt will point to '.'
michael@0 645
michael@0 646 // Build an argument array, IF the fmt is numbered argument
michael@0 647 // list style, to contain the Numbered Argument list pointers.
michael@0 648
michael@0 649 nas = BuildArgArray(fmt, ap, &rv, nasArray);
michael@0 650 if (rv < 0) {
michael@0 651 // the fmt contains error Numbered Argument format, jliu@netscape.com
michael@0 652 JS_ASSERT(0);
michael@0 653 return rv;
michael@0 654 }
michael@0 655
michael@0 656 while ((c = *fmt++) != 0) {
michael@0 657 if (c != '%') {
michael@0 658 rv = (*ss->stuff)(ss, fmt - 1, 1);
michael@0 659 if (rv < 0) {
michael@0 660 return rv;
michael@0 661 }
michael@0 662 continue;
michael@0 663 }
michael@0 664 fmt0 = fmt - 1;
michael@0 665
michael@0 666 // Gobble up the % format string. Hopefully we have handled all
michael@0 667 // of the strange cases!
michael@0 668 flags = 0;
michael@0 669 c = *fmt++;
michael@0 670 if (c == '%') {
michael@0 671 // quoting a % with %%
michael@0 672 rv = (*ss->stuff)(ss, fmt - 1, 1);
michael@0 673 if (rv < 0) {
michael@0 674 return rv;
michael@0 675 }
michael@0 676 continue;
michael@0 677 }
michael@0 678
michael@0 679 if (nas != nullptr) {
michael@0 680 // the fmt contains the Numbered Arguments feature
michael@0 681 i = 0;
michael@0 682 while (c && c != '$') { // should improve error check later
michael@0 683 i = (i * 10) + (c - '0');
michael@0 684 c = *fmt++;
michael@0 685 }
michael@0 686
michael@0 687 if (nas[i-1].type == TYPE_UNKNOWN) {
michael@0 688 if (nas && nas != nasArray)
michael@0 689 js_free(nas);
michael@0 690 return -1;
michael@0 691 }
michael@0 692
michael@0 693 ap = nas[i-1].ap;
michael@0 694 dolPt = fmt;
michael@0 695 c = *fmt++;
michael@0 696 }
michael@0 697
michael@0 698 // Examine optional flags. Note that we do not implement the
michael@0 699 // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
michael@0 700 // somewhat ambiguous and not ideal, which is perhaps why
michael@0 701 // the various sprintf() implementations are inconsistent
michael@0 702 // on this feature.
michael@0 703 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
michael@0 704 if (c == '-') flags |= FLAG_LEFT;
michael@0 705 if (c == '+') flags |= FLAG_SIGNED;
michael@0 706 if (c == ' ') flags |= FLAG_SPACED;
michael@0 707 if (c == '0') flags |= FLAG_ZEROS;
michael@0 708 c = *fmt++;
michael@0 709 }
michael@0 710 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
michael@0 711 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
michael@0 712
michael@0 713 // width
michael@0 714 if (c == '*') {
michael@0 715 c = *fmt++;
michael@0 716 width = va_arg(ap, int);
michael@0 717 } else {
michael@0 718 width = 0;
michael@0 719 while ((c >= '0') && (c <= '9')) {
michael@0 720 width = (width * 10) + (c - '0');
michael@0 721 c = *fmt++;
michael@0 722 }
michael@0 723 }
michael@0 724
michael@0 725 // precision
michael@0 726 prec = -1;
michael@0 727 if (c == '.') {
michael@0 728 c = *fmt++;
michael@0 729 if (c == '*') {
michael@0 730 c = *fmt++;
michael@0 731 prec = va_arg(ap, int);
michael@0 732 } else {
michael@0 733 prec = 0;
michael@0 734 while ((c >= '0') && (c <= '9')) {
michael@0 735 prec = (prec * 10) + (c - '0');
michael@0 736 c = *fmt++;
michael@0 737 }
michael@0 738 }
michael@0 739 }
michael@0 740
michael@0 741 // size
michael@0 742 type = TYPE_INTN;
michael@0 743 if (c == 'h') {
michael@0 744 type = TYPE_INT16;
michael@0 745 c = *fmt++;
michael@0 746 } else if (c == 'L') {
michael@0 747 // XXX not quite sure here
michael@0 748 type = TYPE_INT64;
michael@0 749 c = *fmt++;
michael@0 750 } else if (c == 'l') {
michael@0 751 type = TYPE_INT32;
michael@0 752 c = *fmt++;
michael@0 753 if (c == 'l') {
michael@0 754 type = TYPE_INT64;
michael@0 755 c = *fmt++;
michael@0 756 }
michael@0 757 }
michael@0 758
michael@0 759 // format
michael@0 760 hexp = hex;
michael@0 761 switch (c) {
michael@0 762 case 'd': case 'i': // decimal/integer
michael@0 763 radix = 10;
michael@0 764 goto fetch_and_convert;
michael@0 765
michael@0 766 case 'o': // octal
michael@0 767 radix = 8;
michael@0 768 type |= 1;
michael@0 769 goto fetch_and_convert;
michael@0 770
michael@0 771 case 'u': // unsigned decimal
michael@0 772 radix = 10;
michael@0 773 type |= 1;
michael@0 774 goto fetch_and_convert;
michael@0 775
michael@0 776 case 'x': // unsigned hex
michael@0 777 radix = 16;
michael@0 778 type |= 1;
michael@0 779 goto fetch_and_convert;
michael@0 780
michael@0 781 case 'X': // unsigned HEX
michael@0 782 radix = 16;
michael@0 783 hexp = HEX;
michael@0 784 type |= 1;
michael@0 785 goto fetch_and_convert;
michael@0 786
michael@0 787 fetch_and_convert:
michael@0 788 switch (type) {
michael@0 789 case TYPE_INT16:
michael@0 790 u.l = va_arg(ap, int);
michael@0 791 if (u.l < 0) {
michael@0 792 u.l = -u.l;
michael@0 793 flags |= FLAG_NEG;
michael@0 794 }
michael@0 795 goto do_long;
michael@0 796 case TYPE_UINT16:
michael@0 797 u.l = va_arg(ap, int) & 0xffff;
michael@0 798 goto do_long;
michael@0 799 case TYPE_INTN:
michael@0 800 u.l = va_arg(ap, int);
michael@0 801 if (u.l < 0) {
michael@0 802 u.l = -u.l;
michael@0 803 flags |= FLAG_NEG;
michael@0 804 }
michael@0 805 goto do_long;
michael@0 806 case TYPE_UINTN:
michael@0 807 u.l = (long)va_arg(ap, unsigned int);
michael@0 808 goto do_long;
michael@0 809
michael@0 810 case TYPE_INT32:
michael@0 811 u.l = va_arg(ap, int32_t);
michael@0 812 if (u.l < 0) {
michael@0 813 u.l = -u.l;
michael@0 814 flags |= FLAG_NEG;
michael@0 815 }
michael@0 816 goto do_long;
michael@0 817 case TYPE_UINT32:
michael@0 818 u.l = (long)va_arg(ap, uint32_t);
michael@0 819 do_long:
michael@0 820 rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
michael@0 821 if (rv < 0) {
michael@0 822 return rv;
michael@0 823 }
michael@0 824 break;
michael@0 825
michael@0 826 case TYPE_INT64:
michael@0 827 u.ll = va_arg(ap, int64_t);
michael@0 828 if (u.ll < 0) {
michael@0 829 u.ll = -u.ll;
michael@0 830 flags |= FLAG_NEG;
michael@0 831 }
michael@0 832 goto do_longlong;
michael@0 833 case TYPE_UINT64:
michael@0 834 u.ll = va_arg(ap, uint64_t);
michael@0 835 do_longlong:
michael@0 836 rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
michael@0 837 if (rv < 0) {
michael@0 838 return rv;
michael@0 839 }
michael@0 840 break;
michael@0 841 }
michael@0 842 break;
michael@0 843
michael@0 844 case 'e':
michael@0 845 case 'E':
michael@0 846 case 'f':
michael@0 847 case 'g':
michael@0 848 u.d = va_arg(ap, double);
michael@0 849 if (nas != nullptr) {
michael@0 850 i = fmt - dolPt;
michael@0 851 if (i < int(sizeof(pattern))) {
michael@0 852 pattern[0] = '%';
michael@0 853 js_memcpy(&pattern[1], dolPt, size_t(i));
michael@0 854 rv = cvt_f(ss, u.d, pattern, &pattern[i + 1]);
michael@0 855 }
michael@0 856 } else
michael@0 857 rv = cvt_f(ss, u.d, fmt0, fmt);
michael@0 858
michael@0 859 if (rv < 0) {
michael@0 860 return rv;
michael@0 861 }
michael@0 862 break;
michael@0 863
michael@0 864 case 'c':
michael@0 865 if ((flags & FLAG_LEFT) == 0) {
michael@0 866 while (width-- > 1) {
michael@0 867 rv = (*ss->stuff)(ss, " ", 1);
michael@0 868 if (rv < 0) {
michael@0 869 return rv;
michael@0 870 }
michael@0 871 }
michael@0 872 }
michael@0 873 switch (type) {
michael@0 874 case TYPE_INT16:
michael@0 875 case TYPE_INTN:
michael@0 876 u.ch = va_arg(ap, int);
michael@0 877 rv = (*ss->stuff)(ss, &u.ch, 1);
michael@0 878 break;
michael@0 879 }
michael@0 880 if (rv < 0) {
michael@0 881 return rv;
michael@0 882 }
michael@0 883 if (flags & FLAG_LEFT) {
michael@0 884 while (width-- > 1) {
michael@0 885 rv = (*ss->stuff)(ss, " ", 1);
michael@0 886 if (rv < 0) {
michael@0 887 return rv;
michael@0 888 }
michael@0 889 }
michael@0 890 }
michael@0 891 break;
michael@0 892
michael@0 893 case 'p':
michael@0 894 if (sizeof(void *) == sizeof(int32_t)) {
michael@0 895 type = TYPE_UINT32;
michael@0 896 } else if (sizeof(void *) == sizeof(int64_t)) {
michael@0 897 type = TYPE_UINT64;
michael@0 898 } else if (sizeof(void *) == sizeof(int)) {
michael@0 899 type = TYPE_UINTN;
michael@0 900 } else {
michael@0 901 JS_ASSERT(0);
michael@0 902 break;
michael@0 903 }
michael@0 904 radix = 16;
michael@0 905 goto fetch_and_convert;
michael@0 906
michael@0 907 #if 0
michael@0 908 case 'C':
michael@0 909 case 'S':
michael@0 910 case 'E':
michael@0 911 case 'G':
michael@0 912 // XXX not supported I suppose
michael@0 913 JS_ASSERT(0);
michael@0 914 break;
michael@0 915 #endif
michael@0 916
michael@0 917 case 's':
michael@0 918 if(type == TYPE_INT16) {
michael@0 919 u.ws = va_arg(ap, const jschar*);
michael@0 920 rv = cvt_s(ss, u.ws, width, prec, flags);
michael@0 921 } else {
michael@0 922 u.s = va_arg(ap, const char*);
michael@0 923 rv = cvt_s(ss, u.s, width, prec, flags);
michael@0 924 }
michael@0 925 if (rv < 0) {
michael@0 926 return rv;
michael@0 927 }
michael@0 928 break;
michael@0 929
michael@0 930 case 'n':
michael@0 931 u.ip = va_arg(ap, int*);
michael@0 932 if (u.ip) {
michael@0 933 *u.ip = ss->cur - ss->base;
michael@0 934 }
michael@0 935 break;
michael@0 936
michael@0 937 default:
michael@0 938 // Not a % token after all... skip it
michael@0 939 #if 0
michael@0 940 JS_ASSERT(0);
michael@0 941 #endif
michael@0 942 rv = (*ss->stuff)(ss, "%", 1);
michael@0 943 if (rv < 0) {
michael@0 944 return rv;
michael@0 945 }
michael@0 946 rv = (*ss->stuff)(ss, fmt - 1, 1);
michael@0 947 if (rv < 0) {
michael@0 948 return rv;
michael@0 949 }
michael@0 950 }
michael@0 951 }
michael@0 952
michael@0 953 // Stuff trailing NUL
michael@0 954 rv = (*ss->stuff)(ss, "\0", 1);
michael@0 955
michael@0 956 if (nas && nas != nasArray)
michael@0 957 js_free(nas);
michael@0 958
michael@0 959 return rv;
michael@0 960 }
michael@0 961
michael@0 962 /************************************************************************/
michael@0 963
michael@0 964 /*
michael@0 965 * Stuff routine that automatically grows the js_malloc'd output buffer
michael@0 966 * before it overflows.
michael@0 967 */
michael@0 968 static int
michael@0 969 GrowStuff(SprintfState *ss, const char *sp, size_t len)
michael@0 970 {
michael@0 971 ptrdiff_t off;
michael@0 972 char *newbase;
michael@0 973 size_t newlen;
michael@0 974
michael@0 975 off = ss->cur - ss->base;
michael@0 976 if (off + len >= ss->maxlen) {
michael@0 977 /* Grow the buffer */
michael@0 978 newlen = ss->maxlen + ((len > 32) ? len : 32);
michael@0 979 newbase = static_cast<char *>(js_realloc(ss->base, newlen));
michael@0 980 if (!newbase) {
michael@0 981 /* Ran out of memory */
michael@0 982 return -1;
michael@0 983 }
michael@0 984 ss->base = newbase;
michael@0 985 ss->maxlen = newlen;
michael@0 986 ss->cur = ss->base + off;
michael@0 987 }
michael@0 988
michael@0 989 /* Copy data */
michael@0 990 while (len) {
michael@0 991 --len;
michael@0 992 *ss->cur++ = *sp++;
michael@0 993 }
michael@0 994 MOZ_ASSERT(size_t(ss->cur - ss->base) <= ss->maxlen);
michael@0 995 return 0;
michael@0 996 }
michael@0 997
michael@0 998 /*
michael@0 999 * sprintf into a js_malloc'd buffer
michael@0 1000 */
michael@0 1001 JS_PUBLIC_API(char *)
michael@0 1002 JS_smprintf(const char *fmt, ...)
michael@0 1003 {
michael@0 1004 va_list ap;
michael@0 1005 char *rv;
michael@0 1006
michael@0 1007 va_start(ap, fmt);
michael@0 1008 rv = JS_vsmprintf(fmt, ap);
michael@0 1009 va_end(ap);
michael@0 1010 return rv;
michael@0 1011 }
michael@0 1012
michael@0 1013 /*
michael@0 1014 * Free memory allocated, for the caller, by JS_smprintf
michael@0 1015 */
michael@0 1016 JS_PUBLIC_API(void)
michael@0 1017 JS_smprintf_free(char *mem)
michael@0 1018 {
michael@0 1019 js_free(mem);
michael@0 1020 }
michael@0 1021
michael@0 1022 JS_PUBLIC_API(char *)
michael@0 1023 JS_vsmprintf(const char *fmt, va_list ap)
michael@0 1024 {
michael@0 1025 SprintfState ss;
michael@0 1026 int rv;
michael@0 1027
michael@0 1028 ss.stuff = GrowStuff;
michael@0 1029 ss.base = 0;
michael@0 1030 ss.cur = 0;
michael@0 1031 ss.maxlen = 0;
michael@0 1032 rv = dosprintf(&ss, fmt, ap);
michael@0 1033 if (rv < 0) {
michael@0 1034 js_free(ss.base);
michael@0 1035 return 0;
michael@0 1036 }
michael@0 1037 return ss.base;
michael@0 1038 }
michael@0 1039
michael@0 1040 /*
michael@0 1041 * Stuff routine that discards overflow data
michael@0 1042 */
michael@0 1043 static int
michael@0 1044 LimitStuff(SprintfState *ss, const char *sp, size_t len)
michael@0 1045 {
michael@0 1046 size_t limit = ss->maxlen - (ss->cur - ss->base);
michael@0 1047
michael@0 1048 if (len > limit)
michael@0 1049 len = limit;
michael@0 1050 while (len) {
michael@0 1051 --len;
michael@0 1052 *ss->cur++ = *sp++;
michael@0 1053 }
michael@0 1054 return 0;
michael@0 1055 }
michael@0 1056
michael@0 1057 /*
michael@0 1058 * sprintf into a fixed size buffer. Make sure there is a NUL at the end
michael@0 1059 * when finished.
michael@0 1060 */
michael@0 1061 JS_PUBLIC_API(uint32_t)
michael@0 1062 JS_snprintf(char *out, uint32_t outlen, const char *fmt, ...)
michael@0 1063 {
michael@0 1064 va_list ap;
michael@0 1065 int rv;
michael@0 1066
michael@0 1067 JS_ASSERT(int32_t(outlen) > 0);
michael@0 1068 if (int32_t(outlen) <= 0)
michael@0 1069 return 0;
michael@0 1070
michael@0 1071 va_start(ap, fmt);
michael@0 1072 rv = JS_vsnprintf(out, outlen, fmt, ap);
michael@0 1073 va_end(ap);
michael@0 1074 return rv;
michael@0 1075 }
michael@0 1076
michael@0 1077 JS_PUBLIC_API(uint32_t)
michael@0 1078 JS_vsnprintf(char *out, uint32_t outlen, const char *fmt, va_list ap)
michael@0 1079 {
michael@0 1080 SprintfState ss;
michael@0 1081 uint32_t n;
michael@0 1082
michael@0 1083 JS_ASSERT(int32_t(outlen) > 0);
michael@0 1084 if (int32_t(outlen) <= 0) {
michael@0 1085 return 0;
michael@0 1086 }
michael@0 1087
michael@0 1088 ss.stuff = LimitStuff;
michael@0 1089 ss.base = out;
michael@0 1090 ss.cur = out;
michael@0 1091 ss.maxlen = outlen;
michael@0 1092 (void) dosprintf(&ss, fmt, ap);
michael@0 1093
michael@0 1094 /* If we added chars, and we didn't append a null, do it now. */
michael@0 1095 if (ss.cur != ss.base && ss.cur[-1] != '\0')
michael@0 1096 ss.cur[-1] = '\0';
michael@0 1097
michael@0 1098 n = ss.cur - ss.base;
michael@0 1099 return n ? n - 1 : n;
michael@0 1100 }
michael@0 1101
michael@0 1102 JS_PUBLIC_API(char *)
michael@0 1103 JS_sprintf_append(char *last, const char *fmt, ...)
michael@0 1104 {
michael@0 1105 va_list ap;
michael@0 1106 char *rv;
michael@0 1107
michael@0 1108 va_start(ap, fmt);
michael@0 1109 rv = JS_vsprintf_append(last, fmt, ap);
michael@0 1110 va_end(ap);
michael@0 1111 return rv;
michael@0 1112 }
michael@0 1113
michael@0 1114 JS_PUBLIC_API(char *)
michael@0 1115 JS_vsprintf_append(char *last, const char *fmt, va_list ap)
michael@0 1116 {
michael@0 1117 SprintfState ss;
michael@0 1118 int rv;
michael@0 1119
michael@0 1120 ss.stuff = GrowStuff;
michael@0 1121 if (last) {
michael@0 1122 size_t lastlen = strlen(last);
michael@0 1123 ss.base = last;
michael@0 1124 ss.cur = last + lastlen;
michael@0 1125 ss.maxlen = lastlen;
michael@0 1126 } else {
michael@0 1127 ss.base = 0;
michael@0 1128 ss.cur = 0;
michael@0 1129 ss.maxlen = 0;
michael@0 1130 }
michael@0 1131 rv = dosprintf(&ss, fmt, ap);
michael@0 1132 if (rv < 0) {
michael@0 1133 js_free(ss.base);
michael@0 1134 return 0;
michael@0 1135 }
michael@0 1136 return ss.base;
michael@0 1137 }
michael@0 1138
michael@0 1139 #undef TYPE_INT16
michael@0 1140 #undef TYPE_UINT16
michael@0 1141 #undef TYPE_INTN
michael@0 1142 #undef TYPE_UINTN
michael@0 1143 #undef TYPE_INT32
michael@0 1144 #undef TYPE_UINT32
michael@0 1145 #undef TYPE_INT64
michael@0 1146 #undef TYPE_UINT64
michael@0 1147 #undef TYPE_STRING
michael@0 1148 #undef TYPE_DOUBLE
michael@0 1149 #undef TYPE_INTSTR
michael@0 1150 #undef TYPE_WSTRING
michael@0 1151 #undef TYPE_UNKNOWN
michael@0 1152
michael@0 1153 #undef FLAG_LEFT
michael@0 1154 #undef FLAG_SIGNED
michael@0 1155 #undef FLAG_SPACED
michael@0 1156 #undef FLAG_ZEROS
michael@0 1157 #undef FLAG_NEG

mercurial