nsprpub/pr/src/io/prprf.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial