1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 5 * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 6 * Copyright (c) 1989, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 /* 34 * Important: This file is used both as a standalone program /usr/bin/printf 35 * and as a builtin for /bin/sh (#define SHELL). 36 */ 37 38 #ifndef SHELL 39 #ifndef lint 40 static char const copyright[] = 41 "@(#) Copyright (c) 1989, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43 #endif /* not lint */ 44 #endif 45 46 #ifndef lint 47 #if 0 48 static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93"; 49 #endif 50 static const char rcsid[] = 51 "$FreeBSD$"; 52 #endif /* not lint */ 53 54 #include <sys/types.h> 55 56 #include <ctype.h> 57 #include <err.h> 58 #include <errno.h> 59 #include <inttypes.h> 60 #include <limits.h> 61 #include <locale.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 #include <wchar.h> 67 68 #ifdef SHELL 69 #define main printfcmd 70 #include "bltin/bltin.h" 71 #include "options.h" 72 #endif 73 74 #define PF(f, func) do { \ 75 if (havewidth) \ 76 if (haveprec) \ 77 (void)printf(f, fieldwidth, precision, func); \ 78 else \ 79 (void)printf(f, fieldwidth, func); \ 80 else if (haveprec) \ 81 (void)printf(f, precision, func); \ 82 else \ 83 (void)printf(f, func); \ 84 } while (0) 85 86 static int asciicode(void); 87 static char *printf_doformat(char *, int *); 88 static int escape(char *, int, size_t *); 89 static int getchr(void); 90 static int getfloating(long double *, int); 91 static int getint(int *); 92 static int getnum(intmax_t *, uintmax_t *, int); 93 static const char 94 *getstr(void); 95 static char *mknum(char *, char); 96 static void usage(void); 97 98 static const char digits[] = "0123456789"; 99 100 static char end_fmt[1]; 101 102 static int myargc; 103 static char **myargv; 104 static char **gargv; 105 static char **maxargv; 106 107 int 108 main(int argc, char *argv[]) 109 { 110 size_t len; 111 int end, rval; 112 char *format, *fmt, *start; 113 #ifndef SHELL 114 int ch; 115 116 (void) setlocale(LC_ALL, ""); 117 #endif 118 119 #ifdef SHELL 120 nextopt(""); 121 argc -= argptr - argv; 122 argv = argptr; 123 #else 124 while ((ch = getopt(argc, argv, "")) != -1) 125 switch (ch) { 126 case '?': 127 default: 128 usage(); 129 return (1); 130 } 131 argc -= optind; 132 argv += optind; 133 #endif 134 135 if (argc < 1) { 136 usage(); 137 return (1); 138 } 139 140 #ifdef SHELL 141 INTOFF; 142 #endif 143 /* 144 * Basic algorithm is to scan the format string for conversion 145 * specifications -- once one is found, find out if the field 146 * width or precision is a '*'; if it is, gather up value. Note, 147 * format strings are reused as necessary to use up the provided 148 * arguments, arguments of zero/null string are provided to use 149 * up the format string. 150 */ 151 fmt = format = *argv; 152 escape(fmt, 1, &len); /* backslash interpretation */ 153 rval = end = 0; 154 gargv = ++argv; 155 156 for (;;) { 157 maxargv = gargv; 158 159 myargv = gargv; 160 for (myargc = 0; gargv[myargc]; myargc++) 161 /* nop */; 162 start = fmt; 163 while (fmt < format + len) { 164 if (fmt[0] == '%') { 165 fwrite(start, 1, fmt - start, stdout); 166 if (fmt[1] == '%') { 167 /* %% prints a % */ 168 putchar('%'); 169 fmt += 2; 170 } else { 171 fmt = printf_doformat(fmt, &rval); 172 if (fmt == NULL || fmt == end_fmt) { 173 #ifdef SHELL 174 INTON; 175 #endif 176 return (fmt == NULL ? 1 : rval); 177 } 178 end = 0; 179 } 180 start = fmt; 181 } else 182 fmt++; 183 if (gargv > maxargv) 184 maxargv = gargv; 185 } 186 gargv = maxargv; 187 188 if (end == 1) { 189 warnx("missing format character"); 190 #ifdef SHELL 191 INTON; 192 #endif 193 return (1); 194 } 195 fwrite(start, 1, fmt - start, stdout); 196 if (!*gargv) { 197 #ifdef SHELL 198 INTON; 199 #endif 200 return (rval); 201 } 202 /* Restart at the beginning of the format string. */ 203 fmt = format; 204 end = 1; 205 } 206 /* NOTREACHED */ 207 } 208 209 210 static char * 211 printf_doformat(char *fmt, int *rval) 212 { 213 static const char skip1[] = "#'-+ 0"; 214 int fieldwidth, haveprec, havewidth, mod_ldbl, precision; 215 char convch, nextch; 216 char start[strlen(fmt) + 1]; 217 char **fargv; 218 char *dptr; 219 int l; 220 221 dptr = start; 222 *dptr++ = '%'; 223 *dptr = 0; 224 225 fmt++; 226 227 /* look for "n$" field index specifier */ 228 l = strspn(fmt, digits); 229 if ((l > 0) && (fmt[l] == '$')) { 230 int idx = atoi(fmt); 231 if (idx <= myargc) { 232 gargv = &myargv[idx - 1]; 233 } else { 234 gargv = &myargv[myargc]; 235 } 236 if (gargv > maxargv) 237 maxargv = gargv; 238 fmt += l + 1; 239 240 /* save format argument */ 241 fargv = gargv; 242 } else { 243 fargv = NULL; 244 } 245 246 /* skip to field width */ 247 while (*fmt && strchr(skip1, *fmt) != NULL) { 248 *dptr++ = *fmt++; 249 *dptr = 0; 250 } 251 252 if (*fmt == '*') { 253 254 fmt++; 255 l = strspn(fmt, digits); 256 if ((l > 0) && (fmt[l] == '$')) { 257 int idx = atoi(fmt); 258 if (fargv == NULL) { 259 warnx("incomplete use of n$"); 260 return (NULL); 261 } 262 if (idx <= myargc) { 263 gargv = &myargv[idx - 1]; 264 } else { 265 gargv = &myargv[myargc]; 266 } 267 fmt += l + 1; 268 } else if (fargv != NULL) { 269 warnx("incomplete use of n$"); 270 return (NULL); 271 } 272 273 if (getint(&fieldwidth)) 274 return (NULL); 275 if (gargv > maxargv) 276 maxargv = gargv; 277 havewidth = 1; 278 279 *dptr++ = '*'; 280 *dptr = 0; 281 } else { 282 havewidth = 0; 283 284 /* skip to possible '.', get following precision */ 285 while (isdigit(*fmt)) { 286 *dptr++ = *fmt++; 287 *dptr = 0; 288 } 289 } 290 291 if (*fmt == '.') { 292 /* precision present? */ 293 fmt++; 294 *dptr++ = '.'; 295 296 if (*fmt == '*') { 297 298 fmt++; 299 l = strspn(fmt, digits); 300 if ((l > 0) && (fmt[l] == '$')) { 301 int idx = atoi(fmt); 302 if (fargv == NULL) { 303 warnx("incomplete use of n$"); 304 return (NULL); 305 } 306 if (idx <= myargc) { 307 gargv = &myargv[idx - 1]; 308 } else { 309 gargv = &myargv[myargc]; 310 } 311 fmt += l + 1; 312 } else if (fargv != NULL) { 313 warnx("incomplete use of n$"); 314 return (NULL); 315 } 316 317 if (getint(&precision)) 318 return (NULL); 319 if (gargv > maxargv) 320 maxargv = gargv; 321 haveprec = 1; 322 *dptr++ = '*'; 323 *dptr = 0; 324 } else { 325 haveprec = 0; 326 327 /* skip to conversion char */ 328 while (isdigit(*fmt)) { 329 *dptr++ = *fmt++; 330 *dptr = 0; 331 } 332 } 333 } else 334 haveprec = 0; 335 if (!*fmt) { 336 warnx("missing format character"); 337 return (NULL); 338 } 339 *dptr++ = *fmt; 340 *dptr = 0; 341 342 /* 343 * Look for a length modifier. POSIX doesn't have these, so 344 * we only support them for floating-point conversions, which 345 * are extensions. This is useful because the L modifier can 346 * be used to gain extra range and precision, while omitting 347 * it is more likely to produce consistent results on different 348 * architectures. This is not so important for integers 349 * because overflow is the only bad thing that can happen to 350 * them, but consider the command printf %a 1.1 351 */ 352 if (*fmt == 'L') { 353 mod_ldbl = 1; 354 fmt++; 355 if (!strchr("aAeEfFgG", *fmt)) { 356 warnx("bad modifier L for %%%c", *fmt); 357 return (NULL); 358 } 359 } else { 360 mod_ldbl = 0; 361 } 362 363 /* save the current arg offset, and set to the format arg */ 364 if (fargv != NULL) { 365 gargv = fargv; 366 } 367 368 convch = *fmt; 369 nextch = *++fmt; 370 371 *fmt = '\0'; 372 switch (convch) { 373 case 'b': { 374 size_t len; 375 char *p; 376 int getout; 377 378 p = strdup(getstr()); 379 if (p == NULL) { 380 warnx("%s", strerror(ENOMEM)); 381 return (NULL); 382 } 383 getout = escape(p, 0, &len); 384 fputs(p, stdout); 385 free(p); 386 if (getout) 387 return (end_fmt); 388 break; 389 } 390 case 'c': { 391 char p; 392 393 p = getchr(); 394 if (p != '\0') 395 PF(start, p); 396 break; 397 } 398 case 's': { 399 const char *p; 400 401 p = getstr(); 402 PF(start, p); 403 break; 404 } 405 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 406 char *f; 407 intmax_t val; 408 uintmax_t uval; 409 int signedconv; 410 411 signedconv = (convch == 'd' || convch == 'i'); 412 if ((f = mknum(start, convch)) == NULL) 413 return (NULL); 414 if (getnum(&val, &uval, signedconv)) 415 *rval = 1; 416 if (signedconv) 417 PF(f, val); 418 else 419 PF(f, uval); 420 break; 421 } 422 case 'e': case 'E': 423 case 'f': case 'F': 424 case 'g': case 'G': 425 case 'a': case 'A': { 426 long double p; 427 428 if (getfloating(&p, mod_ldbl)) 429 *rval = 1; 430 if (mod_ldbl) 431 PF(start, p); 432 else 433 PF(start, (double)p); 434 break; 435 } 436 default: 437 warnx("illegal format character %c", convch); 438 return (NULL); 439 } 440 *fmt = nextch; 441 /* return the gargv to the next element */ 442 return (fmt); 443 } 444 445 static char * 446 mknum(char *str, char ch) 447 { 448 static char *copy; 449 static size_t copy_size; 450 char *newcopy; 451 size_t len, newlen; 452 453 len = strlen(str) + 2; 454 if (len > copy_size) { 455 newlen = ((len + 1023) >> 10) << 10; 456 if ((newcopy = realloc(copy, newlen)) == NULL) { 457 warnx("%s", strerror(ENOMEM)); 458 return (NULL); 459 } 460 copy = newcopy; 461 copy_size = newlen; 462 } 463 464 memmove(copy, str, len - 3); 465 copy[len - 3] = 'j'; 466 copy[len - 2] = ch; 467 copy[len - 1] = '\0'; 468 return (copy); 469 } 470 471 static int 472 escape(char *fmt, int percent, size_t *len) 473 { 474 char *save, *store, c; 475 int value; 476 477 for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { 478 if (c != '\\') { 479 *store = c; 480 continue; 481 } 482 switch (*++fmt) { 483 case '\0': /* EOS, user error */ 484 *store = '\\'; 485 *++store = '\0'; 486 *len = store - save; 487 return (0); 488 case '\\': /* backslash */ 489 case '\'': /* single quote */ 490 *store = *fmt; 491 break; 492 case 'a': /* bell/alert */ 493 *store = '\a'; 494 break; 495 case 'b': /* backspace */ 496 *store = '\b'; 497 break; 498 case 'c': 499 if (!percent) { 500 *store = '\0'; 501 *len = store - save; 502 return (1); 503 } 504 *store = 'c'; 505 break; 506 case 'f': /* form-feed */ 507 *store = '\f'; 508 break; 509 case 'n': /* newline */ 510 *store = '\n'; 511 break; 512 case 'r': /* carriage-return */ 513 *store = '\r'; 514 break; 515 case 't': /* horizontal tab */ 516 *store = '\t'; 517 break; 518 case 'v': /* vertical tab */ 519 *store = '\v'; 520 break; 521 /* octal constant */ 522 case '0': case '1': case '2': case '3': 523 case '4': case '5': case '6': case '7': 524 c = (!percent && *fmt == '0') ? 4 : 3; 525 for (value = 0; 526 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 527 value <<= 3; 528 value += *fmt - '0'; 529 } 530 --fmt; 531 if (percent && value == '%') { 532 *store++ = '%'; 533 *store = '%'; 534 } else 535 *store = (char)value; 536 break; 537 default: 538 *store = *fmt; 539 break; 540 } 541 } 542 *store = '\0'; 543 *len = store - save; 544 return (0); 545 } 546 547 static int 548 getchr(void) 549 { 550 if (!*gargv) 551 return ('\0'); 552 return ((int)**gargv++); 553 } 554 555 static const char * 556 getstr(void) 557 { 558 if (!*gargv) 559 return (""); 560 return (*gargv++); 561 } 562 563 static int 564 getint(int *ip) 565 { 566 intmax_t val; 567 uintmax_t uval; 568 int rval; 569 570 if (getnum(&val, &uval, 1)) 571 return (1); 572 rval = 0; 573 if (val < INT_MIN || val > INT_MAX) { 574 warnx("%s: %s", *gargv, strerror(ERANGE)); 575 rval = 1; 576 } 577 *ip = (int)val; 578 return (rval); 579 } 580 581 static int 582 getnum(intmax_t *ip, uintmax_t *uip, int signedconv) 583 { 584 char *ep; 585 int rval; 586 587 if (!*gargv) { 588 *ip = *uip = 0; 589 return (0); 590 } 591 if (**gargv == '"' || **gargv == '\'') { 592 if (signedconv) 593 *ip = asciicode(); 594 else 595 *uip = asciicode(); 596 return (0); 597 } 598 rval = 0; 599 errno = 0; 600 if (signedconv) 601 *ip = strtoimax(*gargv, &ep, 0); 602 else 603 *uip = strtoumax(*gargv, &ep, 0); 604 if (ep == *gargv) { 605 warnx("%s: expected numeric value", *gargv); 606 rval = 1; 607 } 608 else if (*ep != '\0') { 609 warnx("%s: not completely converted", *gargv); 610 rval = 1; 611 } 612 if (errno == ERANGE) { 613 warnx("%s: %s", *gargv, strerror(ERANGE)); 614 rval = 1; 615 } 616 ++gargv; 617 return (rval); 618 } 619 620 static int 621 getfloating(long double *dp, int mod_ldbl) 622 { 623 char *ep; 624 int rval; 625 626 if (!*gargv) { 627 *dp = 0.0; 628 return (0); 629 } 630 if (**gargv == '"' || **gargv == '\'') { 631 *dp = asciicode(); 632 return (0); 633 } 634 rval = 0; 635 errno = 0; 636 if (mod_ldbl) 637 *dp = strtold(*gargv, &ep); 638 else 639 *dp = strtod(*gargv, &ep); 640 if (ep == *gargv) { 641 warnx("%s: expected numeric value", *gargv); 642 rval = 1; 643 } else if (*ep != '\0') { 644 warnx("%s: not completely converted", *gargv); 645 rval = 1; 646 } 647 if (errno == ERANGE) { 648 warnx("%s: %s", *gargv, strerror(ERANGE)); 649 rval = 1; 650 } 651 ++gargv; 652 return (rval); 653 } 654 655 static int 656 asciicode(void) 657 { 658 int ch; 659 wchar_t wch; 660 mbstate_t mbs; 661 662 ch = (unsigned char)**gargv; 663 if (ch == '\'' || ch == '"') { 664 memset(&mbs, 0, sizeof(mbs)); 665 switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) { 666 case (size_t)-2: 667 case (size_t)-1: 668 wch = (unsigned char)gargv[0][1]; 669 break; 670 case 0: 671 wch = 0; 672 break; 673 } 674 ch = wch; 675 } 676 ++gargv; 677 return (ch); 678 } 679 680 static void 681 usage(void) 682 { 683 (void)fprintf(stderr, "usage: printf format [arguments ...]\n"); 684 } 685