1 /* $NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl 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\n\ 36 The Regents of the University of California. All rights reserved.\n"); 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.31 2005/03/22 23:55:46 dsl 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 (void)printf(f, fieldwidth, precision, func); \ 102 else \ 103 (void)printf(f, fieldwidth, func); \ 104 } else if (precision != -1) \ 105 (void)printf(f, precision, func); \ 106 else \ 107 (void)printf(f, func); \ 108 } 109 110 #define APF(cpp, f, func) { \ 111 if (fieldwidth != -1) { \ 112 if (precision != -1) \ 113 (void)asprintf(cpp, f, fieldwidth, precision, func); \ 114 else \ 115 (void)asprintf(cpp, f, fieldwidth, func); \ 116 } else if (precision != -1) \ 117 (void)asprintf(cpp, f, precision, func); \ 118 else \ 119 (void)asprintf(cpp, f, func); \ 120 } 121 122 int main(int, char **); 123 int main(int argc, char *argv[]) 124 { 125 char *fmt, *start; 126 int fieldwidth, precision; 127 char nextch; 128 char *format; 129 int ch; 130 131 #if !defined(SHELL) && !defined(BUILTIN) 132 (void)setlocale (LC_ALL, ""); 133 #endif 134 135 while ((ch = getopt(argc, argv, "")) != -1) { 136 switch (ch) { 137 case '?': 138 default: 139 usage(); 140 return 1; 141 } 142 } 143 argc -= optind; 144 argv += optind; 145 146 if (argc < 1) { 147 usage(); 148 return 1; 149 } 150 151 format = *argv; 152 gargv = ++argv; 153 154 #define SKIP1 "#-+ 0" 155 #define SKIP2 "*0123456789" 156 do { 157 /* 158 * Basic algorithm is to scan the format string for conversion 159 * specifications -- once one is found, find out if the field 160 * width or precision is a '*'; if it is, gather up value. 161 * Note, format strings are reused as necessary to use up the 162 * provided arguments, arguments of zero/null string are 163 * provided to use up the format string. 164 */ 165 166 /* find next format specification */ 167 for (fmt = format; (ch = *fmt++) != '\0';) { 168 if (ch == '\\') { 169 char c_ch; 170 fmt = conv_escape(fmt, &c_ch); 171 putchar(c_ch); 172 continue; 173 } 174 if (ch != '%' || (*fmt == '%' && ++fmt)) { 175 (void)putchar(ch); 176 continue; 177 } 178 179 /* Ok - we've found a format specification, 180 Save its address for a later printf(). */ 181 start = fmt - 1; 182 183 /* skip to field width */ 184 fmt += strspn(fmt, SKIP1); 185 fieldwidth = *fmt == '*' ? getwidth() : -1; 186 187 /* skip to possible '.', get following precision */ 188 fmt += strspn(fmt, SKIP2); 189 if (*fmt == '.') 190 ++fmt; 191 precision = *fmt == '*' ? getwidth() : -1; 192 193 fmt += strspn(fmt, SKIP2); 194 195 ch = *fmt; 196 if (!ch) { 197 warnx("missing format character"); 198 return (1); 199 } 200 /* null terminate format string to we can use it 201 as an argument to printf. */ 202 nextch = fmt[1]; 203 fmt[1] = 0; 204 switch (ch) { 205 206 case 'B': { 207 const char *p = conv_expand(getstr()); 208 *fmt = 's'; 209 PF(start, p); 210 break; 211 } 212 case 'b': { 213 /* There has to be a better way to do this, 214 * but the string we generate might have 215 * embedded nulls. */ 216 static char *a, *t; 217 char *cp = getstr(); 218 /* Free on entry in case shell longjumped out */ 219 if (a != NULL) 220 free(a); 221 a = NULL; 222 if (t != NULL) 223 free(t); 224 t = NULL; 225 /* Count number of bytes we want to output */ 226 b_length = 0; 227 conv_escape_str(cp, b_count); 228 t = malloc(b_length + 1); 229 if (t == NULL) 230 break; 231 memset(t, 'x', b_length); 232 t[b_length] = 0; 233 /* Get printf to calculate the lengths */ 234 *fmt = 's'; 235 APF(&a, start, t); 236 b_fmt = a; 237 /* Output leading spaces and data bytes */ 238 conv_escape_str(cp, b_output); 239 /* Add any trailing spaces */ 240 printf("%s", b_fmt); 241 break; 242 } 243 case 'c': { 244 char p = getchr(); 245 PF(start, p); 246 break; 247 } 248 case 's': { 249 char *p = getstr(); 250 PF(start, p); 251 break; 252 } 253 case 'd': 254 case 'i': { 255 intmax_t p = getintmax(); 256 char *f = mklong(start, ch); 257 PF(f, p); 258 break; 259 } 260 case 'o': 261 case 'u': 262 case 'x': 263 case 'X': { 264 uintmax_t p = getuintmax(); 265 char *f = mklong(start, ch); 266 PF(f, p); 267 break; 268 } 269 case 'e': 270 case 'E': 271 case 'f': 272 case 'g': 273 case 'G': { 274 double p = getdouble(); 275 PF(start, p); 276 break; 277 } 278 default: 279 warnx("%s: invalid directive", start); 280 return 1; 281 } 282 *fmt++ = ch; 283 *fmt = nextch; 284 /* escape if a \c was encountered */ 285 if (rval & 0x100) 286 return rval & ~0x100; 287 } 288 } while (gargv != argv && *gargv); 289 290 return rval; 291 } 292 293 /* helper functions for conv_escape_str */ 294 295 static void 296 /*ARGSUSED*/ 297 b_count(int ch) 298 { 299 b_length++; 300 } 301 302 /* Output one converted character for every 'x' in the 'format' */ 303 304 static void 305 b_output(int ch) 306 { 307 for (;;) { 308 switch (*b_fmt++) { 309 case 0: 310 b_fmt--; 311 return; 312 case ' ': 313 putchar(' '); 314 break; 315 default: 316 putchar(ch); 317 return; 318 } 319 } 320 } 321 322 323 /* 324 * Print SysV echo(1) style escape string 325 * Halts processing string if a \c escape is encountered. 326 */ 327 static void 328 conv_escape_str(char *str, void (*do_putchar)(int)) 329 { 330 int value; 331 int ch; 332 char c; 333 334 while ((ch = *str++) != '\0') { 335 if (ch != '\\') { 336 do_putchar(ch); 337 continue; 338 } 339 340 ch = *str++; 341 if (ch == 'c') { 342 /* \c as in SYSV echo - abort all processing.... */ 343 rval |= 0x100; 344 break; 345 } 346 347 /* 348 * %b string octal constants are not like those in C. 349 * They start with a \0, and are followed by 0, 1, 2, 350 * or 3 octal digits. 351 */ 352 if (ch == '0') { 353 int octnum = 0, i; 354 for (i = 0; i < 3; i++) { 355 if (!isdigit((unsigned char)*str) || *str > '7') 356 break; 357 octnum = (octnum << 3) | (*str++ - '0'); 358 } 359 do_putchar(octnum); 360 continue; 361 } 362 363 /* \[M][^|-]C as defined by vis(3) */ 364 if (ch == 'M' && *str == '-') { 365 do_putchar(0200 | str[1]); 366 str += 2; 367 continue; 368 } 369 if (ch == 'M' && *str == '^') { 370 str++; 371 value = 0200; 372 ch = '^'; 373 } else 374 value = 0; 375 if (ch == '^') { 376 ch = *str++; 377 if (ch == '?') 378 value |= 0177; 379 else 380 value |= ch & 037; 381 do_putchar(value); 382 continue; 383 } 384 385 /* Finally test for sequences valid in the format string */ 386 str = conv_escape(str - 1, &c); 387 do_putchar(c); 388 } 389 } 390 391 /* 392 * Print "standard" escape characters 393 */ 394 static char * 395 conv_escape(char *str, char *conv_ch) 396 { 397 int value; 398 int ch; 399 char num_buf[4], *num_end; 400 401 ch = *str++; 402 403 switch (ch) { 404 case '0': case '1': case '2': case '3': 405 case '4': case '5': case '6': case '7': 406 num_buf[0] = ch; 407 ch = str[0]; 408 num_buf[1] = ch; 409 num_buf[2] = ch ? str[1] : 0; 410 num_buf[3] = 0; 411 value = strtoul(num_buf, &num_end, 8); 412 str += num_end - (num_buf + 1); 413 break; 414 415 case 'x': 416 /* Hexadecimal character constants are not required to be 417 supported (by SuS v1) because there is no consistent 418 way to detect the end of the constant. 419 Supporting 2 byte constants is a compromise. */ 420 ch = str[0]; 421 num_buf[0] = ch; 422 num_buf[1] = ch ? str[1] : 0; 423 num_buf[2] = 0; 424 value = strtoul(num_buf, &num_end, 16); 425 str += num_end - num_buf; 426 break; 427 428 case '\\': value = '\\'; break; /* backslash */ 429 case '\'': value = '\''; break; /* single quote */ 430 case '"': value = '"'; break; /* double quote */ 431 case 'a': value = '\a'; break; /* alert */ 432 case 'b': value = '\b'; break; /* backspace */ 433 case 'e': value = ESCAPE; break; /* escape */ 434 case 'f': value = '\f'; break; /* form-feed */ 435 case 'n': value = '\n'; break; /* newline */ 436 case 'r': value = '\r'; break; /* carriage-return */ 437 case 't': value = '\t'; break; /* tab */ 438 case 'v': value = '\v'; break; /* vertical-tab */ 439 440 default: 441 warnx("unknown escape sequence `\\%c'", ch); 442 rval = 1; 443 value = ch; 444 break; 445 } 446 447 *conv_ch = value; 448 return str; 449 } 450 451 /* expand a string so that everything is printable */ 452 453 static char * 454 conv_expand(const char *str) 455 { 456 static char *conv_str; 457 static char no_memory[] = "<no memory>"; 458 char *cp; 459 int ch; 460 461 if (conv_str) 462 free(conv_str); 463 /* get a buffer that is definitely large enough.... */ 464 conv_str = malloc(4 * strlen(str) + 1); 465 if (!conv_str) 466 return no_memory; 467 cp = conv_str; 468 469 while ((ch = *(const unsigned char *)str++) != '\0') { 470 switch (ch) { 471 /* Use C escapes for expected control characters */ 472 case '\\': ch = '\\'; break; /* backslash */ 473 case '\'': ch = '\''; break; /* single quote */ 474 case '"': ch = '"'; break; /* double quote */ 475 case '\a': ch = 'a'; break; /* alert */ 476 case '\b': ch = 'b'; break; /* backspace */ 477 case ESCAPE: ch = 'e'; break; /* escape */ 478 case '\f': ch = 'f'; break; /* form-feed */ 479 case '\n': ch = 'n'; break; /* newline */ 480 case '\r': ch = 'r'; break; /* carriage-return */ 481 case '\t': ch = 't'; break; /* tab */ 482 case '\v': ch = 'v'; break; /* vertical-tab */ 483 default: 484 /* Copy anything printable */ 485 if (isprint(ch)) { 486 *cp++ = ch; 487 continue; 488 } 489 /* Use vis(3) encodings for the rest */ 490 *cp++ = '\\'; 491 if (ch & 0200) { 492 *cp++ = 'M'; 493 ch &= ~0200; 494 } 495 if (ch == 0177) { 496 *cp++ = '^'; 497 *cp++ = '?'; 498 continue; 499 } 500 if (ch < 040) { 501 *cp++ = '^'; 502 *cp++ = ch | 0100; 503 continue; 504 } 505 *cp++ = '-'; 506 *cp++ = ch; 507 continue; 508 } 509 *cp++ = '\\'; 510 *cp++ = ch; 511 } 512 513 *cp = 0; 514 return conv_str; 515 } 516 517 static char * 518 mklong(const char *str, int ch) 519 { 520 static char copy[64]; 521 size_t len; 522 523 len = strlen(str) + 2; 524 if (len > sizeof copy) { 525 warnx("format %s too complex\n", str); 526 len = 4; 527 } 528 (void)memmove(copy, str, len - 3); 529 copy[len - 3] = 'j'; 530 copy[len - 2] = ch; 531 copy[len - 1] = '\0'; 532 return copy; 533 } 534 535 static int 536 getchr(void) 537 { 538 if (!*gargv) 539 return 0; 540 return (int)**gargv++; 541 } 542 543 static char * 544 getstr(void) 545 { 546 static char empty[] = ""; 547 if (!*gargv) 548 return empty; 549 return *gargv++; 550 } 551 552 static int 553 getwidth(void) 554 { 555 long val; 556 char *s, *ep; 557 558 s = *gargv; 559 if (!*gargv) 560 return (0); 561 gargv++; 562 563 errno = 0; 564 val = strtoul(s, &ep, 0); 565 check_conversion(s, ep); 566 567 /* Arbitrarily 'restrict' field widths to 1Mbyte */ 568 if (val < 0 || val > 1 << 20) { 569 warnx("%s: invalid field width", s); 570 return 0; 571 } 572 573 return val; 574 } 575 576 static intmax_t 577 getintmax(void) 578 { 579 intmax_t val; 580 char *cp, *ep; 581 582 cp = *gargv; 583 if (cp == NULL) 584 return 0; 585 gargv++; 586 587 if (*cp == '\"' || *cp == '\'') 588 return *(cp+1); 589 590 errno = 0; 591 val = strtoimax(cp, &ep, 0); 592 check_conversion(cp, ep); 593 return val; 594 } 595 596 static uintmax_t 597 getuintmax(void) 598 { 599 uintmax_t val; 600 char *cp, *ep; 601 602 cp = *gargv; 603 if (cp == NULL) 604 return 0; 605 gargv++; 606 607 if (*cp == '\"' || *cp == '\'') 608 return *(cp + 1); 609 610 /* strtoumax won't error -ve values */ 611 while (isspace(*(unsigned char *)cp)) 612 cp++; 613 if (*cp == '-') { 614 warnx("%s: expected positive numeric value", cp); 615 rval = 1; 616 return 0; 617 } 618 619 errno = 0; 620 val = strtoumax(cp, &ep, 0); 621 check_conversion(cp, ep); 622 return val; 623 } 624 625 static double 626 getdouble(void) 627 { 628 double val; 629 char *ep; 630 631 if (!*gargv) 632 return (0.0); 633 634 if (**gargv == '\"' || **gargv == '\'') 635 return (double) *((*gargv++)+1); 636 637 errno = 0; 638 val = strtod(*gargv, &ep); 639 check_conversion(*gargv++, ep); 640 return val; 641 } 642 643 static void 644 check_conversion(const char *s, const char *ep) 645 { 646 if (*ep) { 647 if (ep == s) 648 warnx("%s: expected numeric value", s); 649 else 650 warnx("%s: not completely converted", s); 651 rval = 1; 652 } else if (errno == ERANGE) { 653 warnx("%s: %s", s, strerror(ERANGE)); 654 rval = 1; 655 } 656 } 657 658 static void 659 usage(void) 660 { 661 (void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname()); 662 } 663