1 /* $NetBSD: printf.c,v 1.29 2003/08/07 11:15:33 agc 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.29 2003/08/07 11:15:33 agc 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 int 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++) ;) { 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 b_count(int ch) 297 { 298 b_length++; 299 } 300 301 /* Output one converted character for every 'x' in the 'format' */ 302 303 static void 304 b_output(int ch) 305 { 306 for (;;) { 307 switch (*b_fmt++) { 308 case 0: 309 b_fmt--; 310 return; 311 case ' ': 312 putchar(' '); 313 break; 314 default: 315 putchar(ch); 316 return; 317 } 318 } 319 } 320 321 322 /* 323 * Print SysV echo(1) style escape string 324 * Halts processing string if a \c escape is encountered. 325 */ 326 static void 327 conv_escape_str(char *str, void (*do_putchar)(int)) 328 { 329 int value; 330 int ch; 331 char c; 332 333 while ((ch = *str++)) { 334 if (ch != '\\') { 335 do_putchar(ch); 336 continue; 337 } 338 339 ch = *str++; 340 if (ch == 'c') { 341 /* \c as in SYSV echo - abort all processing.... */ 342 rval |= 0x100; 343 break; 344 } 345 346 /* 347 * %b string octal constants are not like those in C. 348 * They start with a \0, and are followed by 0, 1, 2, 349 * or 3 octal digits. 350 */ 351 if (ch == '0') { 352 char octnum[4], *oct_end; 353 octnum[0] = str[0]; 354 octnum[1] = str[1]; 355 octnum[2] = str[2]; 356 octnum[3] = 0; 357 do_putchar(strtoul(octnum, &oct_end, 8)); 358 str += oct_end - octnum; 359 continue; 360 } 361 362 /* \[M][^|-]C as defined by vis(3) */ 363 if (ch == 'M' && *str == '-') { 364 do_putchar(0200 | str[1]); 365 str += 2; 366 continue; 367 } 368 if (ch == 'M' && *str == '^') { 369 str++; 370 value = 0200; 371 ch = '^'; 372 } else 373 value = 0; 374 if (ch == '^') { 375 ch = *str++; 376 if (ch == '?') 377 value |= 0177; 378 else 379 value |= ch & 037; 380 do_putchar(value); 381 continue; 382 } 383 384 /* Finally test for sequences valid in the format string */ 385 str = conv_escape(str - 1, &c); 386 do_putchar(c); 387 } 388 } 389 390 /* 391 * Print "standard" escape characters 392 */ 393 static char * 394 conv_escape(char *str, char *conv_ch) 395 { 396 int value; 397 int ch; 398 char num_buf[4], *num_end; 399 400 ch = *str++; 401 402 switch (ch) { 403 case '0': case '1': case '2': case '3': 404 case '4': case '5': case '6': case '7': 405 num_buf[0] = ch; 406 ch = str[0]; 407 num_buf[1] = ch; 408 num_buf[2] = ch ? str[1] : 0; 409 num_buf[3] = 0; 410 value = strtoul(num_buf, &num_end, 8); 411 str += num_end - (num_buf + 1); 412 break; 413 414 case 'x': 415 /* Hexadecimal character constants are not required to be 416 supported (by SuS v1) because there is no consistent 417 way to detect the end of the constant. 418 Supporting 2 byte constants is a compromise. */ 419 ch = str[0]; 420 num_buf[0] = ch; 421 num_buf[1] = ch ? str[1] : 0; 422 num_buf[2] = 0; 423 value = strtoul(num_buf, &num_end, 16); 424 str += num_end - num_buf; 425 break; 426 427 case '\\': value = '\\'; break; /* backslash */ 428 case '\'': value = '\''; break; /* single quote */ 429 case '"': value = '"'; break; /* double quote */ 430 case 'a': value = '\a'; break; /* alert */ 431 case 'b': value = '\b'; break; /* backspace */ 432 case 'e': value = ESCAPE; break; /* escape */ 433 case 'f': value = '\f'; break; /* form-feed */ 434 case 'n': value = '\n'; break; /* newline */ 435 case 'r': value = '\r'; break; /* carriage-return */ 436 case 't': value = '\t'; break; /* tab */ 437 case 'v': value = '\v'; break; /* vertical-tab */ 438 439 default: 440 warnx("unknown escape sequence `\\%c'", ch); 441 rval = 1; 442 value = ch; 443 break; 444 } 445 446 *conv_ch = value; 447 return str; 448 } 449 450 /* expand a string so that everything is printable */ 451 452 static char * 453 conv_expand(const char *str) 454 { 455 static char *conv_str; 456 char *cp; 457 int ch; 458 459 if (conv_str) 460 free(conv_str); 461 /* get a buffer that is definitely large enough.... */ 462 conv_str = malloc(4 * strlen(str) + 1); 463 if (!conv_str) 464 return "<no memory>"; 465 cp = conv_str; 466 467 while ((ch = *(unsigned char *)str++)) { 468 switch (ch) { 469 /* Use C escapes for expected control characters */ 470 case '\\': ch = '\\'; break; /* backslash */ 471 case '\'': ch = '\''; break; /* single quote */ 472 case '"': ch = '"'; break; /* double quote */ 473 case '\a': ch = 'a'; break; /* alert */ 474 case '\b': ch = 'b'; break; /* backspace */ 475 case ESCAPE: ch = 'e'; break; /* escape */ 476 case '\f': ch = 'f'; break; /* form-feed */ 477 case '\n': ch = 'n'; break; /* newline */ 478 case '\r': ch = 'r'; break; /* carriage-return */ 479 case '\t': ch = 't'; break; /* tab */ 480 case '\v': ch = 'v'; break; /* vertical-tab */ 481 default: 482 /* Copy anything printable */ 483 if (isprint(ch)) { 484 *cp++ = ch; 485 continue; 486 } 487 /* Use vis(3) encodings for the rest */ 488 *cp++ = '\\'; 489 if (ch & 0200) { 490 *cp++ = 'M'; 491 ch &= ~0200; 492 } 493 if (ch == 0177) { 494 *cp++ = '^'; 495 *cp++ = '?'; 496 continue; 497 } 498 if (ch < 040) { 499 *cp++ = '^'; 500 *cp++ = ch | 0100; 501 continue; 502 } 503 *cp++ = '-'; 504 *cp++ = ch; 505 continue; 506 } 507 *cp++ = '\\'; 508 *cp++ = ch; 509 } 510 511 *cp = 0; 512 return conv_str; 513 } 514 515 static char * 516 mklong(const char *str, int ch) 517 { 518 static char copy[64]; 519 size_t len; 520 521 len = strlen(str) + 2; 522 if (len > sizeof copy) { 523 warnx("format %s too complex\n", str); 524 len = 4; 525 } 526 (void)memmove(copy, str, len - 3); 527 copy[len - 3] = 'j'; 528 copy[len - 2] = ch; 529 copy[len - 1] = '\0'; 530 return (copy); 531 } 532 533 static int 534 getchr(void) 535 { 536 if (!*gargv) 537 return ('\0'); 538 return ((int)**gargv++); 539 } 540 541 static char * 542 getstr(void) 543 { 544 if (!*gargv) 545 return (""); 546 return (*gargv++); 547 } 548 549 static int 550 getwidth(void) 551 { 552 long val; 553 char *s, *ep; 554 555 s = *gargv; 556 if (!*gargv) 557 return (0); 558 gargv++; 559 560 errno = 0; 561 val = strtoul(s, &ep, 0); 562 check_conversion(s, ep); 563 564 /* Arbitrarily 'restrict' field widths to 1Mbyte */ 565 if (val < 0 || val > 1 << 20) { 566 warnx("%s: invalid field width", s); 567 return 0; 568 } 569 570 return val; 571 } 572 573 static intmax_t 574 getintmax(void) 575 { 576 intmax_t val; 577 char *cp, *ep; 578 579 cp = *gargv; 580 if (cp == NULL) 581 return 0; 582 gargv++; 583 584 if (*cp == '\"' || *cp == '\'') 585 return *(cp+1); 586 587 errno = 0; 588 val = strtoimax(cp, &ep, 0); 589 check_conversion(cp, ep); 590 return val; 591 } 592 593 static uintmax_t 594 getuintmax(void) 595 { 596 uintmax_t val; 597 char *cp, *ep; 598 599 cp = *gargv; 600 if (cp == NULL) 601 return 0; 602 gargv++; 603 604 if (*cp == '\"' || *cp == '\'') 605 return *(cp+1); 606 607 /* strtoumax won't error -ve values */ 608 while (isspace(*(unsigned char *)cp)) 609 cp++; 610 if (*cp == '-') { 611 warnx("%s: expected positive numeric value", cp); 612 rval = 1; 613 return 0; 614 } 615 616 errno = 0; 617 val = strtoumax(cp, &ep, 0); 618 check_conversion(cp, ep); 619 return val; 620 } 621 622 static double 623 getdouble(void) 624 { 625 double val; 626 char *ep; 627 628 if (!*gargv) 629 return (0.0); 630 631 if (**gargv == '\"' || **gargv == '\'') 632 return (double) *((*gargv++)+1); 633 634 errno = 0; 635 val = strtod(*gargv, &ep); 636 check_conversion(*gargv++, ep); 637 return val; 638 } 639 640 static void 641 check_conversion(const char *s, const char *ep) 642 { 643 if (*ep) { 644 if (ep == s) 645 warnx("%s: expected numeric value", s); 646 else 647 warnx("%s: not completely converted", s); 648 rval = 1; 649 } else if (errno == ERANGE) { 650 warnx("%s: %s", s, strerror(ERANGE)); 651 rval = 1; 652 } 653 } 654 655 static void 656 usage(void) 657 { 658 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 659 } 660