1 /* 2 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 3 * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 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 * 4. 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/types.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <inttypes.h> 37 #include <limits.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <alloca.h> 43 #include <ctype.h> 44 #include <locale.h> 45 #include <note.h> 46 47 #define warnx1(a, b, c) warnx(a) 48 #define warnx2(a, b, c) warnx(a, b) 49 #define warnx3(a, b, c) warnx(a, b, c) 50 51 #define PTRDIFF(x, y) ((uintptr_t)(x) - (uintptr_t)(y)) 52 53 #define _(x) gettext(x) 54 55 #define PF(f, func) do { \ 56 char *b = NULL; \ 57 if (havewidth) \ 58 if (haveprec) \ 59 (void) asprintf(&b, f, fieldwidth, precision, func); \ 60 else \ 61 (void) asprintf(&b, f, fieldwidth, func); \ 62 else if (haveprec) \ 63 (void) asprintf(&b, f, precision, func); \ 64 else \ 65 (void) asprintf(&b, f, func); \ 66 if (b) { \ 67 (void) fputs(b, stdout); \ 68 free(b); \ 69 } \ 70 _NOTE(CONSTCOND) } while (0) 71 72 static int asciicode(void); 73 static char *doformat(char *, int *); 74 static int escape(char *, int, size_t *); 75 static int getchr(void); 76 static int getfloating(long double *, int); 77 static int getint(int *); 78 static int getnum(intmax_t *, uintmax_t *, int); 79 static const char 80 *getstr(void); 81 static char *mknum(char *, char); 82 static void usage(void); 83 84 static const char digits[] = "0123456789"; 85 86 static int myargc; 87 static char **myargv; 88 static char **gargv; 89 static char **maxargv; 90 91 int 92 main(int argc, char *argv[]) 93 { 94 size_t len; 95 int end, rval; 96 char *format, *fmt, *start; 97 98 (void) setlocale(LC_ALL, ""); 99 100 argv++; 101 argc--; 102 103 /* 104 * POSIX says: Standard utilities that do not accept options, 105 * but that do accept operands, shall recognize "--" as a 106 * first argument to be discarded. 107 */ 108 if (argc && strcmp(argv[0], "--") == 0) { 109 argc--; 110 argv++; 111 } 112 113 if (argc < 1) { 114 usage(); 115 return (1); 116 } 117 118 /* 119 * Basic algorithm is to scan the format string for conversion 120 * specifications -- once one is found, find out if the field 121 * width or precision is a '*'; if it is, gather up value. Note, 122 * format strings are reused as necessary to use up the provided 123 * arguments, arguments of zero/null string are provided to use 124 * up the format string. 125 */ 126 fmt = format = *argv; 127 (void) escape(fmt, 1, &len); /* backslash interpretation */ 128 rval = end = 0; 129 gargv = ++argv; 130 131 for (;;) { 132 maxargv = gargv; 133 134 myargv = gargv; 135 for (myargc = 0; gargv[myargc]; myargc++) 136 /* nop */; 137 start = fmt; 138 while (fmt < format + len) { 139 if (fmt[0] == '%') { 140 (void) fwrite(start, 1, PTRDIFF(fmt, start), 141 stdout); 142 if (fmt[1] == '%') { 143 /* %% prints a % */ 144 (void) putchar('%'); 145 fmt += 2; 146 } else { 147 fmt = doformat(fmt, &rval); 148 if (fmt == NULL) 149 return (1); 150 end = 0; 151 } 152 start = fmt; 153 } else 154 fmt++; 155 if (gargv > maxargv) 156 maxargv = gargv; 157 } 158 gargv = maxargv; 159 160 if (end == 1) { 161 warnx1(_("missing format character"), NULL, NULL); 162 return (1); 163 } 164 (void) fwrite(start, 1, PTRDIFF(fmt, start), stdout); 165 if (!*gargv) 166 return (rval); 167 /* Restart at the beginning of the format string. */ 168 fmt = format; 169 end = 1; 170 } 171 /* NOTREACHED */ 172 } 173 174 175 static char * 176 doformat(char *fmt, int *rval) 177 { 178 static const char skip1[] = "#'-+ 0"; 179 int fieldwidth, haveprec, havewidth, mod_ldbl, precision; 180 char convch, nextch; 181 char *start; 182 char **fargv; 183 char *dptr; 184 int l; 185 186 start = alloca(strlen(fmt) + 1); 187 188 dptr = start; 189 *dptr++ = '%'; 190 *dptr = 0; 191 192 fmt++; 193 194 /* look for "n$" field index specifier */ 195 l = strspn(fmt, digits); 196 if ((l > 0) && (fmt[l] == '$')) { 197 int idx = atoi(fmt); 198 if (idx <= myargc) { 199 gargv = &myargv[idx - 1]; 200 } else { 201 gargv = &myargv[myargc]; 202 } 203 if (gargv > maxargv) { 204 maxargv = gargv; 205 } 206 fmt += l + 1; 207 208 /* save format argument */ 209 fargv = gargv; 210 } else { 211 fargv = NULL; 212 } 213 214 /* skip to field width */ 215 while (strchr(skip1, *fmt) != NULL) { 216 *dptr++ = *fmt++; 217 *dptr = 0; 218 } 219 220 221 if (*fmt == '*') { 222 223 fmt++; 224 l = strspn(fmt, digits); 225 if ((l > 0) && (fmt[l] == '$')) { 226 int idx = atoi(fmt); 227 if (idx <= myargc) { 228 gargv = &myargv[idx - 1]; 229 } else { 230 gargv = &myargv[myargc]; 231 } 232 fmt += l + 1; 233 } 234 235 if (getint(&fieldwidth)) 236 return (NULL); 237 if (gargv > maxargv) { 238 maxargv = gargv; 239 } 240 havewidth = 1; 241 242 *dptr++ = '*'; 243 *dptr = 0; 244 } else { 245 havewidth = 0; 246 247 /* skip to possible '.', get following precision */ 248 while (isdigit(*fmt)) { 249 *dptr++ = *fmt++; 250 *dptr = 0; 251 } 252 } 253 254 if (*fmt == '.') { 255 /* precision present? */ 256 fmt++; 257 *dptr++ = '.'; 258 259 if (*fmt == '*') { 260 261 fmt++; 262 l = strspn(fmt, digits); 263 if ((l > 0) && (fmt[l] == '$')) { 264 int idx = atoi(fmt); 265 if (idx <= myargc) { 266 gargv = &myargv[idx - 1]; 267 } else { 268 gargv = &myargv[myargc]; 269 } 270 fmt += l + 1; 271 } 272 273 if (getint(&precision)) 274 return (NULL); 275 if (gargv > maxargv) { 276 maxargv = gargv; 277 } 278 haveprec = 1; 279 *dptr++ = '*'; 280 *dptr = 0; 281 } else { 282 haveprec = 0; 283 284 /* skip to conversion char */ 285 while (isdigit(*fmt)) { 286 *dptr++ = *fmt++; 287 *dptr = 0; 288 } 289 } 290 } else 291 haveprec = 0; 292 if (!*fmt) { 293 warnx1(_("missing format character"), NULL, NULL); 294 return (NULL); 295 } 296 *dptr++ = *fmt; 297 *dptr = 0; 298 299 /* 300 * Look for a length modifier. POSIX doesn't have these, so 301 * we only support them for floating-point conversions, which 302 * are extensions. This is useful because the L modifier can 303 * be used to gain extra range and precision, while omitting 304 * it is more likely to produce consistent results on different 305 * architectures. This is not so important for integers 306 * because overflow is the only bad thing that can happen to 307 * them, but consider the command printf %a 1.1 308 */ 309 if (*fmt == 'L') { 310 mod_ldbl = 1; 311 fmt++; 312 if (!strchr("aAeEfFgG", *fmt)) { 313 warnx2(_("bad modifier L for %%%c"), *fmt, NULL); 314 return (NULL); 315 } 316 } else { 317 mod_ldbl = 0; 318 } 319 320 /* save the current arg offset, and set to the format arg */ 321 if (fargv != NULL) { 322 gargv = fargv; 323 } 324 325 convch = *fmt; 326 nextch = *++fmt; 327 328 *fmt = '\0'; 329 switch (convch) { 330 case 'b': { 331 size_t len; 332 char *p; 333 int getout; 334 335 p = strdup(getstr()); 336 if (p == NULL) { 337 warnx2("%s", strerror(ENOMEM), NULL); 338 return (NULL); 339 } 340 getout = escape(p, 0, &len); 341 (void) fputs(p, stdout); 342 free(p); 343 344 if (getout) 345 exit(*rval); 346 break; 347 } 348 case 'c': { 349 char p; 350 351 p = getchr(); 352 PF(start, p); 353 break; 354 } 355 case 's': { 356 const char *p; 357 358 p = getstr(); 359 PF(start, p); 360 break; 361 } 362 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 363 char *f; 364 intmax_t val; 365 uintmax_t uval; 366 int signedconv; 367 368 signedconv = (convch == 'd' || convch == 'i'); 369 if ((f = mknum(start, convch)) == NULL) 370 return (NULL); 371 if (getnum(&val, &uval, signedconv)) 372 *rval = 1; 373 if (signedconv) 374 PF(f, val); 375 else 376 PF(f, uval); 377 break; 378 } 379 case 'e': case 'E': 380 case 'f': case 'F': 381 case 'g': case 'G': 382 case 'a': case 'A': { 383 long double p; 384 385 if (getfloating(&p, mod_ldbl)) 386 *rval = 1; 387 if (mod_ldbl) 388 PF(start, p); 389 else 390 PF(start, (double)p); 391 break; 392 } 393 default: 394 warnx2(_("illegal format character %c"), convch, NULL); 395 return (NULL); 396 } 397 *fmt = nextch; 398 399 /* return the gargv to the next element */ 400 return (fmt); 401 } 402 403 static char * 404 mknum(char *str, char ch) 405 { 406 static char *copy; 407 static size_t copy_size; 408 char *newcopy; 409 size_t len, newlen; 410 411 len = strlen(str) + 2; 412 if (len > copy_size) { 413 newlen = ((len + 1023) >> 10) << 10; 414 if ((newcopy = realloc(copy, newlen)) == NULL) { 415 warnx2("%s", strerror(ENOMEM), NULL); 416 return (NULL); 417 } 418 copy = newcopy; 419 copy_size = newlen; 420 } 421 422 (void) memmove(copy, str, len - 3); 423 copy[len - 3] = 'j'; 424 copy[len - 2] = ch; 425 copy[len - 1] = '\0'; 426 return (copy); 427 } 428 429 static int 430 escape(char *fmt, int percent, size_t *len) 431 { 432 char *save, *store, c; 433 int value; 434 435 for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { 436 if (c != '\\') { 437 *store = c; 438 continue; 439 } 440 switch (*++fmt) { 441 case '\0': /* EOS, user error */ 442 *store = '\\'; 443 *++store = '\0'; 444 *len = PTRDIFF(store, save); 445 return (0); 446 case '\\': /* backslash */ 447 case '\'': /* single quote */ 448 *store = *fmt; 449 break; 450 case 'a': /* bell/alert */ 451 *store = '\a'; 452 break; 453 case 'b': /* backspace */ 454 *store = '\b'; 455 break; 456 case 'c': 457 if (!percent) { 458 *store = '\0'; 459 *len = PTRDIFF(store, save); 460 return (1); 461 } 462 *store = 'c'; 463 break; 464 case 'f': /* form-feed */ 465 *store = '\f'; 466 break; 467 case 'n': /* newline */ 468 *store = '\n'; 469 break; 470 case 'r': /* carriage-return */ 471 *store = '\r'; 472 break; 473 case 't': /* horizontal tab */ 474 *store = '\t'; 475 break; 476 case 'v': /* vertical tab */ 477 *store = '\v'; 478 break; 479 /* octal constant */ 480 case '0': case '1': case '2': case '3': 481 case '4': case '5': case '6': case '7': 482 c = (!percent && *fmt == '0') ? 4 : 3; 483 for (value = 0; 484 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 485 value <<= 3; 486 value += *fmt - '0'; 487 } 488 --fmt; 489 if (percent && value == '%') { 490 *store++ = '%'; 491 *store = '%'; 492 } else 493 *store = (char)value; 494 break; 495 default: 496 *store = *fmt; 497 break; 498 } 499 } 500 *store = '\0'; 501 *len = PTRDIFF(store, save); 502 return (0); 503 } 504 505 static int 506 getchr(void) 507 { 508 if (!*gargv) 509 return ('\0'); 510 return ((int)**gargv++); 511 } 512 513 static const char * 514 getstr(void) 515 { 516 if (!*gargv) 517 return (""); 518 return (*gargv++); 519 } 520 521 static int 522 getint(int *ip) 523 { 524 intmax_t val; 525 uintmax_t uval; 526 int rval; 527 528 if (getnum(&val, &uval, 1)) 529 return (1); 530 rval = 0; 531 if (val < INT_MIN || val > INT_MAX) { 532 warnx3("%s: %s", *gargv, strerror(ERANGE)); 533 rval = 1; 534 } 535 *ip = (int)val; 536 return (rval); 537 } 538 539 static int 540 getnum(intmax_t *ip, uintmax_t *uip, int signedconv) 541 { 542 char *ep; 543 int rval; 544 545 if (!*gargv) { 546 *ip = 0; 547 return (0); 548 } 549 if (**gargv == '"' || **gargv == '\'') { 550 if (signedconv) 551 *ip = asciicode(); 552 else 553 *uip = asciicode(); 554 return (0); 555 } 556 rval = 0; 557 errno = 0; 558 if (signedconv) 559 *ip = strtoimax(*gargv, &ep, 0); 560 else 561 *uip = strtoumax(*gargv, &ep, 0); 562 if (ep == *gargv) { 563 warnx2(_("%s: expected numeric value"), *gargv, NULL); 564 rval = 1; 565 } else if (*ep != '\0') { 566 warnx2(_("%s: not completely converted"), *gargv, NULL); 567 rval = 1; 568 } 569 if (errno == ERANGE) { 570 warnx3("%s: %s", *gargv, strerror(ERANGE)); 571 rval = 1; 572 } 573 ++gargv; 574 return (rval); 575 } 576 577 static int 578 getfloating(long double *dp, int mod_ldbl) 579 { 580 char *ep; 581 int rval; 582 583 if (!*gargv) { 584 *dp = 0.0; 585 return (0); 586 } 587 if (**gargv == '"' || **gargv == '\'') { 588 *dp = asciicode(); 589 return (0); 590 } 591 rval = 0; 592 errno = 0; 593 if (mod_ldbl) 594 *dp = strtold(*gargv, &ep); 595 else 596 *dp = strtod(*gargv, &ep); 597 if (ep == *gargv) { 598 warnx2(_("%s: expected numeric value"), *gargv, NULL); 599 rval = 1; 600 } else if (*ep != '\0') { 601 warnx2(_("%s: not completely converted"), *gargv, NULL); 602 rval = 1; 603 } 604 if (errno == ERANGE) { 605 warnx3("%s: %s", *gargv, strerror(ERANGE)); 606 rval = 1; 607 } 608 ++gargv; 609 return (rval); 610 } 611 612 static int 613 asciicode(void) 614 { 615 int ch; 616 617 ch = **gargv; 618 if (ch == '\'' || ch == '"') 619 ch = (*gargv)[1]; 620 ++gargv; 621 return (ch); 622 } 623 624 static void 625 usage(void) 626 { 627 (void) fprintf(stderr, _("usage: printf format [arguments ...]\n")); 628 }