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