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