| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * NOTE: If you change this file, please merge it into rsync, samba, etc. | |
| 3 */ | |
| 4 | |
| 5 /* | |
| 6 * Copyright Patrick Powell 1995 | |
| 7 * This code is based on code written by Patrick Powell (papowell@astart.com) | |
| 8 * It may be used for any purpose as long as this notice remains intact | |
| 9 * on all source code distributions | |
| 10 */ | |
| 11 | |
| 12 /************************************************************** | |
| 13 * Original: | |
| 14 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 | |
| 15 * A bombproof version of doprnt (dopr) included. | |
| 16 * Sigh. This sort of thing is always nasty do deal with. Note that | |
| 17 * the version here does not include floating point... | |
| 18 * | |
| 19 * snprintf() is used instead of sprintf() as it does limit checks | |
| 20 * for string length. This covers a nasty loophole. | |
| 21 * | |
| 22 * The other functions are there to prevent NULL pointers from | |
| 23 * causing nast effects. | |
| 24 * | |
| 25 * More Recently: | |
| 26 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 | |
| 27 * This was ugly. It is still ugly. I opted out of floating point | |
| 28 * numbers, but the formatter understands just about everything | |
| 29 * from the normal C string format, at least as far as I can tell from | |
| 30 * the Solaris 2.5 printf(3S) man page. | |
| 31 * | |
| 32 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 | |
| 33 * Ok, added some minimal floating point support, which means this | |
| 34 * probably requires libm on most operating systems. Don't yet | |
| 35 * support the exponent (e,E) and sigfig (g,G). Also, fmtint() | |
| 36 * was pretty badly broken, it just wasn't being exercised in ways | |
| 37 * which showed it, so that's been fixed. Also, formated the code | |
| 38 * to mutt conventions, and removed dead code left over from the | |
| 39 * original. Also, there is now a builtin-test, just compile with: | |
| 40 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm | |
| 41 * and run snprintf for results. | |
| 42 * | |
| 43 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i | |
| 44 * The PGP code was using unsigned hexadecimal formats. | |
| 45 * Unfortunately, unsigned formats simply didn't work. | |
| 46 * | |
| 47 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 | |
| 48 * The original code assumed that both snprintf() and vsnprintf() were | |
| 49 * missing. Some systems only have snprintf() but not vsnprintf(), so | |
| 50 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. | |
| 51 * | |
| 52 * Andrew Tridgell (tridge@samba.org) Oct 1998 | |
| 53 * fixed handling of %.0f | |
| 54 * added test for HAVE_LONG_DOUBLE | |
| 55 * | |
| 56 * tridge@samba.org, idra@samba.org, April 2001 | |
| 57 * got rid of fcvt code (twas buggy and made testing harder) | |
| 58 * added C99 semantics | |
| 59 * | |
| 60 * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0 | |
| 61 * actually print args for %g and %e | |
| 62 * | |
| 63 * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0 | |
| 64 * Since includes.h isn't included here, VA_COPY has to be defined here. I don'
t | |
| 65 * see any include file that is guaranteed to be here, so I'm defining it | |
| 66 * locally. Fixes AIX and Solaris builds. | |
| 67 * | |
| 68 * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13 | |
| 69 * put the ifdef for HAVE_VA_COPY in one place rather than in lots of | |
| 70 * functions | |
| 71 * | |
| 72 * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4 | |
| 73 * Fix usage of va_list passed as an arg. Use __va_copy before using it | |
| 74 * when it exists. | |
| 75 * | |
| 76 * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14 | |
| 77 * Fix incorrect zpadlen handling in fmtfp. | |
| 78 * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it. | |
| 79 * few mods to make it easier to compile the tests. | |
| 80 * addedd the "Ollie" test to the floating point ones. | |
| 81 * | |
| 82 * Martin Pool (mbp@samba.org) April 2003 | |
| 83 * Remove NO_CONFIG_H so that the test case can be built within a source | |
| 84 * tree with less trouble. | |
| 85 * Remove unnecessary SAFE_FREE() definition. | |
| 86 * | |
| 87 * Martin Pool (mbp@samba.org) May 2003 | |
| 88 * Put in a prototype for dummy_snprintf() to quiet compiler warnings. | |
| 89 * | |
| 90 * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even | |
| 91 * if the C library has some snprintf functions already. | |
| 92 * | |
| 93 * Darren Tucker (dtucker@zip.com.au) 2005 | |
| 94 * Fix bug allowing read overruns of the source string with "%.*s" | |
| 95 * Usually harmless unless the read runs outside the process' allocation | |
| 96 * (eg if your malloc does guard pages) in which case it will segfault. | |
| 97 * From OpenSSH. Also added test for same. | |
| 98 * | |
| 99 * Simo Sorce (idra@samba.org) Jan 2006 | |
| 100 * | |
| 101 * Add support for position independent parameters | |
| 102 * fix fmtstr now it conforms to sprintf wrt min.max | |
| 103 * | |
| 104 **************************************************************/ | |
| 105 | |
| 106 #include "replace.h" | |
| 107 #include "system/locale.h" | |
| 108 | |
| 109 #ifdef TEST_SNPRINTF /* need math library headers for testing */ | |
| 110 | |
| 111 /* In test mode, we pretend that this system doesn't have any snprintf | |
| 112 * functions, regardless of what config.h says. */ | |
| 113 # undef HAVE_SNPRINTF | |
| 114 # undef HAVE_VSNPRINTF | |
| 115 # undef HAVE_C99_VSNPRINTF | |
| 116 # undef HAVE_ASPRINTF | |
| 117 # undef HAVE_VASPRINTF | |
| 118 # include <math.h> | |
| 119 #endif /* TEST_SNPRINTF */ | |
| 120 | |
| 121 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRI
NTF) | |
| 122 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */ | |
| 123 #include <stdio.h> | |
| 124 /* make the compiler happy with an empty file */ | |
| 125 void dummy_snprintf(void); | |
| 126 void dummy_snprintf(void) {} | |
| 127 #endif /* HAVE_SNPRINTF, etc */ | |
| 128 | |
| 129 /* yes this really must be a ||. Don't muck with this (tridge) */ | |
| 130 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) | |
| 131 | |
| 132 #ifdef HAVE_LONG_DOUBLE | |
| 133 #define LDOUBLE long double | |
| 134 #else | |
| 135 #define LDOUBLE double | |
| 136 #endif | |
| 137 | |
| 138 #ifdef HAVE_LONG_LONG | |
| 139 #define LLONG long long | |
| 140 #else | |
| 141 #define LLONG long | |
| 142 #endif | |
| 143 | |
| 144 #ifndef VA_COPY | |
| 145 #ifdef HAVE_VA_COPY | |
| 146 #define VA_COPY(dest, src) va_copy(dest, src) | |
| 147 #else | |
| 148 #ifdef HAVE___VA_COPY | |
| 149 #define VA_COPY(dest, src) __va_copy(dest, src) | |
| 150 #else | |
| 151 #define VA_COPY(dest, src) (dest) = (src) | |
| 152 #endif | |
| 153 #endif | |
| 154 | |
| 155 /* | |
| 156 * dopr(): poor man's version of doprintf | |
| 157 */ | |
| 158 | |
| 159 /* format read states */ | |
| 160 #define DP_S_DEFAULT 0 | |
| 161 #define DP_S_FLAGS 1 | |
| 162 #define DP_S_MIN 2 | |
| 163 #define DP_S_DOT 3 | |
| 164 #define DP_S_MAX 4 | |
| 165 #define DP_S_MOD 5 | |
| 166 #define DP_S_CONV 6 | |
| 167 #define DP_S_DONE 7 | |
| 168 | |
| 169 /* format flags - Bits */ | |
| 170 #define DP_F_MINUS (1 << 0) | |
| 171 #define DP_F_PLUS (1 << 1) | |
| 172 #define DP_F_SPACE (1 << 2) | |
| 173 #define DP_F_NUM (1 << 3) | |
| 174 #define DP_F_ZERO (1 << 4) | |
| 175 #define DP_F_UP (1 << 5) | |
| 176 #define DP_F_UNSIGNED (1 << 6) | |
| 177 | |
| 178 /* Conversion Flags */ | |
| 179 #define DP_C_CHAR 1 | |
| 180 #define DP_C_SHORT 2 | |
| 181 #define DP_C_LONG 3 | |
| 182 #define DP_C_LDOUBLE 4 | |
| 183 #define DP_C_LLONG 5 | |
| 184 #define DP_C_SIZET 6 | |
| 185 | |
| 186 /* Chunk types */ | |
| 187 #define CNK_FMT_STR 0 | |
| 188 #define CNK_INT 1 | |
| 189 #define CNK_OCTAL 2 | |
| 190 #define CNK_UINT 3 | |
| 191 #define CNK_HEX 4 | |
| 192 #define CNK_FLOAT 5 | |
| 193 #define CNK_CHAR 6 | |
| 194 #define CNK_STRING 7 | |
| 195 #define CNK_PTR 8 | |
| 196 #define CNK_NUM 9 | |
| 197 #define CNK_PRCNT 10 | |
| 198 | |
| 199 #define char_to_int(p) ((p)- '0') | |
| 200 #ifndef MAX | |
| 201 #define MAX(p,q) (((p) >= (q)) ? (p) : (q)) | |
| 202 #endif | |
| 203 | |
| 204 struct pr_chunk { | |
| 205 int type; /* chunk type */ | |
| 206 int num; /* parameter number */ | |
| 207 int min; | |
| 208 int max; | |
| 209 int flags; | |
| 210 int cflags; | |
| 211 int start; | |
| 212 int len; | |
| 213 LLONG value; | |
| 214 LDOUBLE fvalue; | |
| 215 char *strvalue; | |
| 216 void *pnum; | |
| 217 struct pr_chunk *min_star; | |
| 218 struct pr_chunk *max_star; | |
| 219 struct pr_chunk *next; | |
| 220 }; | |
| 221 | |
| 222 struct pr_chunk_x { | |
| 223 struct pr_chunk **chunks; | |
| 224 int num; | |
| 225 }; | |
| 226 | |
| 227 static int dopr(char *buffer, size_t maxlen, const char *format, | |
| 228 va_list args_in); | |
| 229 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, | |
| 230 char *value, int flags, int min, int max); | |
| 231 static void fmtint(char *buffer, size_t *currlen, size_t maxlen, | |
| 232 LLONG value, int base, int min, int max, int flags); | |
| 233 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, | |
| 234 LDOUBLE fvalue, int min, int max, int flags); | |
| 235 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); | |
| 236 static struct pr_chunk *new_chunk(void); | |
| 237 static int add_cnk_list_entry(struct pr_chunk_x **list, | |
| 238 int max_num, struct pr_chunk *chunk); | |
| 239 | |
| 240 static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in
) | |
| 241 { | |
| 242 char ch; | |
| 243 int state; | |
| 244 int pflag; | |
| 245 int pnum; | |
| 246 int pfirst; | |
| 247 size_t currlen; | |
| 248 va_list args; | |
| 249 const char *base; | |
| 250 struct pr_chunk *chunks = NULL; | |
| 251 struct pr_chunk *cnk = NULL; | |
| 252 struct pr_chunk_x *clist = NULL; | |
| 253 int max_pos; | |
| 254 int ret = -1; | |
| 255 | |
| 256 VA_COPY(args, args_in); | |
| 257 | |
| 258 state = DP_S_DEFAULT; | |
| 259 pfirst = 1; | |
| 260 pflag = 0; | |
| 261 pnum = 0; | |
| 262 | |
| 263 max_pos = 0; | |
| 264 base = format; | |
| 265 ch = *format++; | |
| 266 | |
| 267 /* retrieve the string structure as chunks */ | |
| 268 while (state != DP_S_DONE) { | |
| 269 if (ch == '\0') | |
| 270 state = DP_S_DONE; | |
| 271 | |
| 272 switch(state) { | |
| 273 case DP_S_DEFAULT: | |
| 274 | |
| 275 if (cnk) { | |
| 276 cnk->next = new_chunk(); | |
| 277 cnk = cnk->next; | |
| 278 } else { | |
| 279 cnk = new_chunk(); | |
| 280 } | |
| 281 if (!cnk) goto done; | |
| 282 if (!chunks) chunks = cnk; | |
| 283 | |
| 284 if (ch == '%') { | |
| 285 state = DP_S_FLAGS; | |
| 286 ch = *format++; | |
| 287 } else { | |
| 288 cnk->type = CNK_FMT_STR; | |
| 289 cnk->start = format - base -1; | |
| 290 while ((ch != '\0') && (ch != '%')) ch = *format
++; | |
| 291 cnk->len = format - base - cnk->start -1; | |
| 292 } | |
| 293 break; | |
| 294 case DP_S_FLAGS: | |
| 295 switch (ch) { | |
| 296 case '-': | |
| 297 cnk->flags |= DP_F_MINUS; | |
| 298 ch = *format++; | |
| 299 break; | |
| 300 case '+': | |
| 301 cnk->flags |= DP_F_PLUS; | |
| 302 ch = *format++; | |
| 303 break; | |
| 304 case ' ': | |
| 305 cnk->flags |= DP_F_SPACE; | |
| 306 ch = *format++; | |
| 307 break; | |
| 308 case '#': | |
| 309 cnk->flags |= DP_F_NUM; | |
| 310 ch = *format++; | |
| 311 break; | |
| 312 case '0': | |
| 313 cnk->flags |= DP_F_ZERO; | |
| 314 ch = *format++; | |
| 315 break; | |
| 316 case 'I': | |
| 317 /* internationalization not supported yet */ | |
| 318 ch = *format++; | |
| 319 break; | |
| 320 default: | |
| 321 state = DP_S_MIN; | |
| 322 break; | |
| 323 } | |
| 324 break; | |
| 325 case DP_S_MIN: | |
| 326 if (isdigit((unsigned char)ch)) { | |
| 327 cnk->min = 10 * cnk->min + char_to_int (ch); | |
| 328 ch = *format++; | |
| 329 } else if (ch == '$') { | |
| 330 if (!pfirst && !pflag) { | |
| 331 /* parameters must be all positioned or
none */ | |
| 332 goto done; | |
| 333 } | |
| 334 if (pfirst) { | |
| 335 pfirst = 0; | |
| 336 pflag = 1; | |
| 337 } | |
| 338 if (cnk->min == 0) /* what ?? */ | |
| 339 goto done; | |
| 340 cnk->num = cnk->min; | |
| 341 cnk->min = 0; | |
| 342 ch = *format++; | |
| 343 } else if (ch == '*') { | |
| 344 if (pfirst) pfirst = 0; | |
| 345 cnk->min_star = new_chunk(); | |
| 346 if (!cnk->min_star) /* out of memory :-( */ | |
| 347 goto done; | |
| 348 cnk->min_star->type = CNK_INT; | |
| 349 if (pflag) { | |
| 350 int num; | |
| 351 ch = *format++; | |
| 352 if (!isdigit((unsigned char)ch)) { | |
| 353 /* parameters must be all positi
oned or none */ | |
| 354 goto done; | |
| 355 } | |
| 356 for (num = 0; isdigit((unsigned char)ch)
; ch = *format++) { | |
| 357 num = 10 * num + char_to_int(ch)
; | |
| 358 } | |
| 359 cnk->min_star->num = num; | |
| 360 if (ch != '$') /* what ?? */ | |
| 361 goto done; | |
| 362 } else { | |
| 363 cnk->min_star->num = ++pnum; | |
| 364 } | |
| 365 max_pos = add_cnk_list_entry(&clist, max_pos, cn
k->min_star); | |
| 366 if (max_pos == 0) /* out of memory :-( */ | |
| 367 goto done; | |
| 368 ch = *format++; | |
| 369 state = DP_S_DOT; | |
| 370 } else { | |
| 371 if (pfirst) pfirst = 0; | |
| 372 state = DP_S_DOT; | |
| 373 } | |
| 374 break; | |
| 375 case DP_S_DOT: | |
| 376 if (ch == '.') { | |
| 377 state = DP_S_MAX; | |
| 378 ch = *format++; | |
| 379 } else { | |
| 380 state = DP_S_MOD; | |
| 381 } | |
| 382 break; | |
| 383 case DP_S_MAX: | |
| 384 if (isdigit((unsigned char)ch)) { | |
| 385 if (cnk->max < 0) | |
| 386 cnk->max = 0; | |
| 387 cnk->max = 10 * cnk->max + char_to_int (ch); | |
| 388 ch = *format++; | |
| 389 } else if (ch == '$') { | |
| 390 if (!pfirst && !pflag) { | |
| 391 /* parameters must be all positioned or
none */ | |
| 392 goto done; | |
| 393 } | |
| 394 if (cnk->max <= 0) /* what ?? */ | |
| 395 goto done; | |
| 396 cnk->num = cnk->max; | |
| 397 cnk->max = -1; | |
| 398 ch = *format++; | |
| 399 } else if (ch == '*') { | |
| 400 cnk->max_star = new_chunk(); | |
| 401 if (!cnk->max_star) /* out of memory :-( */ | |
| 402 goto done; | |
| 403 cnk->max_star->type = CNK_INT; | |
| 404 if (pflag) { | |
| 405 int num; | |
| 406 ch = *format++; | |
| 407 if (!isdigit((unsigned char)ch)) { | |
| 408 /* parameters must be all positi
oned or none */ | |
| 409 goto done; | |
| 410 } | |
| 411 for (num = 0; isdigit((unsigned char)ch)
; ch = *format++) { | |
| 412 num = 10 * num + char_to_int(ch)
; | |
| 413 } | |
| 414 cnk->max_star->num = num; | |
| 415 if (ch != '$') /* what ?? */ | |
| 416 goto done; | |
| 417 } else { | |
| 418 cnk->max_star->num = ++pnum; | |
| 419 } | |
| 420 max_pos = add_cnk_list_entry(&clist, max_pos, cn
k->max_star); | |
| 421 if (max_pos == 0) /* out of memory :-( */ | |
| 422 goto done; | |
| 423 | |
| 424 ch = *format++; | |
| 425 state = DP_S_MOD; | |
| 426 } else { | |
| 427 state = DP_S_MOD; | |
| 428 } | |
| 429 break; | |
| 430 case DP_S_MOD: | |
| 431 switch (ch) { | |
| 432 case 'h': | |
| 433 cnk->cflags = DP_C_SHORT; | |
| 434 ch = *format++; | |
| 435 if (ch == 'h') { | |
| 436 cnk->cflags = DP_C_CHAR; | |
| 437 ch = *format++; | |
| 438 } | |
| 439 break; | |
| 440 case 'l': | |
| 441 cnk->cflags = DP_C_LONG; | |
| 442 ch = *format++; | |
| 443 if (ch == 'l') { /* It's a long long */ | |
| 444 cnk->cflags = DP_C_LLONG; | |
| 445 ch = *format++; | |
| 446 } | |
| 447 break; | |
| 448 case 'L': | |
| 449 cnk->cflags = DP_C_LDOUBLE; | |
| 450 ch = *format++; | |
| 451 break; | |
| 452 case 'z': | |
| 453 cnk->cflags = DP_C_SIZET; | |
| 454 ch = *format++; | |
| 455 break; | |
| 456 default: | |
| 457 break; | |
| 458 } | |
| 459 state = DP_S_CONV; | |
| 460 break; | |
| 461 case DP_S_CONV: | |
| 462 if (cnk->num == 0) cnk->num = ++pnum; | |
| 463 max_pos = add_cnk_list_entry(&clist, max_pos, cnk); | |
| 464 if (max_pos == 0) /* out of memory :-( */ | |
| 465 goto done; | |
| 466 | |
| 467 switch (ch) { | |
| 468 case 'd': | |
| 469 case 'i': | |
| 470 cnk->type = CNK_INT; | |
| 471 break; | |
| 472 case 'o': | |
| 473 cnk->type = CNK_OCTAL; | |
| 474 cnk->flags |= DP_F_UNSIGNED; | |
| 475 break; | |
| 476 case 'u': | |
| 477 cnk->type = CNK_UINT; | |
| 478 cnk->flags |= DP_F_UNSIGNED; | |
| 479 break; | |
| 480 case 'X': | |
| 481 cnk->flags |= DP_F_UP; | |
| 482 case 'x': | |
| 483 cnk->type = CNK_HEX; | |
| 484 cnk->flags |= DP_F_UNSIGNED; | |
| 485 break; | |
| 486 case 'A': | |
| 487 /* hex float not supported yet */ | |
| 488 case 'E': | |
| 489 case 'G': | |
| 490 case 'F': | |
| 491 cnk->flags |= DP_F_UP; | |
| 492 case 'a': | |
| 493 /* hex float not supported yet */ | |
| 494 case 'e': | |
| 495 case 'f': | |
| 496 case 'g': | |
| 497 cnk->type = CNK_FLOAT; | |
| 498 break; | |
| 499 case 'c': | |
| 500 cnk->type = CNK_CHAR; | |
| 501 break; | |
| 502 case 's': | |
| 503 cnk->type = CNK_STRING; | |
| 504 break; | |
| 505 case 'p': | |
| 506 cnk->type = CNK_PTR; | |
| 507 break; | |
| 508 case 'n': | |
| 509 cnk->type = CNK_NUM; | |
| 510 break; | |
| 511 case '%': | |
| 512 cnk->type = CNK_PRCNT; | |
| 513 break; | |
| 514 default: | |
| 515 /* Unknown, bail out*/ | |
| 516 goto done; | |
| 517 } | |
| 518 ch = *format++; | |
| 519 state = DP_S_DEFAULT; | |
| 520 break; | |
| 521 case DP_S_DONE: | |
| 522 break; | |
| 523 default: | |
| 524 /* hmm? */ | |
| 525 break; /* some picky compilers need this */ | |
| 526 } | |
| 527 } | |
| 528 | |
| 529 /* retrieve the format arguments */ | |
| 530 for (pnum = 0; pnum < max_pos; pnum++) { | |
| 531 int i; | |
| 532 | |
| 533 if (clist[pnum].num == 0) { | |
| 534 /* ignoring a parameter should not be permitted | |
| 535 * all parameters must be matched at least once | |
| 536 * BUT seem some system ignore this rule ... | |
| 537 * at least my glibc based system does --SSS | |
| 538 */ | |
| 539 #ifdef DEBUG_SNPRINTF | |
| 540 printf("parameter at position %d not used\n", pnum+1); | |
| 541 #endif | |
| 542 /* eat the parameter */ | |
| 543 va_arg (args, int); | |
| 544 continue; | |
| 545 } | |
| 546 for (i = 1; i < clist[pnum].num; i++) { | |
| 547 if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]
->type) { | |
| 548 /* nooo noo no! | |
| 549 * all the references to a parameter | |
| 550 * must be of the same type | |
| 551 */ | |
| 552 goto done; | |
| 553 } | |
| 554 } | |
| 555 cnk = clist[pnum].chunks[0]; | |
| 556 switch (cnk->type) { | |
| 557 case CNK_INT: | |
| 558 if (cnk->cflags == DP_C_SHORT) | |
| 559 cnk->value = va_arg (args, int); | |
| 560 else if (cnk->cflags == DP_C_LONG) | |
| 561 cnk->value = va_arg (args, long int); | |
| 562 else if (cnk->cflags == DP_C_LLONG) | |
| 563 cnk->value = va_arg (args, LLONG); | |
| 564 else if (cnk->cflags == DP_C_SIZET) | |
| 565 cnk->value = va_arg (args, ssize_t); | |
| 566 else | |
| 567 cnk->value = va_arg (args, int); | |
| 568 | |
| 569 for (i = 1; i < clist[pnum].num; i++) { | |
| 570 clist[pnum].chunks[i]->value = cnk->value; | |
| 571 } | |
| 572 break; | |
| 573 | |
| 574 case CNK_OCTAL: | |
| 575 case CNK_UINT: | |
| 576 case CNK_HEX: | |
| 577 if (cnk->cflags == DP_C_SHORT) | |
| 578 cnk->value = va_arg (args, unsigned int); | |
| 579 else if (cnk->cflags == DP_C_LONG) | |
| 580 cnk->value = (unsigned long int)va_arg (args, un
signed long int); | |
| 581 else if (cnk->cflags == DP_C_LLONG) | |
| 582 cnk->value = (LLONG)va_arg (args, unsigned LLONG
); | |
| 583 else if (cnk->cflags == DP_C_SIZET) | |
| 584 cnk->value = (size_t)va_arg (args, size_t); | |
| 585 else | |
| 586 cnk->value = (unsigned int)va_arg (args, unsigne
d int); | |
| 587 | |
| 588 for (i = 1; i < clist[pnum].num; i++) { | |
| 589 clist[pnum].chunks[i]->value = cnk->value; | |
| 590 } | |
| 591 break; | |
| 592 | |
| 593 case CNK_FLOAT: | |
| 594 if (cnk->cflags == DP_C_LDOUBLE) | |
| 595 cnk->fvalue = va_arg (args, LDOUBLE); | |
| 596 else | |
| 597 cnk->fvalue = va_arg (args, double); | |
| 598 | |
| 599 for (i = 1; i < clist[pnum].num; i++) { | |
| 600 clist[pnum].chunks[i]->fvalue = cnk->fvalue; | |
| 601 } | |
| 602 break; | |
| 603 | |
| 604 case CNK_CHAR: | |
| 605 cnk->value = va_arg (args, int); | |
| 606 | |
| 607 for (i = 1; i < clist[pnum].num; i++) { | |
| 608 clist[pnum].chunks[i]->value = cnk->value; | |
| 609 } | |
| 610 break; | |
| 611 | |
| 612 case CNK_STRING: | |
| 613 cnk->strvalue = va_arg (args, char *); | |
| 614 if (!cnk->strvalue) cnk->strvalue = "(NULL)"; | |
| 615 | |
| 616 for (i = 1; i < clist[pnum].num; i++) { | |
| 617 clist[pnum].chunks[i]->strvalue = cnk->strvalue; | |
| 618 } | |
| 619 break; | |
| 620 | |
| 621 case CNK_PTR: | |
| 622 cnk->strvalue = va_arg (args, void *); | |
| 623 for (i = 1; i < clist[pnum].num; i++) { | |
| 624 clist[pnum].chunks[i]->strvalue = cnk->strvalue; | |
| 625 } | |
| 626 break; | |
| 627 | |
| 628 case CNK_NUM: | |
| 629 if (cnk->cflags == DP_C_CHAR) | |
| 630 cnk->pnum = va_arg (args, char *); | |
| 631 else if (cnk->cflags == DP_C_SHORT) | |
| 632 cnk->pnum = va_arg (args, short int *); | |
| 633 else if (cnk->cflags == DP_C_LONG) | |
| 634 cnk->pnum = va_arg (args, long int *); | |
| 635 else if (cnk->cflags == DP_C_LLONG) | |
| 636 cnk->pnum = va_arg (args, LLONG *); | |
| 637 else if (cnk->cflags == DP_C_SIZET) | |
| 638 cnk->pnum = va_arg (args, ssize_t *); | |
| 639 else | |
| 640 cnk->pnum = va_arg (args, int *); | |
| 641 | |
| 642 for (i = 1; i < clist[pnum].num; i++) { | |
| 643 clist[pnum].chunks[i]->pnum = cnk->pnum; | |
| 644 } | |
| 645 break; | |
| 646 | |
| 647 case CNK_PRCNT: | |
| 648 break; | |
| 649 | |
| 650 default: | |
| 651 /* what ?? */ | |
| 652 goto done; | |
| 653 } | |
| 654 } | |
| 655 /* print out the actual string from chunks */ | |
| 656 currlen = 0; | |
| 657 cnk = chunks; | |
| 658 while (cnk) { | |
| 659 int len, min, max; | |
| 660 | |
| 661 if (cnk->min_star) min = cnk->min_star->value; | |
| 662 else min = cnk->min; | |
| 663 if (cnk->max_star) max = cnk->max_star->value; | |
| 664 else max = cnk->max; | |
| 665 | |
| 666 switch (cnk->type) { | |
| 667 | |
| 668 case CNK_FMT_STR: | |
| 669 if (maxlen != 0 && maxlen > currlen) { | |
| 670 if (maxlen > (currlen + cnk->len)) len = cnk->le
n; | |
| 671 else len = maxlen - currlen; | |
| 672 | |
| 673 memcpy(&(buffer[currlen]), &(base[cnk->start]),
len); | |
| 674 } | |
| 675 currlen += cnk->len; | |
| 676 | |
| 677 break; | |
| 678 | |
| 679 case CNK_INT: | |
| 680 case CNK_UINT: | |
| 681 fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, m
ax, cnk->flags); | |
| 682 break; | |
| 683 | |
| 684 case CNK_OCTAL: | |
| 685 fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, ma
x, cnk->flags); | |
| 686 break; | |
| 687 | |
| 688 case CNK_HEX: | |
| 689 fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, m
ax, cnk->flags); | |
| 690 break; | |
| 691 | |
| 692 case CNK_FLOAT: | |
| 693 fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max,
cnk->flags); | |
| 694 break; | |
| 695 | |
| 696 case CNK_CHAR: | |
| 697 dopr_outch (buffer, &currlen, maxlen, cnk->value); | |
| 698 break; | |
| 699 | |
| 700 case CNK_STRING: | |
| 701 if (max == -1) { | |
| 702 max = strlen(cnk->strvalue); | |
| 703 } | |
| 704 fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->fl
ags, min, max); | |
| 705 break; | |
| 706 | |
| 707 case CNK_PTR: | |
| 708 fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue),
16, min, max, cnk->flags); | |
| 709 break; | |
| 710 | |
| 711 case CNK_NUM: | |
| 712 if (cnk->cflags == DP_C_CHAR) | |
| 713 *((char *)(cnk->pnum)) = (char)currlen; | |
| 714 else if (cnk->cflags == DP_C_SHORT) | |
| 715 *((short int *)(cnk->pnum)) = (short int)currlen
; | |
| 716 else if (cnk->cflags == DP_C_LONG) | |
| 717 *((long int *)(cnk->pnum)) = (long int)currlen; | |
| 718 else if (cnk->cflags == DP_C_LLONG) | |
| 719 *((LLONG *)(cnk->pnum)) = (LLONG)currlen; | |
| 720 else if (cnk->cflags == DP_C_SIZET) | |
| 721 *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen; | |
| 722 else | |
| 723 *((int *)(cnk->pnum)) = (int)currlen; | |
| 724 break; | |
| 725 | |
| 726 case CNK_PRCNT: | |
| 727 dopr_outch (buffer, &currlen, maxlen, '%'); | |
| 728 break; | |
| 729 | |
| 730 default: | |
| 731 /* what ?? */ | |
| 732 goto done; | |
| 733 } | |
| 734 cnk = cnk->next; | |
| 735 } | |
| 736 if (maxlen != 0) { | |
| 737 if (currlen < maxlen - 1) | |
| 738 buffer[currlen] = '\0'; | |
| 739 else if (maxlen > 0) | |
| 740 buffer[maxlen - 1] = '\0'; | |
| 741 } | |
| 742 ret = currlen; | |
| 743 | |
| 744 done: | |
| 745 va_end(args); | |
| 746 | |
| 747 while (chunks) { | |
| 748 cnk = chunks->next; | |
| 749 free(chunks); | |
| 750 chunks = cnk; | |
| 751 } | |
| 752 if (clist) { | |
| 753 for (pnum = 0; pnum < max_pos; pnum++) { | |
| 754 if (clist[pnum].chunks) free(clist[pnum].chunks); | |
| 755 } | |
| 756 free(clist); | |
| 757 } | |
| 758 return ret; | |
| 759 } | |
| 760 | |
| 761 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, | |
| 762 char *value, int flags, int min, int max) | |
| 763 { | |
| 764 int padlen, strln; /* amount to pad */ | |
| 765 int cnt = 0; | |
| 766 | |
| 767 #ifdef DEBUG_SNPRINTF | |
| 768 printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); | |
| 769 #endif | |
| 770 if (value == 0) { | |
| 771 value = "<NULL>"; | |
| 772 } | |
| 773 | |
| 774 for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ | |
| 775 padlen = min - strln; | |
| 776 if (padlen < 0) | |
| 777 padlen = 0; | |
| 778 if (flags & DP_F_MINUS) | |
| 779 padlen = -padlen; /* Left Justify */ | |
| 780 | |
| 781 while (padlen > 0) { | |
| 782 dopr_outch (buffer, currlen, maxlen, ' '); | |
| 783 --padlen; | |
| 784 } | |
| 785 while (*value && (cnt < max)) { | |
| 786 dopr_outch (buffer, currlen, maxlen, *value++); | |
| 787 ++cnt; | |
| 788 } | |
| 789 while (padlen < 0) { | |
| 790 dopr_outch (buffer, currlen, maxlen, ' '); | |
| 791 ++padlen; | |
| 792 } | |
| 793 } | |
| 794 | |
| 795 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ | |
| 796 | |
| 797 static void fmtint(char *buffer, size_t *currlen, size_t maxlen, | |
| 798 LLONG value, int base, int min, int max, int flags) | |
| 799 { | |
| 800 int signvalue = 0; | |
| 801 unsigned LLONG uvalue; | |
| 802 char convert[20]; | |
| 803 int place = 0; | |
| 804 int spadlen = 0; /* amount to space pad */ | |
| 805 int zpadlen = 0; /* amount to zero pad */ | |
| 806 int caps = 0; | |
| 807 | |
| 808 if (max < 0) | |
| 809 max = 0; | |
| 810 | |
| 811 uvalue = value; | |
| 812 | |
| 813 if(!(flags & DP_F_UNSIGNED)) { | |
| 814 if( value < 0 ) { | |
| 815 signvalue = '-'; | |
| 816 uvalue = -value; | |
| 817 } else { | |
| 818 if (flags & DP_F_PLUS) /* Do a sign (+/i) */ | |
| 819 signvalue = '+'; | |
| 820 else if (flags & DP_F_SPACE) | |
| 821 signvalue = ' '; | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ | |
| 826 | |
| 827 do { | |
| 828 convert[place++] = | |
| 829 (caps? "0123456789ABCDEF":"0123456789abcdef") | |
| 830 [uvalue % (unsigned)base ]; | |
| 831 uvalue = (uvalue / (unsigned)base ); | |
| 832 } while(uvalue && (place < 20)); | |
| 833 if (place == 20) place--; | |
| 834 convert[place] = 0; | |
| 835 | |
| 836 zpadlen = max - place; | |
| 837 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); | |
| 838 if (zpadlen < 0) zpadlen = 0; | |
| 839 if (spadlen < 0) spadlen = 0; | |
| 840 if (flags & DP_F_ZERO) { | |
| 841 zpadlen = MAX(zpadlen, spadlen); | |
| 842 spadlen = 0; | |
| 843 } | |
| 844 if (flags & DP_F_MINUS) | |
| 845 spadlen = -spadlen; /* Left Justifty */ | |
| 846 | |
| 847 #ifdef DEBUG_SNPRINTF | |
| 848 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", | |
| 849 zpadlen, spadlen, min, max, place); | |
| 850 #endif | |
| 851 | |
| 852 /* Spaces */ | |
| 853 while (spadlen > 0) { | |
| 854 dopr_outch (buffer, currlen, maxlen, ' '); | |
| 855 --spadlen; | |
| 856 } | |
| 857 | |
| 858 /* Sign */ | |
| 859 if (signvalue) | |
| 860 dopr_outch (buffer, currlen, maxlen, signvalue); | |
| 861 | |
| 862 /* Zeros */ | |
| 863 if (zpadlen > 0) { | |
| 864 while (zpadlen > 0) { | |
| 865 dopr_outch (buffer, currlen, maxlen, '0'); | |
| 866 --zpadlen; | |
| 867 } | |
| 868 } | |
| 869 | |
| 870 /* Digits */ | |
| 871 while (place > 0) | |
| 872 dopr_outch (buffer, currlen, maxlen, convert[--place]); | |
| 873 | |
| 874 /* Left Justified spaces */ | |
| 875 while (spadlen < 0) { | |
| 876 dopr_outch (buffer, currlen, maxlen, ' '); | |
| 877 ++spadlen; | |
| 878 } | |
| 879 } | |
| 880 | |
| 881 static LDOUBLE abs_val(LDOUBLE value) | |
| 882 { | |
| 883 LDOUBLE result = value; | |
| 884 | |
| 885 if (value < 0) | |
| 886 result = -value; | |
| 887 | |
| 888 return result; | |
| 889 } | |
| 890 | |
| 891 static LDOUBLE POW10(int exp) | |
| 892 { | |
| 893 LDOUBLE result = 1; | |
| 894 | |
| 895 while (exp) { | |
| 896 result *= 10; | |
| 897 exp--; | |
| 898 } | |
| 899 | |
| 900 return result; | |
| 901 } | |
| 902 | |
| 903 static LLONG ROUND(LDOUBLE value) | |
| 904 { | |
| 905 LLONG intpart; | |
| 906 | |
| 907 intpart = (LLONG)value; | |
| 908 value = value - intpart; | |
| 909 if (value >= 0.5) intpart++; | |
| 910 | |
| 911 return intpart; | |
| 912 } | |
| 913 | |
| 914 /* a replacement for modf that doesn't need the math library. Should | |
| 915 be portable, but slow */ | |
| 916 static double my_modf(double x0, double *iptr) | |
| 917 { | |
| 918 int i; | |
| 919 LLONG l=0; | |
| 920 double x = x0; | |
| 921 double f = 1.0; | |
| 922 | |
| 923 for (i=0;i<100;i++) { | |
| 924 l = (long)x; | |
| 925 if (l <= (x+1) && l >= (x-1)) break; | |
| 926 x *= 0.1; | |
| 927 f *= 10.0; | |
| 928 } | |
| 929 | |
| 930 if (i == 100) { | |
| 931 /* yikes! the number is beyond what we can handle. What do we do
? */ | |
| 932 (*iptr) = 0; | |
| 933 return 0; | |
| 934 } | |
| 935 | |
| 936 if (i != 0) { | |
| 937 double i2; | |
| 938 double ret; | |
| 939 | |
| 940 ret = my_modf(x0-l*f, &i2); | |
| 941 (*iptr) = l*f + i2; | |
| 942 return ret; | |
| 943 } | |
| 944 | |
| 945 (*iptr) = l; | |
| 946 return x - (*iptr); | |
| 947 } | |
| 948 | |
| 949 | |
| 950 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, | |
| 951 LDOUBLE fvalue, int min, int max, int flags) | |
| 952 { | |
| 953 int signvalue = 0; | |
| 954 double ufvalue; | |
| 955 char iconvert[311]; | |
| 956 char fconvert[311]; | |
| 957 int iplace = 0; | |
| 958 int fplace = 0; | |
| 959 int padlen = 0; /* amount to pad */ | |
| 960 int zpadlen = 0; | |
| 961 int caps = 0; | |
| 962 int idx; | |
| 963 double intpart; | |
| 964 double fracpart; | |
| 965 double temp; | |
| 966 | |
| 967 /* | |
| 968 * AIX manpage says the default is 0, but Solaris says the default | |
| 969 * is 6, and sprintf on AIX defaults to 6 | |
| 970 */ | |
| 971 if (max < 0) | |
| 972 max = 6; | |
| 973 | |
| 974 ufvalue = abs_val (fvalue); | |
| 975 | |
| 976 if (fvalue < 0) { | |
| 977 signvalue = '-'; | |
| 978 } else { | |
| 979 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ | |
| 980 signvalue = '+'; | |
| 981 } else { | |
| 982 if (flags & DP_F_SPACE) | |
| 983 signvalue = ' '; | |
| 984 } | |
| 985 } | |
| 986 | |
| 987 #if 0 | |
| 988 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ | |
| 989 #endif | |
| 990 | |
| 991 #if 0 | |
| 992 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ | |
| 993 #endif | |
| 994 | |
| 995 /* | |
| 996 * Sorry, we only support 9 digits past the decimal because of our | |
| 997 * conversion method | |
| 998 */ | |
| 999 if (max > 9) | |
| 1000 max = 9; | |
| 1001 | |
| 1002 /* We "cheat" by converting the fractional part to integer by | |
| 1003 * multiplying by a factor of 10 | |
| 1004 */ | |
| 1005 | |
| 1006 temp = ufvalue; | |
| 1007 my_modf(temp, &intpart); | |
| 1008 | |
| 1009 fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); | |
| 1010 | |
| 1011 if (fracpart >= POW10(max)) { | |
| 1012 intpart++; | |
| 1013 fracpart -= POW10(max); | |
| 1014 } | |
| 1015 | |
| 1016 | |
| 1017 /* Convert integer part */ | |
| 1018 do { | |
| 1019 temp = intpart*0.1; | |
| 1020 my_modf(temp, &intpart); | |
| 1021 idx = (int) ((temp -intpart +0.05)* 10.0); | |
| 1022 /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ | |
| 1023 /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ | |
| 1024 iconvert[iplace++] = | |
| 1025 (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; | |
| 1026 } while (intpart && (iplace < 311)); | |
| 1027 if (iplace == 311) iplace--; | |
| 1028 iconvert[iplace] = 0; | |
| 1029 | |
| 1030 /* Convert fractional part */ | |
| 1031 if (fracpart) | |
| 1032 { | |
| 1033 do { | |
| 1034 temp = fracpart*0.1; | |
| 1035 my_modf(temp, &fracpart); | |
| 1036 idx = (int) ((temp -fracpart +0.05)* 10.0); | |
| 1037 /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ | |
| 1038 /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ | |
| 1039 fconvert[fplace++] = | |
| 1040 (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; | |
| 1041 } while(fracpart && (fplace < 311)); | |
| 1042 if (fplace == 311) fplace--; | |
| 1043 } | |
| 1044 fconvert[fplace] = 0; | |
| 1045 | |
| 1046 /* -1 for decimal point, another -1 if we are printing a sign */ | |
| 1047 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); | |
| 1048 zpadlen = max - fplace; | |
| 1049 if (zpadlen < 0) zpadlen = 0; | |
| 1050 if (padlen < 0) | |
| 1051 padlen = 0; | |
| 1052 if (flags & DP_F_MINUS) | |
| 1053 padlen = -padlen; /* Left Justifty */ | |
| 1054 | |
| 1055 if ((flags & DP_F_ZERO) && (padlen > 0)) { | |
| 1056 if (signvalue) { | |
| 1057 dopr_outch (buffer, currlen, maxlen, signvalue); | |
| 1058 --padlen; | |
| 1059 signvalue = 0; | |
| 1060 } | |
| 1061 while (padlen > 0) { | |
| 1062 dopr_outch (buffer, currlen, maxlen, '0'); | |
| 1063 --padlen; | |
| 1064 } | |
| 1065 } | |
| 1066 while (padlen > 0) { | |
| 1067 dopr_outch (buffer, currlen, maxlen, ' '); | |
| 1068 --padlen; | |
| 1069 } | |
| 1070 if (signvalue) | |
| 1071 dopr_outch (buffer, currlen, maxlen, signvalue); | |
| 1072 | |
| 1073 while (iplace > 0) | |
| 1074 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); | |
| 1075 | |
| 1076 #ifdef DEBUG_SNPRINTF | |
| 1077 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); | |
| 1078 #endif | |
| 1079 | |
| 1080 /* | |
| 1081 * Decimal point. This should probably use locale to find the correct | |
| 1082 * char to print out. | |
| 1083 */ | |
| 1084 if (max > 0) { | |
| 1085 dopr_outch (buffer, currlen, maxlen, '.'); | |
| 1086 | |
| 1087 while (zpadlen > 0) { | |
| 1088 dopr_outch (buffer, currlen, maxlen, '0'); | |
| 1089 --zpadlen; | |
| 1090 } | |
| 1091 | |
| 1092 while (fplace > 0) | |
| 1093 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace])
; | |
| 1094 } | |
| 1095 | |
| 1096 while (padlen < 0) { | |
| 1097 dopr_outch (buffer, currlen, maxlen, ' '); | |
| 1098 ++padlen; | |
| 1099 } | |
| 1100 } | |
| 1101 | |
| 1102 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) | |
| 1103 { | |
| 1104 if (*currlen < maxlen) { | |
| 1105 buffer[(*currlen)] = c; | |
| 1106 } | |
| 1107 (*currlen)++; | |
| 1108 } | |
| 1109 | |
| 1110 static struct pr_chunk *new_chunk(void) { | |
| 1111 struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chun
k)); | |
| 1112 | |
| 1113 if (!new_c) | |
| 1114 return NULL; | |
| 1115 | |
| 1116 new_c->type = 0; | |
| 1117 new_c->num = 0; | |
| 1118 new_c->min = 0; | |
| 1119 new_c->min_star = NULL; | |
| 1120 new_c->max = -1; | |
| 1121 new_c->max_star = NULL; | |
| 1122 new_c->flags = 0; | |
| 1123 new_c->cflags = 0; | |
| 1124 new_c->start = 0; | |
| 1125 new_c->len = 0; | |
| 1126 new_c->value = 0; | |
| 1127 new_c->fvalue = 0; | |
| 1128 new_c->strvalue = NULL; | |
| 1129 new_c->pnum = NULL; | |
| 1130 new_c->next = NULL; | |
| 1131 | |
| 1132 return new_c; | |
| 1133 } | |
| 1134 | |
| 1135 static int add_cnk_list_entry(struct pr_chunk_x **list, | |
| 1136 int max_num, struct pr_chunk *chunk) { | |
| 1137 struct pr_chunk_x *l; | |
| 1138 struct pr_chunk **c; | |
| 1139 int max; | |
| 1140 int cnum; | |
| 1141 int i, pos; | |
| 1142 | |
| 1143 if (chunk->num > max_num) { | |
| 1144 max = chunk->num; | |
| 1145 | |
| 1146 if (*list == NULL) { | |
| 1147 l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x
) * max); | |
| 1148 pos = 0; | |
| 1149 } else { | |
| 1150 l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr
_chunk_x) * max); | |
| 1151 pos = max_num; | |
| 1152 } | |
| 1153 if (l == NULL) { | |
| 1154 for (i = 0; i < max; i++) { | |
| 1155 if ((*list)[i].chunks) free((*list)[i].chunks); | |
| 1156 } | |
| 1157 return 0; | |
| 1158 } | |
| 1159 for (i = pos; i < max; i++) { | |
| 1160 l[i].chunks = NULL; | |
| 1161 l[i].num = 0; | |
| 1162 } | |
| 1163 } else { | |
| 1164 l = *list; | |
| 1165 max = max_num; | |
| 1166 } | |
| 1167 | |
| 1168 i = chunk->num - 1; | |
| 1169 cnum = l[i].num + 1; | |
| 1170 if (l[i].chunks == NULL) { | |
| 1171 c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum)
; | |
| 1172 } else { | |
| 1173 c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_ch
unk *) * cnum); | |
| 1174 } | |
| 1175 if (c == NULL) { | |
| 1176 for (i = 0; i < max; i++) { | |
| 1177 if (l[i].chunks) free(l[i].chunks); | |
| 1178 } | |
| 1179 return 0; | |
| 1180 } | |
| 1181 c[l[i].num] = chunk; | |
| 1182 l[i].chunks = c; | |
| 1183 l[i].num = cnum; | |
| 1184 | |
| 1185 *list = l; | |
| 1186 return max; | |
| 1187 } | |
| 1188 | |
| 1189 int vsnprintf (char *str, size_t count, const char *fmt, va_list args) | |
| 1190 { | |
| 1191 return dopr(str, count, fmt, args); | |
| 1192 } | |
| 1193 #endif | |
| 1194 | |
| 1195 /* yes this really must be a ||. Don't muck with this (tridge) | |
| 1196 * | |
| 1197 * The logic for these two is that we need our own definition if the | |
| 1198 * OS *either* has no definition of *sprintf, or if it does have one | |
| 1199 * that doesn't work properly according to the autoconf test. | |
| 1200 */ | |
| 1201 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF) | |
| 1202 int snprintf(char *str,size_t count,const char *fmt,...) | |
| 1203 { | |
| 1204 size_t ret; | |
| 1205 va_list ap; | |
| 1206 | |
| 1207 va_start(ap, fmt); | |
| 1208 ret = vsnprintf(str, count, fmt, ap); | |
| 1209 va_end(ap); | |
| 1210 return ret; | |
| 1211 } | |
| 1212 #endif | |
| 1213 | |
| 1214 #ifndef HAVE_C99_VSNPRINTF | |
| 1215 int printf(const char *fmt, ...) | |
| 1216 { | |
| 1217 va_list ap; | |
| 1218 int ret; | |
| 1219 char *s; | |
| 1220 | |
| 1221 s = NULL; | |
| 1222 va_start(ap, fmt); | |
| 1223 ret = vasprintf(&s, fmt, ap); | |
| 1224 va_end(ap); | |
| 1225 | |
| 1226 if (s) { | |
| 1227 fwrite(s, 1, strlen(s), stdout); | |
| 1228 } | |
| 1229 free(s); | |
| 1230 | |
| 1231 return ret; | |
| 1232 } | |
| 1233 #endif | |
| 1234 | |
| 1235 #ifndef HAVE_C99_VSNPRINTF | |
| 1236 int fprintf(FILE *stream, const char *fmt, ...) | |
| 1237 { | |
| 1238 va_list ap; | |
| 1239 int ret; | |
| 1240 char *s; | |
| 1241 | |
| 1242 s = NULL; | |
| 1243 va_start(ap, fmt); | |
| 1244 ret = vasprintf(&s, fmt, ap); | |
| 1245 va_end(ap); | |
| 1246 | |
| 1247 if (s) { | |
| 1248 fwrite(s, 1, strlen(s), stream); | |
| 1249 } | |
| 1250 free(s); | |
| 1251 | |
| 1252 return ret; | |
| 1253 } | |
| 1254 #endif | |
| 1255 | |
| 1256 #endif | |
| 1257 | |
| 1258 #ifndef HAVE_VASPRINTF | |
| 1259 int vasprintf(char **ptr, const char *format, va_list ap) | |
| 1260 { | |
| 1261 int ret; | |
| 1262 va_list ap2; | |
| 1263 | |
| 1264 VA_COPY(ap2, ap); | |
| 1265 ret = vsnprintf(NULL, 0, format, ap2); | |
| 1266 va_end(ap2); | |
| 1267 if (ret < 0) return ret; | |
| 1268 | |
| 1269 (*ptr) = (char *)malloc(ret+1); | |
| 1270 if (!*ptr) return -1; | |
| 1271 | |
| 1272 VA_COPY(ap2, ap); | |
| 1273 ret = vsnprintf(*ptr, ret+1, format, ap2); | |
| 1274 va_end(ap2); | |
| 1275 | |
| 1276 return ret; | |
| 1277 } | |
| 1278 #endif | |
| 1279 | |
| 1280 | |
| 1281 #ifndef HAVE_ASPRINTF | |
| 1282 int asprintf(char **ptr, const char *format, ...) | |
| 1283 { | |
| 1284 va_list ap; | |
| 1285 int ret; | |
| 1286 | |
| 1287 *ptr = NULL; | |
| 1288 va_start(ap, format); | |
| 1289 ret = vasprintf(ptr, format, ap); | |
| 1290 va_end(ap); | |
| 1291 | |
| 1292 return ret; | |
| 1293 } | |
| 1294 #endif | |
| 1295 | |
| 1296 #ifdef TEST_SNPRINTF | |
| 1297 | |
| 1298 int sprintf(char *str,const char *fmt,...); | |
| 1299 int printf(const char *fmt,...); | |
| 1300 | |
| 1301 int main (void) | |
| 1302 { | |
| 1303 char buf1[1024]; | |
| 1304 char buf2[1024]; | |
| 1305 char *buf3; | |
| 1306 char *fp_fmt[] = { | |
| 1307 "%1.1f", | |
| 1308 "%-1.5f", | |
| 1309 "%1.5f", | |
| 1310 "%123.9f", | |
| 1311 "%10.5f", | |
| 1312 "% 10.5f", | |
| 1313 "%+22.9f", | |
| 1314 "%+4.9f", | |
| 1315 "%01.3f", | |
| 1316 "%4f", | |
| 1317 "%3.1f", | |
| 1318 "%3.2f", | |
| 1319 "%.0f", | |
| 1320 "%f", | |
| 1321 "%-8.8f", | |
| 1322 "%-9.9f", | |
| 1323 NULL | |
| 1324 }; | |
| 1325 double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 2
03.9, 0.96, 0.996, | |
| 1326 0.9996, 1.996, 4.136, 5.030201, 0.00205, | |
| 1327 /* END LIST */ 0}; | |
| 1328 char *int_fmt[] = { | |
| 1329 "%-1.5d", | |
| 1330 "%1.5d", | |
| 1331 "%123.9d", | |
| 1332 "%5.5d", | |
| 1333 "%10.5d", | |
| 1334 "% 10.5d", | |
| 1335 "%+22.33d", | |
| 1336 "%01.3d", | |
| 1337 "%4d", | |
| 1338 "%d", | |
| 1339 NULL | |
| 1340 }; | |
| 1341 long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0}; | |
| 1342 char *str_fmt[] = { | |
| 1343 "%10.5s", | |
| 1344 "%-10.5s", | |
| 1345 "%5.10s", | |
| 1346 "%-5.10s", | |
| 1347 "%10.1s", | |
| 1348 "%0.10s", | |
| 1349 "%10.0s", | |
| 1350 "%1.10s", | |
| 1351 "%s", | |
| 1352 "%.1s", | |
| 1353 "%.10s", | |
| 1354 "%10s", | |
| 1355 NULL | |
| 1356 }; | |
| 1357 char *str_vals[] = {"hello", "a", "", "a longer string", NULL}; | |
| 1358 #ifdef HAVE_LONG_LONG | |
| 1359 char *ll_fmt[] = { | |
| 1360 "%llu", | |
| 1361 NULL | |
| 1362 }; | |
| 1363 LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 12800618614000000
0LL, 0}; | |
| 1364 #endif | |
| 1365 int x, y; | |
| 1366 int fail = 0; | |
| 1367 int num = 0; | |
| 1368 int l1, l2; | |
| 1369 char *ss_fmt[] = { | |
| 1370 "%zd", | |
| 1371 "%zu", | |
| 1372 NULL | |
| 1373 }; | |
| 1374 size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0}; | |
| 1375 | |
| 1376 printf ("Testing snprintf format codes against system sprintf...\n"); | |
| 1377 | |
| 1378 for (x = 0; fp_fmt[x] ; x++) { | |
| 1379 for (y = 0; fp_nums[y] != 0 ; y++) { | |
| 1380 buf1[0] = buf2[0] = '\0'; | |
| 1381 l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y])
; | |
| 1382 l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]); | |
| 1383 buf1[1023] = buf2[1023] = '\0'; | |
| 1384 if (strcmp (buf1, buf2) || (l1 != l2)) { | |
| 1385 printf("snprintf doesn't match Format: %s\n\tsnp
rintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
| 1386 fp_fmt[x], l1, buf1, l2, buf2); | |
| 1387 fail++; | |
| 1388 } | |
| 1389 num++; | |
| 1390 } | |
| 1391 } | |
| 1392 | |
| 1393 for (x = 0; int_fmt[x] ; x++) { | |
| 1394 for (y = 0; int_nums[y] != 0 ; y++) { | |
| 1395 buf1[0] = buf2[0] = '\0'; | |
| 1396 l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y
]); | |
| 1397 l2 = sprintf (buf2, int_fmt[x], int_nums[y]); | |
| 1398 buf1[1023] = buf2[1023] = '\0'; | |
| 1399 if (strcmp (buf1, buf2) || (l1 != l2)) { | |
| 1400 printf("snprintf doesn't match Format: %s\n\tsnp
rintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
| 1401 int_fmt[x], l1, buf1, l2, buf2); | |
| 1402 fail++; | |
| 1403 } | |
| 1404 num++; | |
| 1405 } | |
| 1406 } | |
| 1407 | |
| 1408 for (x = 0; str_fmt[x] ; x++) { | |
| 1409 for (y = 0; str_vals[y] != 0 ; y++) { | |
| 1410 buf1[0] = buf2[0] = '\0'; | |
| 1411 l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y
]); | |
| 1412 l2 = sprintf (buf2, str_fmt[x], str_vals[y]); | |
| 1413 buf1[1023] = buf2[1023] = '\0'; | |
| 1414 if (strcmp (buf1, buf2) || (l1 != l2)) { | |
| 1415 printf("snprintf doesn't match Format: %s\n\tsnp
rintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
| 1416 str_fmt[x], l1, buf1, l2, buf2); | |
| 1417 fail++; | |
| 1418 } | |
| 1419 num++; | |
| 1420 } | |
| 1421 } | |
| 1422 | |
| 1423 #ifdef HAVE_LONG_LONG | |
| 1424 for (x = 0; ll_fmt[x] ; x++) { | |
| 1425 for (y = 0; ll_nums[y] != 0 ; y++) { | |
| 1426 buf1[0] = buf2[0] = '\0'; | |
| 1427 l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y])
; | |
| 1428 l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]); | |
| 1429 buf1[1023] = buf2[1023] = '\0'; | |
| 1430 if (strcmp (buf1, buf2) || (l1 != l2)) { | |
| 1431 printf("snprintf doesn't match Format: %s\n\tsnp
rintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
| 1432 ll_fmt[x], l1, buf1, l2, buf2); | |
| 1433 fail++; | |
| 1434 } | |
| 1435 num++; | |
| 1436 } | |
| 1437 } | |
| 1438 #endif | |
| 1439 | |
| 1440 #define BUFSZ 2048 | |
| 1441 | |
| 1442 buf1[0] = buf2[0] = '\0'; | |
| 1443 if ((buf3 = malloc(BUFSZ)) == NULL) { | |
| 1444 fail++; | |
| 1445 } else { | |
| 1446 num++; | |
| 1447 memset(buf3, 'a', BUFSZ); | |
| 1448 snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3); | |
| 1449 buf1[1023] = '\0'; | |
| 1450 if (strcmp(buf1, "a") != 0) { | |
| 1451 printf("length limit buf1 '%s' expected 'a'\n", buf1); | |
| 1452 fail++; | |
| 1453 } | |
| 1454 } | |
| 1455 | |
| 1456 buf1[0] = buf2[0] = '\0'; | |
| 1457 l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos te
st", 12.3456, 9); | |
| 1458 l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9
); | |
| 1459 buf1[1023] = buf2[1023] = '\0'; | |
| 1460 if (strcmp(buf1, buf2) || (l1 != l2)) { | |
| 1461 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]
\n\t sprintf(%d) = [%s]\n", | |
| 1462 "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2); | |
| 1463 fail++; | |
| 1464 } | |
| 1465 | |
| 1466 buf1[0] = buf2[0] = '\0'; | |
| 1467 l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos te
st", 12.3456, 9); | |
| 1468 l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9
); | |
| 1469 buf1[1023] = buf2[1023] = '\0'; | |
| 1470 if (strcmp(buf1, buf2)) { | |
| 1471 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]
\n\t sprintf(%d) = [%s]\n", | |
| 1472 "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2); | |
| 1473 fail++; | |
| 1474 } | |
| 1475 | |
| 1476 for (x = 0; ss_fmt[x] ; x++) { | |
| 1477 for (y = 0; ss_nums[y] != 0 ; y++) { | |
| 1478 buf1[0] = buf2[0] = '\0'; | |
| 1479 l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y])
; | |
| 1480 l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]); | |
| 1481 buf1[1023] = buf2[1023] = '\0'; | |
| 1482 if (strcmp (buf1, buf2) || (l1 != l2)) { | |
| 1483 printf("snprintf doesn't match Format: %s\n\tsnp
rintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", | |
| 1484 ss_fmt[x], l1, buf1, l2, buf2); | |
| 1485 fail++; | |
| 1486 } | |
| 1487 num++; | |
| 1488 } | |
| 1489 } | |
| 1490 #if 0 | |
| 1491 buf1[0] = buf2[0] = '\0'; | |
| 1492 l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890); | |
| 1493 l2 = sprintf(buf2, "%lld", (LLONG)1234567890); | |
| 1494 buf1[1023] = buf2[1023] = '\0'; | |
| 1495 if (strcmp(buf1, buf2)) { | |
| 1496 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]
\n\t sprintf(%d) = [%s]\n", | |
| 1497 "%lld", l1, buf1, l2, buf2); | |
| 1498 fail++; | |
| 1499 } | |
| 1500 | |
| 1501 buf1[0] = buf2[0] = '\0'; | |
| 1502 l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123); | |
| 1503 l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123); | |
| 1504 buf1[1023] = buf2[1023] = '\0'; | |
| 1505 if (strcmp(buf1, buf2)) { | |
| 1506 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]
\n\t sprintf(%d) = [%s]\n", | |
| 1507 "%Lf", l1, buf1, l2, buf2); | |
| 1508 fail++; | |
| 1509 } | |
| 1510 #endif | |
| 1511 printf ("%d tests failed out of %d.\n", fail, num); | |
| 1512 | |
| 1513 printf("seeing how many digits we support\n"); | |
| 1514 { | |
| 1515 double v0 = 0.12345678901234567890123456789012345678901; | |
| 1516 for (x=0; x<100; x++) { | |
| 1517 double p = pow(10, x); | |
| 1518 double r = v0*p; | |
| 1519 snprintf(buf1, sizeof(buf1), "%1.1f", r); | |
| 1520 sprintf(buf2, "%1.1f", r); | |
| 1521 if (strcmp(buf1, buf2)) { | |
| 1522 printf("we seem to support %d digits\n", x-1); | |
| 1523 break; | |
| 1524 } | |
| 1525 } | |
| 1526 } | |
| 1527 | |
| 1528 return 0; | |
| 1529 } | |
| 1530 #endif /* TEST_SNPRINTF */ | |
| OLD | NEW |