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