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