1 /* $NetBSD: printf.c,v 1.35 2011/03/15 23:11:49 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if !defined(BUILTIN) && !defined(SHELL) 35 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\ 36 The Regents of the University of California. All rights reserved."); 37 #endif 38 #endif 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95"; 43 #else 44 __RCSID("$NetBSD: printf.c,v 1.35 2011/03/15 23:11:49 christos Exp $"); 45 #endif 46 #endif /* not lint */ 47 48 #include <sys/types.h> 49 50 #include <ctype.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <inttypes.h> 54 #include <limits.h> 55 #include <locale.h> 56 #include <stdarg.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 62 #ifdef __GNUC__ 63 #define ESCAPE '\e' 64 #else 65 #define ESCAPE 033 66 #endif 67 68 static void conv_escape_str(char *, void (*)(int)); 69 static char *conv_escape(char *, char *); 70 static char *conv_expand(const char *); 71 static int getchr(void); 72 static double getdouble(void); 73 static int getwidth(void); 74 static intmax_t getintmax(void); 75 static uintmax_t getuintmax(void); 76 static char *getstr(void); 77 static char *mklong(const char *, int); 78 static void check_conversion(const char *, const char *); 79 static void usage(void); 80 81 static void b_count(int); 82 static void b_output(int); 83 static size_t b_length; 84 static char *b_fmt; 85 86 static int rval; 87 static char **gargv; 88 89 #ifdef BUILTIN /* csh builtin */ 90 #define main progprintf 91 #endif 92 93 #ifdef SHELL /* sh (aka ash) builtin */ 94 #define main printfcmd 95 #include "../../bin/sh/bltin/bltin.h" 96 #endif /* SHELL */ 97 98 #define PF(f, func) { \ 99 if (fieldwidth != -1) { \ 100 if (precision != -1) \ 101 error = printf(f, fieldwidth, precision, func); \ 102 else \ 103 error = printf(f, fieldwidth, func); \ 104 } else if (precision != -1) \ 105 error = printf(f, precision, func); \ 106 else \ 107 error = printf(f, func); \ 108 } 109 110 #define APF(cpp, f, func) { \ 111 if (fieldwidth != -1) { \ 112 if (precision != -1) \ 113 error = asprintf(cpp, f, fieldwidth, precision, func); \ 114 else \ 115 error = asprintf(cpp, f, fieldwidth, func); \ 116 } else if (precision != -1) \ 117 error = asprintf(cpp, f, precision, func); \ 118 else \ 119 error = asprintf(cpp, f, func); \ 120 } 121 122 #ifdef main 123 int main(int, char *[]); 124 #endif 125 int main(int argc, char *argv[]) 126 { 127 char *fmt, *start; 128 int fieldwidth, precision; 129 char nextch; 130 char *format; 131 int ch; 132 int error; 133 134 #if !defined(SHELL) && !defined(BUILTIN) 135 (void)setlocale (LC_ALL, ""); 136 #endif 137 138 while ((ch = getopt(argc, argv, "")) != -1) { 139 switch (ch) { 140 case '?': 141 default: 142 usage(); 143 return 1; 144 } 145 } 146 argc -= optind; 147 argv += optind; 148 149 if (argc < 1) { 150 usage(); 151 return 1; 152 } 153 154 format = *argv; 155 gargv = ++argv; 156 157 #define SKIP1 "#-+ 0'" 158 #define SKIP2 "0123456789" 159 do { 160 /* 161 * Basic algorithm is to scan the format string for conversion 162 * specifications -- once one is found, find out if the field 163 * width or precision is a '*'; if it is, gather up value. 164 * Note, format strings are reused as necessary to use up the 165 * provided arguments, arguments of zero/null string are 166 * provided to use up the format string. 167 */ 168 169 /* find next format specification */ 170 for (fmt = format; (ch = *fmt++) != '\0';) { 171 if (ch == '\\') { 172 char c_ch; 173 fmt = conv_escape(fmt, &c_ch); 174 putchar(c_ch); 175 continue; 176 } 177 if (ch != '%' || (*fmt == '%' && ++fmt)) { 178 (void)putchar(ch); 179 continue; 180 } 181 182 /* Ok - we've found a format specification, 183 Save its address for a later printf(). */ 184 start = fmt - 1; 185 186 /* skip to field width */ 187 fmt += strspn(fmt, SKIP1); 188 if (*fmt == '*') { 189 fmt++; 190 fieldwidth = getwidth(); 191 } else 192 fieldwidth = -1; 193 194 /* skip to possible '.', get following precision */ 195 fmt += strspn(fmt, SKIP2); 196 if (*fmt == '.') { 197 fmt++; 198 if (*fmt == '*') { 199 fmt++; 200 precision = getwidth(); 201 } else 202 precision = -1; 203 } else 204 precision = -1; 205 206 fmt += strspn(fmt, SKIP2); 207 208 ch = *fmt; 209 if (!ch) { 210 warnx("missing format character"); 211 return (1); 212 } 213 /* null terminate format string to we can use it 214 as an argument to printf. */ 215 nextch = fmt[1]; 216 fmt[1] = 0; 217 switch (ch) { 218 219 case 'B': { 220 const char *p = conv_expand(getstr()); 221 if (p == NULL) 222 goto out; 223 *fmt = 's'; 224 PF(start, p); 225 if (error < 0) 226 goto out; 227 break; 228 } 229 case 'b': { 230 /* There has to be a better way to do this, 231 * but the string we generate might have 232 * embedded nulls. */ 233 static char *a, *t; 234 char *cp = getstr(); 235 /* Free on entry in case shell longjumped out */ 236 if (a != NULL) 237 free(a); 238 a = NULL; 239 if (t != NULL) 240 free(t); 241 t = NULL; 242 /* Count number of bytes we want to output */ 243 b_length = 0; 244 conv_escape_str(cp, b_count); 245 t = malloc(b_length + 1); 246 if (t == NULL) 247 goto out; 248 (void)memset(t, 'x', b_length); 249 t[b_length] = 0; 250 /* Get printf to calculate the lengths */ 251 *fmt = 's'; 252 APF(&a, start, t); 253 if (error == -1) 254 goto out; 255 b_fmt = a; 256 /* Output leading spaces and data bytes */ 257 conv_escape_str(cp, b_output); 258 /* Add any trailing spaces */ 259 printf("%s", b_fmt); 260 break; 261 } 262 case 'c': { 263 char p = getchr(); 264 PF(start, p); 265 if (error < 0) 266 goto out; 267 break; 268 } 269 case 's': { 270 char *p = getstr(); 271 PF(start, p); 272 if (error < 0) 273 goto out; 274 break; 275 } 276 case 'd': 277 case 'i': { 278 intmax_t p = getintmax(); 279 char *f = mklong(start, ch); 280 PF(f, p); 281 if (error < 0) 282 goto out; 283 break; 284 } 285 case 'o': 286 case 'u': 287 case 'x': 288 case 'X': { 289 uintmax_t p = getuintmax(); 290 char *f = mklong(start, ch); 291 PF(f, p); 292 if (error < 0) 293 goto out; 294 break; 295 } 296 case 'e': 297 case 'E': 298 case 'f': 299 case 'g': 300 case 'G': { 301 double p = getdouble(); 302 PF(start, p); 303 if (error < 0) 304 goto out; 305 break; 306 } 307 default: 308 warnx("%s: invalid directive", start); 309 return 1; 310 } 311 *fmt++ = ch; 312 *fmt = nextch; 313 /* escape if a \c was encountered */ 314 if (rval & 0x100) 315 return rval & ~0x100; 316 } 317 } while (gargv != argv && *gargv); 318 319 return rval & ~0x100; 320 out: 321 warn("print failed"); 322 return 1; 323 } 324 325 /* helper functions for conv_escape_str */ 326 327 static void 328 /*ARGSUSED*/ 329 b_count(int ch) 330 { 331 b_length++; 332 } 333 334 /* Output one converted character for every 'x' in the 'format' */ 335 336 static void 337 b_output(int ch) 338 { 339 for (;;) { 340 switch (*b_fmt++) { 341 case 0: 342 b_fmt--; 343 return; 344 case ' ': 345 putchar(' '); 346 break; 347 default: 348 putchar(ch); 349 return; 350 } 351 } 352 } 353 354 355 /* 356 * Print SysV echo(1) style escape string 357 * Halts processing string if a \c escape is encountered. 358 */ 359 static void 360 conv_escape_str(char *str, void (*do_putchar)(int)) 361 { 362 int value; 363 int ch; 364 char c; 365 366 while ((ch = *str++) != '\0') { 367 if (ch != '\\') { 368 do_putchar(ch); 369 continue; 370 } 371 372 ch = *str++; 373 if (ch == 'c') { 374 /* \c as in SYSV echo - abort all processing.... */ 375 rval |= 0x100; 376 break; 377 } 378 379 /* 380 * %b string octal constants are not like those in C. 381 * They start with a \0, and are followed by 0, 1, 2, 382 * or 3 octal digits. 383 */ 384 if (ch == '0') { 385 int octnum = 0, i; 386 for (i = 0; i < 3; i++) { 387 if (!isdigit((unsigned char)*str) || *str > '7') 388 break; 389 octnum = (octnum << 3) | (*str++ - '0'); 390 } 391 do_putchar(octnum); 392 continue; 393 } 394 395 /* \[M][^|-]C as defined by vis(3) */ 396 if (ch == 'M' && *str == '-') { 397 do_putchar(0200 | str[1]); 398 str += 2; 399 continue; 400 } 401 if (ch == 'M' && *str == '^') { 402 str++; 403 value = 0200; 404 ch = '^'; 405 } else 406 value = 0; 407 if (ch == '^') { 408 ch = *str++; 409 if (ch == '?') 410 value |= 0177; 411 else 412 value |= ch & 037; 413 do_putchar(value); 414 continue; 415 } 416 417 /* Finally test for sequences valid in the format string */ 418 str = conv_escape(str - 1, &c); 419 do_putchar(c); 420 } 421 } 422 423 /* 424 * Print "standard" escape characters 425 */ 426 static char * 427 conv_escape(char *str, char *conv_ch) 428 { 429 int value; 430 int ch; 431 char num_buf[4], *num_end; 432 433 ch = *str++; 434 435 switch (ch) { 436 case '0': case '1': case '2': case '3': 437 case '4': case '5': case '6': case '7': 438 num_buf[0] = ch; 439 ch = str[0]; 440 num_buf[1] = ch; 441 num_buf[2] = ch ? str[1] : 0; 442 num_buf[3] = 0; 443 value = strtoul(num_buf, &num_end, 8); 444 str += num_end - (num_buf + 1); 445 break; 446 447 case 'x': 448 /* Hexadecimal character constants are not required to be 449 supported (by SuS v1) because there is no consistent 450 way to detect the end of the constant. 451 Supporting 2 byte constants is a compromise. */ 452 ch = str[0]; 453 num_buf[0] = ch; 454 num_buf[1] = ch ? str[1] : 0; 455 num_buf[2] = 0; 456 value = strtoul(num_buf, &num_end, 16); 457 str += num_end - num_buf; 458 break; 459 460 case '\\': value = '\\'; break; /* backslash */ 461 case '\'': value = '\''; break; /* single quote */ 462 case '"': value = '"'; break; /* double quote */ 463 case 'a': value = '\a'; break; /* alert */ 464 case 'b': value = '\b'; break; /* backspace */ 465 case 'e': value = ESCAPE; break; /* escape */ 466 case 'f': value = '\f'; break; /* form-feed */ 467 case 'n': value = '\n'; break; /* newline */ 468 case 'r': value = '\r'; break; /* carriage-return */ 469 case 't': value = '\t'; break; /* tab */ 470 case 'v': value = '\v'; break; /* vertical-tab */ 471 472 default: 473 warnx("unknown escape sequence `\\%c'", ch); 474 rval = 1; 475 value = ch; 476 break; 477 } 478 479 *conv_ch = value; 480 return str; 481 } 482 483 /* expand a string so that everything is printable */ 484 485 static char * 486 conv_expand(const char *str) 487 { 488 static char *conv_str; 489 char *cp; 490 int ch; 491 492 if (conv_str) 493 free(conv_str); 494 /* get a buffer that is definitely large enough.... */ 495 conv_str = malloc(4 * strlen(str) + 1); 496 if (!conv_str) 497 return NULL; 498 cp = conv_str; 499 500 while ((ch = *(const unsigned char *)str++) != '\0') { 501 switch (ch) { 502 /* Use C escapes for expected control characters */ 503 case '\\': ch = '\\'; break; /* backslash */ 504 case '\'': ch = '\''; break; /* single quote */ 505 case '"': ch = '"'; break; /* double quote */ 506 case '\a': ch = 'a'; break; /* alert */ 507 case '\b': ch = 'b'; break; /* backspace */ 508 case ESCAPE: ch = 'e'; break; /* escape */ 509 case '\f': ch = 'f'; break; /* form-feed */ 510 case '\n': ch = 'n'; break; /* newline */ 511 case '\r': ch = 'r'; break; /* carriage-return */ 512 case '\t': ch = 't'; break; /* tab */ 513 case '\v': ch = 'v'; break; /* vertical-tab */ 514 default: 515 /* Copy anything printable */ 516 if (isprint(ch)) { 517 *cp++ = ch; 518 continue; 519 } 520 /* Use vis(3) encodings for the rest */ 521 *cp++ = '\\'; 522 if (ch & 0200) { 523 *cp++ = 'M'; 524 ch &= ~0200; 525 } 526 if (ch == 0177) { 527 *cp++ = '^'; 528 *cp++ = '?'; 529 continue; 530 } 531 if (ch < 040) { 532 *cp++ = '^'; 533 *cp++ = ch | 0100; 534 continue; 535 } 536 *cp++ = '-'; 537 *cp++ = ch; 538 continue; 539 } 540 *cp++ = '\\'; 541 *cp++ = ch; 542 } 543 544 *cp = 0; 545 return conv_str; 546 } 547 548 static char * 549 mklong(const char *str, int ch) 550 { 551 static char copy[64]; 552 size_t len; 553 554 len = strlen(str) + 2; 555 if (len > sizeof copy) { 556 warnx("format %s too complex\n", str); 557 len = 4; 558 } 559 (void)memmove(copy, str, len - 3); 560 copy[len - 3] = 'j'; 561 copy[len - 2] = ch; 562 copy[len - 1] = '\0'; 563 return copy; 564 } 565 566 static int 567 getchr(void) 568 { 569 if (!*gargv) 570 return 0; 571 return (int)**gargv++; 572 } 573 574 static char * 575 getstr(void) 576 { 577 static char empty[] = ""; 578 if (!*gargv) 579 return empty; 580 return *gargv++; 581 } 582 583 static int 584 getwidth(void) 585 { 586 long val; 587 char *s, *ep; 588 589 s = *gargv; 590 if (!*gargv) 591 return (0); 592 gargv++; 593 594 errno = 0; 595 val = strtoul(s, &ep, 0); 596 check_conversion(s, ep); 597 598 /* Arbitrarily 'restrict' field widths to 1Mbyte */ 599 if (val < 0 || val > 1 << 20) { 600 warnx("%s: invalid field width", s); 601 return 0; 602 } 603 604 return val; 605 } 606 607 static intmax_t 608 getintmax(void) 609 { 610 intmax_t val; 611 char *cp, *ep; 612 613 cp = *gargv; 614 if (cp == NULL) 615 return 0; 616 gargv++; 617 618 if (*cp == '\"' || *cp == '\'') 619 return *(cp+1); 620 621 errno = 0; 622 val = strtoimax(cp, &ep, 0); 623 check_conversion(cp, ep); 624 return val; 625 } 626 627 static uintmax_t 628 getuintmax(void) 629 { 630 uintmax_t val; 631 char *cp, *ep; 632 633 cp = *gargv; 634 if (cp == NULL) 635 return 0; 636 gargv++; 637 638 if (*cp == '\"' || *cp == '\'') 639 return *(cp + 1); 640 641 /* strtoumax won't error -ve values */ 642 while (isspace(*(unsigned char *)cp)) 643 cp++; 644 if (*cp == '-') { 645 warnx("%s: expected positive numeric value", cp); 646 rval = 1; 647 return 0; 648 } 649 650 errno = 0; 651 val = strtoumax(cp, &ep, 0); 652 check_conversion(cp, ep); 653 return val; 654 } 655 656 static double 657 getdouble(void) 658 { 659 double val; 660 char *ep; 661 662 if (!*gargv) 663 return (0.0); 664 665 if (**gargv == '\"' || **gargv == '\'') 666 return (double) *((*gargv++)+1); 667 668 errno = 0; 669 val = strtod(*gargv, &ep); 670 check_conversion(*gargv++, ep); 671 return val; 672 } 673 674 static void 675 check_conversion(const char *s, const char *ep) 676 { 677 if (*ep) { 678 if (ep == s) 679 warnx("%s: expected numeric value", s); 680 else 681 warnx("%s: not completely converted", s); 682 rval = 1; 683 } else if (errno == ERANGE) { 684 warnx("%s: %s", s, strerror(ERANGE)); 685 rval = 1; 686 } 687 } 688 689 static void 690 usage(void) 691 { 692 (void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname()); 693 } 694