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