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