1 /* 2 * Copyright Patrick Powell 1995 3 * This code is based on code written by Patrick Powell (papowell@astart.com) 4 * It may be used for any purpose as long as this notice remains intact 5 * on all source code distributions 6 */ 7 8 /************************************************************** 9 * Original: 10 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 11 * A bombproof version of doprnt (dopr) included. 12 * Sigh. This sort of thing is always nasty do deal with. Note that 13 * the version here does not include floating point... 14 * 15 * snprintf() is used instead of sprintf() as it does limit checks 16 * for string length. This covers a nasty loophole. 17 * 18 * The other functions are there to prevent NULL pointers from 19 * causing nast effects. 20 * 21 * More Recently: 22 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 23 * This was ugly. It is still ugly. I opted out of floating point 24 * numbers, but the formatter understands just about everything 25 * from the normal C string format, at least as far as I can tell from 26 * the Solaris 2.5 printf(3S) man page. 27 * 28 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 29 * Ok, added some minimal floating point support, which means this 30 * probably requires libm on most operating systems. Don't yet 31 * support the exponent (e,E) and sigfig (g,G). Also, fmtint() 32 * was pretty badly broken, it just wasn't being exercised in ways 33 * which showed it, so that's been fixed. Also, formated the code 34 * to mutt conventions, and removed dead code left over from the 35 * original. Also, there is now a builtin-test, just compile with: 36 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm 37 * and run snprintf for results. 38 * 39 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i 40 * The PGP code was using unsigned hexadecimal formats. 41 * Unfortunately, unsigned formats simply didn't work. 42 * 43 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 44 * The original code assumed that both snprintf() and vsnprintf() were 45 * missing. Some systems only have snprintf() but not vsnprintf(), so 46 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. 47 * 48 * Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH 49 * Welcome to the world of %lld and %qd support. With other 50 * long long support. This is needed for sftp-server to work 51 * right. 52 * 53 * Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH 54 * Removed all hint of VARARGS stuff and banished it to the void, 55 * and did a bit of KNF style work to make things a bit more 56 * acceptable. Consider stealing from mutt or enlightenment. 57 **************************************************************/ 58 59 #include "includes.h" 60 61 RCSID("$Id: bsd-snprintf.c,v 1.7 2003/05/18 14:13:39 djm Exp $"); 62 63 #if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */ 64 # undef HAVE_SNPRINTF 65 # undef HAVE_VSNPRINTF 66 #endif 67 68 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) 69 70 static void 71 dopr(char *buffer, size_t maxlen, const char *format, va_list args); 72 73 static void 74 fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, 75 int min, int max); 76 77 static void 78 fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base, 79 int min, int max, int flags); 80 81 static void 82 fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue, 83 int min, int max, int flags); 84 85 static void 86 dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); 87 88 /* 89 * dopr(): poor man's version of doprintf 90 */ 91 92 /* format read states */ 93 #define DP_S_DEFAULT 0 94 #define DP_S_FLAGS 1 95 #define DP_S_MIN 2 96 #define DP_S_DOT 3 97 #define DP_S_MAX 4 98 #define DP_S_MOD 5 99 #define DP_S_CONV 6 100 #define DP_S_DONE 7 101 102 /* format flags - Bits */ 103 #define DP_F_MINUS (1 << 0) 104 #define DP_F_PLUS (1 << 1) 105 #define DP_F_SPACE (1 << 2) 106 #define DP_F_NUM (1 << 3) 107 #define DP_F_ZERO (1 << 4) 108 #define DP_F_UP (1 << 5) 109 #define DP_F_UNSIGNED (1 << 6) 110 111 /* Conversion Flags */ 112 #define DP_C_SHORT 1 113 #define DP_C_LONG 2 114 #define DP_C_LDOUBLE 3 115 #define DP_C_LONG_LONG 4 116 117 #define char_to_int(p) (p - '0') 118 #define abs_val(p) (p < 0 ? -p : p) 119 120 121 static void 122 dopr(char *buffer, size_t maxlen, const char *format, va_list args) 123 { 124 char *strvalue, ch; 125 long value; 126 long double fvalue; 127 int min = 0, max = -1, state = DP_S_DEFAULT, flags = 0, cflags = 0; 128 size_t currlen = 0; 129 130 ch = *format++; 131 132 while (state != DP_S_DONE) { 133 if ((ch == '\0') || (currlen >= maxlen)) 134 state = DP_S_DONE; 135 136 switch(state) { 137 case DP_S_DEFAULT: 138 if (ch == '%') 139 state = DP_S_FLAGS; 140 else 141 dopr_outch(buffer, &currlen, maxlen, ch); 142 ch = *format++; 143 break; 144 case DP_S_FLAGS: 145 switch (ch) { 146 case '-': 147 flags |= DP_F_MINUS; 148 ch = *format++; 149 break; 150 case '+': 151 flags |= DP_F_PLUS; 152 ch = *format++; 153 break; 154 case ' ': 155 flags |= DP_F_SPACE; 156 ch = *format++; 157 break; 158 case '#': 159 flags |= DP_F_NUM; 160 ch = *format++; 161 break; 162 case '0': 163 flags |= DP_F_ZERO; 164 ch = *format++; 165 break; 166 default: 167 state = DP_S_MIN; 168 break; 169 } 170 break; 171 case DP_S_MIN: 172 if (isdigit((unsigned char)ch)) { 173 min = 10 * min + char_to_int (ch); 174 ch = *format++; 175 } else if (ch == '*') { 176 min = va_arg (args, int); 177 ch = *format++; 178 state = DP_S_DOT; 179 } else 180 state = DP_S_DOT; 181 break; 182 case DP_S_DOT: 183 if (ch == '.') { 184 state = DP_S_MAX; 185 ch = *format++; 186 } else 187 state = DP_S_MOD; 188 break; 189 case DP_S_MAX: 190 if (isdigit((unsigned char)ch)) { 191 if (max < 0) 192 max = 0; 193 max = 10 * max + char_to_int(ch); 194 ch = *format++; 195 } else if (ch == '*') { 196 max = va_arg (args, int); 197 ch = *format++; 198 state = DP_S_MOD; 199 } else 200 state = DP_S_MOD; 201 break; 202 case DP_S_MOD: 203 switch (ch) { 204 case 'h': 205 cflags = DP_C_SHORT; 206 ch = *format++; 207 break; 208 case 'l': 209 cflags = DP_C_LONG; 210 ch = *format++; 211 if (ch == 'l') { 212 cflags = DP_C_LONG_LONG; 213 ch = *format++; 214 } 215 break; 216 case 'q': 217 cflags = DP_C_LONG_LONG; 218 ch = *format++; 219 break; 220 case 'L': 221 cflags = DP_C_LDOUBLE; 222 ch = *format++; 223 break; 224 default: 225 break; 226 } 227 state = DP_S_CONV; 228 break; 229 case DP_S_CONV: 230 switch (ch) { 231 case 'd': 232 case 'i': 233 if (cflags == DP_C_SHORT) 234 value = va_arg(args, int); 235 else if (cflags == DP_C_LONG) 236 value = va_arg(args, long int); 237 else if (cflags == DP_C_LONG_LONG) 238 value = va_arg (args, long long); 239 else 240 value = va_arg (args, int); 241 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags); 242 break; 243 case 'o': 244 flags |= DP_F_UNSIGNED; 245 if (cflags == DP_C_SHORT) 246 value = va_arg(args, unsigned int); 247 else if (cflags == DP_C_LONG) 248 value = va_arg(args, unsigned long int); 249 else if (cflags == DP_C_LONG_LONG) 250 value = va_arg(args, unsigned long long); 251 else 252 value = va_arg(args, unsigned int); 253 fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags); 254 break; 255 case 'u': 256 flags |= DP_F_UNSIGNED; 257 if (cflags == DP_C_SHORT) 258 value = va_arg(args, unsigned int); 259 else if (cflags == DP_C_LONG) 260 value = va_arg(args, unsigned long int); 261 else if (cflags == DP_C_LONG_LONG) 262 value = va_arg(args, unsigned long long); 263 else 264 value = va_arg(args, unsigned int); 265 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 266 break; 267 case 'X': 268 flags |= DP_F_UP; 269 case 'x': 270 flags |= DP_F_UNSIGNED; 271 if (cflags == DP_C_SHORT) 272 value = va_arg(args, unsigned int); 273 else if (cflags == DP_C_LONG) 274 value = va_arg(args, unsigned long int); 275 else if (cflags == DP_C_LONG_LONG) 276 value = va_arg(args, unsigned long long); 277 else 278 value = va_arg(args, unsigned int); 279 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags); 280 break; 281 case 'f': 282 if (cflags == DP_C_LDOUBLE) 283 fvalue = va_arg(args, long double); 284 else 285 fvalue = va_arg(args, double); 286 /* um, floating point? */ 287 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags); 288 break; 289 case 'E': 290 flags |= DP_F_UP; 291 case 'e': 292 if (cflags == DP_C_LDOUBLE) 293 fvalue = va_arg(args, long double); 294 else 295 fvalue = va_arg(args, double); 296 break; 297 case 'G': 298 flags |= DP_F_UP; 299 case 'g': 300 if (cflags == DP_C_LDOUBLE) 301 fvalue = va_arg(args, long double); 302 else 303 fvalue = va_arg(args, double); 304 break; 305 case 'c': 306 dopr_outch(buffer, &currlen, maxlen, va_arg(args, int)); 307 break; 308 case 's': 309 strvalue = va_arg(args, char *); 310 if (max < 0) 311 max = maxlen; /* ie, no max */ 312 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max); 313 break; 314 case 'p': 315 strvalue = va_arg(args, void *); 316 fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); 317 break; 318 case 'n': 319 if (cflags == DP_C_SHORT) { 320 short int *num; 321 num = va_arg(args, short int *); 322 *num = currlen; 323 } else if (cflags == DP_C_LONG) { 324 long int *num; 325 num = va_arg(args, long int *); 326 *num = currlen; 327 } else if (cflags == DP_C_LONG_LONG) { 328 long long *num; 329 num = va_arg(args, long long *); 330 *num = currlen; 331 } else { 332 int *num; 333 num = va_arg(args, int *); 334 *num = currlen; 335 } 336 break; 337 case '%': 338 dopr_outch(buffer, &currlen, maxlen, ch); 339 break; 340 case 'w': /* not supported yet, treat as next char */ 341 ch = *format++; 342 break; 343 default: /* Unknown, skip */ 344 break; 345 } 346 ch = *format++; 347 state = DP_S_DEFAULT; 348 flags = cflags = min = 0; 349 max = -1; 350 break; 351 case DP_S_DONE: 352 break; 353 default: /* hmm? */ 354 break; /* some picky compilers need this */ 355 } 356 } 357 if (currlen < maxlen - 1) 358 buffer[currlen] = '\0'; 359 else 360 buffer[maxlen - 1] = '\0'; 361 } 362 363 static void 364 fmtstr(char *buffer, size_t *currlen, size_t maxlen, 365 char *value, int flags, int min, int max) 366 { 367 int cnt = 0, padlen, strln; /* amount to pad */ 368 369 if (value == 0) 370 value = "<NULL>"; 371 372 for (strln = 0; value[strln]; ++strln); /* strlen */ 373 padlen = min - strln; 374 if (padlen < 0) 375 padlen = 0; 376 if (flags & DP_F_MINUS) 377 padlen = -padlen; /* Left Justify */ 378 379 while ((padlen > 0) && (cnt < max)) { 380 dopr_outch(buffer, currlen, maxlen, ' '); 381 --padlen; 382 ++cnt; 383 } 384 while (*value && (cnt < max)) { 385 dopr_outch(buffer, currlen, maxlen, *value++); 386 ++cnt; 387 } 388 while ((padlen < 0) && (cnt < max)) { 389 dopr_outch(buffer, currlen, maxlen, ' '); 390 ++padlen; 391 ++cnt; 392 } 393 } 394 395 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ 396 397 static void 398 fmtint(char *buffer, size_t *currlen, size_t maxlen, 399 long value, int base, int min, int max, int flags) 400 { 401 unsigned long uvalue; 402 char convert[20]; 403 int signvalue = 0, place = 0, caps = 0; 404 int spadlen = 0; /* amount to space pad */ 405 int zpadlen = 0; /* amount to zero pad */ 406 407 if (max < 0) 408 max = 0; 409 410 uvalue = value; 411 412 if (!(flags & DP_F_UNSIGNED)) { 413 if (value < 0) { 414 signvalue = '-'; 415 uvalue = -value; 416 } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 417 signvalue = '+'; 418 else if (flags & DP_F_SPACE) 419 signvalue = ' '; 420 } 421 422 if (flags & DP_F_UP) 423 caps = 1; /* Should characters be upper case? */ 424 do { 425 convert[place++] = 426 (caps ? "0123456789ABCDEF" : "0123456789abcdef") 427 [uvalue % (unsigned)base]; 428 uvalue = (uvalue / (unsigned)base ); 429 } while (uvalue && (place < 20)); 430 if (place == 20) 431 place--; 432 convert[place] = 0; 433 434 zpadlen = max - place; 435 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); 436 if (zpadlen < 0) 437 zpadlen = 0; 438 if (spadlen < 0) 439 spadlen = 0; 440 if (flags & DP_F_ZERO) { 441 zpadlen = MAX(zpadlen, spadlen); 442 spadlen = 0; 443 } 444 if (flags & DP_F_MINUS) 445 spadlen = -spadlen; /* Left Justifty */ 446 447 /* Spaces */ 448 while (spadlen > 0) { 449 dopr_outch(buffer, currlen, maxlen, ' '); 450 --spadlen; 451 } 452 453 /* Sign */ 454 if (signvalue) 455 dopr_outch(buffer, currlen, maxlen, signvalue); 456 457 /* Zeros */ 458 if (zpadlen > 0) { 459 while (zpadlen > 0) { 460 dopr_outch(buffer, currlen, maxlen, '0'); 461 --zpadlen; 462 } 463 } 464 465 /* Digits */ 466 while (place > 0) 467 dopr_outch(buffer, currlen, maxlen, convert[--place]); 468 469 /* Left Justified spaces */ 470 while (spadlen < 0) { 471 dopr_outch (buffer, currlen, maxlen, ' '); 472 ++spadlen; 473 } 474 } 475 476 static long double 477 pow10(int exp) 478 { 479 long double result = 1; 480 481 while (exp) { 482 result *= 10; 483 exp--; 484 } 485 486 return result; 487 } 488 489 static long 490 round(long double value) 491 { 492 long intpart = value; 493 494 value -= intpart; 495 if (value >= 0.5) 496 intpart++; 497 498 return intpart; 499 } 500 501 static void 502 fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue, 503 int min, int max, int flags) 504 { 505 char iconvert[20], fconvert[20]; 506 int signvalue = 0, iplace = 0, fplace = 0; 507 int padlen = 0; /* amount to pad */ 508 int zpadlen = 0, caps = 0; 509 long intpart, fracpart; 510 long double ufvalue; 511 512 /* 513 * AIX manpage says the default is 0, but Solaris says the default 514 * is 6, and sprintf on AIX defaults to 6 515 */ 516 if (max < 0) 517 max = 6; 518 519 ufvalue = abs_val(fvalue); 520 521 if (fvalue < 0) 522 signvalue = '-'; 523 else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 524 signvalue = '+'; 525 else if (flags & DP_F_SPACE) 526 signvalue = ' '; 527 528 intpart = ufvalue; 529 530 /* 531 * Sorry, we only support 9 digits past the decimal because of our 532 * conversion method 533 */ 534 if (max > 9) 535 max = 9; 536 537 /* We "cheat" by converting the fractional part to integer by 538 * multiplying by a factor of 10 539 */ 540 fracpart = round((pow10 (max)) * (ufvalue - intpart)); 541 542 if (fracpart >= pow10 (max)) { 543 intpart++; 544 fracpart -= pow10 (max); 545 } 546 547 /* Convert integer part */ 548 do { 549 iconvert[iplace++] = 550 (caps ? "0123456789ABCDEF" : "0123456789abcdef") 551 [intpart % 10]; 552 intpart = (intpart / 10); 553 } while(intpart && (iplace < 20)); 554 if (iplace == 20) 555 iplace--; 556 iconvert[iplace] = 0; 557 558 /* Convert fractional part */ 559 do { 560 fconvert[fplace++] = 561 (caps ? "0123456789ABCDEF" : "0123456789abcdef") 562 [fracpart % 10]; 563 fracpart = (fracpart / 10); 564 } while(fracpart && (fplace < 20)); 565 if (fplace == 20) 566 fplace--; 567 fconvert[fplace] = 0; 568 569 /* -1 for decimal point, another -1 if we are printing a sign */ 570 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 571 zpadlen = max - fplace; 572 if (zpadlen < 0) 573 zpadlen = 0; 574 if (padlen < 0) 575 padlen = 0; 576 if (flags & DP_F_MINUS) 577 padlen = -padlen; /* Left Justifty */ 578 579 if ((flags & DP_F_ZERO) && (padlen > 0)) { 580 if (signvalue) { 581 dopr_outch(buffer, currlen, maxlen, signvalue); 582 --padlen; 583 signvalue = 0; 584 } 585 while (padlen > 0) { 586 dopr_outch(buffer, currlen, maxlen, '0'); 587 --padlen; 588 } 589 } 590 while (padlen > 0) { 591 dopr_outch(buffer, currlen, maxlen, ' '); 592 --padlen; 593 } 594 if (signvalue) 595 dopr_outch(buffer, currlen, maxlen, signvalue); 596 597 while (iplace > 0) 598 dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]); 599 600 /* 601 * Decimal point. This should probably use locale to find the 602 * correct char to print out. 603 */ 604 dopr_outch(buffer, currlen, maxlen, '.'); 605 606 while (fplace > 0) 607 dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]); 608 609 while (zpadlen > 0) { 610 dopr_outch(buffer, currlen, maxlen, '0'); 611 --zpadlen; 612 } 613 614 while (padlen < 0) { 615 dopr_outch(buffer, currlen, maxlen, ' '); 616 ++padlen; 617 } 618 } 619 620 static void 621 dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) 622 { 623 if (*currlen < maxlen) 624 buffer[(*currlen)++] = c; 625 } 626 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ 627 628 #ifndef HAVE_VSNPRINTF 629 int 630 vsnprintf(char *str, size_t count, const char *fmt, va_list args) 631 { 632 str[0] = 0; 633 dopr(str, count, fmt, args); 634 635 return(strlen(str)); 636 } 637 #endif /* !HAVE_VSNPRINTF */ 638 639 #ifndef HAVE_SNPRINTF 640 int 641 snprintf(char *str,size_t count,const char *fmt,...) 642 { 643 va_list ap; 644 645 va_start(ap, fmt); 646 (void) vsnprintf(str, count, fmt, ap); 647 va_end(ap); 648 649 return(strlen(str)); 650 } 651 652 #endif /* !HAVE_SNPRINTF */ 653