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