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