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