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