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