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