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