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 * @(#) Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved. 34 * @(#)printf.c 8.1 (Berkeley) 7/20/93 35 * $FreeBSD: src/usr.bin/printf/printf.c,v 1.12.6.6 2002/04/29 16:45:16 jmallett Exp $ 36 * $DragonFly: src/usr.bin/printf/printf.c,v 1.2 2003/06/17 04:29:30 dillon Exp $ 37 */ 38 39 #include <sys/types.h> 40 41 #include <err.h> 42 #include <errno.h> 43 #include <limits.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 #ifdef SHELL 50 #define main printfcmd 51 #include "bltin/bltin.h" 52 #include "memalloc.h" 53 #else 54 #define warnx1(a, b, c) warnx(a) 55 #define warnx2(a, b, c) warnx(a, b) 56 #define warnx3(a, b, c) warnx(a, b, c) 57 #endif 58 59 #ifndef BUILTIN 60 #include <locale.h> 61 #endif 62 63 #define PF(f, func) do { \ 64 char *b = NULL; \ 65 if (fieldwidth) \ 66 if (precision) \ 67 (void)asprintf(&b, f, fieldwidth, precision, func); \ 68 else \ 69 (void)asprintf(&b, f, fieldwidth, func); \ 70 else if (precision) \ 71 (void)asprintf(&b, f, precision, func); \ 72 else \ 73 (void)asprintf(&b, f, func); \ 74 if (b) { \ 75 (void)fputs(b, stdout); \ 76 free(b); \ 77 } \ 78 } while (0) 79 80 static int asciicode(void); 81 static int escape(char *); 82 static int getchr(void); 83 static int getdouble(double *); 84 static int getint(int *); 85 static int getquads(quad_t *, u_quad_t *, int); 86 static const char 87 *getstr(void); 88 static char *mkquad(char *, int); 89 static void usage(void); 90 91 static char **gargv; 92 93 int 94 #ifdef BUILTIN 95 progprintf(argc, argv) 96 #else 97 main(argc, argv) 98 #endif 99 int argc; 100 char *argv[]; 101 { 102 static const char *skip1, *skip2; 103 int ch, chopped, end, fieldwidth, precision, rval; 104 char convch, nextch, *format, *fmt, *start; 105 106 #ifndef BUILTIN 107 (void) setlocale(LC_NUMERIC, ""); 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 /* 125 * Basic algorithm is to scan the format string for conversion 126 * specifications -- once one is found, find out if the field 127 * width or precision is a '*'; if it is, gather up value. Note, 128 * format strings are reused as necessary to use up the provided 129 * arguments, arguments of zero/null string are provided to use 130 * up the format string. 131 */ 132 skip1 = "#-+ 0"; 133 skip2 = "0123456789"; 134 135 chopped = escape(fmt = format = *argv); /* backslash interpretation */ 136 rval = 0; 137 gargv = ++argv; 138 for (;;) { 139 end = 0; 140 /* find next format specification */ 141 next: for (start = fmt;; ++fmt) { 142 if (!*fmt) { 143 /* avoid infinite loop */ 144 if (chopped) { 145 (void)printf("%s", start); 146 return (rval); 147 } 148 if (end == 1) { 149 warnx1("missing format character", 150 NULL, NULL); 151 return (1); 152 } 153 end = 1; 154 if (fmt > start) 155 (void)printf("%s", start); 156 if (!*gargv) 157 return (rval); 158 fmt = format; 159 goto next; 160 } 161 /* %% prints a % */ 162 if (*fmt == '%') { 163 if (*++fmt != '%') 164 break; 165 *fmt++ = '\0'; 166 (void)printf("%s", start); 167 goto next; 168 } 169 } 170 171 /* skip to field width */ 172 for (; strchr(skip1, *fmt); ++fmt); 173 if (*fmt == '*') { 174 if (getint(&fieldwidth)) 175 return (1); 176 ++fmt; 177 } else { 178 fieldwidth = 0; 179 180 /* skip to possible '.', get following precision */ 181 for (; strchr(skip2, *fmt); ++fmt); 182 } 183 if (*fmt == '.') { 184 /* precision present? */ 185 ++fmt; 186 if (*fmt == '*') { 187 if (getint(&precision)) 188 return (1); 189 ++fmt; 190 } else { 191 precision = 0; 192 193 /* skip to conversion char */ 194 for (; strchr(skip2, *fmt); ++fmt); 195 } 196 } else 197 precision = 0; 198 if (!*fmt) { 199 warnx1("missing format character", NULL, NULL); 200 return (1); 201 } 202 203 convch = *fmt; 204 nextch = *++fmt; 205 *fmt = '\0'; 206 switch(convch) { 207 case 'b': { 208 char *p; 209 int getout; 210 211 #ifdef SHELL 212 p = savestr(getstr()); 213 #else 214 p = strdup(getstr()); 215 #endif 216 if (p == NULL) { 217 warnx2("%s", strerror(ENOMEM), NULL); 218 return (1); 219 } 220 getout = escape(p); 221 *(fmt - 1) = 's'; 222 PF(start, p); 223 *(fmt - 1) = 'b'; 224 #ifdef SHELL 225 ckfree(p); 226 #else 227 free(p); 228 #endif 229 if (getout) 230 return (rval); 231 break; 232 } 233 case 'c': { 234 char p; 235 236 p = getchr(); 237 PF(start, p); 238 break; 239 } 240 case 's': { 241 const char *p; 242 243 p = getstr(); 244 PF(start, p); 245 break; 246 } 247 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 248 char *f; 249 quad_t val; 250 u_quad_t uval; 251 int signedconv; 252 253 signedconv = (convch == 'd' || convch == 'i'); 254 if ((f = mkquad(start, convch)) == NULL) 255 return (1); 256 if (getquads(&val, &uval, signedconv)) 257 rval = 1; 258 if (signedconv) 259 PF(f, val); 260 else 261 PF(f, uval); 262 break; 263 } 264 case 'e': case 'E': case 'f': case 'g': case 'G': { 265 double p; 266 267 if (getdouble(&p)) 268 rval = 1; 269 PF(start, p); 270 break; 271 } 272 default: 273 warnx2("illegal format character %c", convch, NULL); 274 return (1); 275 } 276 *fmt = nextch; 277 } 278 /* NOTREACHED */ 279 } 280 281 static char * 282 mkquad(str, ch) 283 char *str; 284 int ch; 285 { 286 static char *copy; 287 static size_t copy_size; 288 char *newcopy; 289 size_t len, newlen; 290 291 len = strlen(str) + 2; 292 if (len > copy_size) { 293 newlen = ((len + 1023) >> 10) << 10; 294 #ifdef SHELL 295 if ((newcopy = ckrealloc(copy, newlen)) == NULL) 296 #else 297 if ((newcopy = realloc(copy, newlen)) == NULL) 298 #endif 299 { 300 warnx2("%s", strerror(ENOMEM), NULL); 301 return (NULL); 302 } 303 copy = newcopy; 304 copy_size = newlen; 305 } 306 307 memmove(copy, str, len - 3); 308 copy[len - 3] = 'q'; 309 copy[len - 2] = ch; 310 copy[len - 1] = '\0'; 311 return (copy); 312 } 313 314 static int 315 escape(fmt) 316 register char *fmt; 317 { 318 register char *store; 319 register int value, c; 320 321 for (store = fmt; (c = *fmt); ++fmt, ++store) { 322 if (c != '\\') { 323 *store = c; 324 continue; 325 } 326 switch (*++fmt) { 327 case '\0': /* EOS, user error */ 328 *store = '\\'; 329 *++store = '\0'; 330 return (0); 331 case '\\': /* backslash */ 332 case '\'': /* single quote */ 333 *store = *fmt; 334 break; 335 case 'a': /* bell/alert */ 336 *store = '\7'; 337 break; 338 case 'b': /* backspace */ 339 *store = '\b'; 340 break; 341 case 'c': 342 *store = '\0'; 343 return (1); 344 case 'f': /* form-feed */ 345 *store = '\f'; 346 break; 347 case 'n': /* newline */ 348 *store = '\n'; 349 break; 350 case 'r': /* carriage-return */ 351 *store = '\r'; 352 break; 353 case 't': /* horizontal tab */ 354 *store = '\t'; 355 break; 356 case 'v': /* vertical tab */ 357 *store = '\13'; 358 break; 359 /* octal constant */ 360 case '0': case '1': case '2': case '3': 361 case '4': case '5': case '6': case '7': 362 for (c = *fmt == '0' ? 4 : 3, value = 0; 363 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 364 value <<= 3; 365 value += *fmt - '0'; 366 } 367 --fmt; 368 *store = value; 369 break; 370 default: 371 *store = *fmt; 372 break; 373 } 374 } 375 *store = '\0'; 376 return (0); 377 } 378 379 static int 380 getchr() 381 { 382 if (!*gargv) 383 return ('\0'); 384 return ((int)**gargv++); 385 } 386 387 static const char * 388 getstr() 389 { 390 if (!*gargv) 391 return (""); 392 return (*gargv++); 393 } 394 395 static int 396 getint(ip) 397 int *ip; 398 { 399 quad_t val; 400 u_quad_t uval; 401 int rval; 402 403 if (getquads(&val, &uval, 1)) 404 return (1); 405 rval = 0; 406 if (val < INT_MIN || val > INT_MAX) { 407 warnx3("%s: %s", *gargv, strerror(ERANGE)); 408 rval = 1; 409 } 410 *ip = (int)val; 411 return (rval); 412 } 413 414 static int 415 getquads(qp, uqp, signedconv) 416 quad_t *qp; 417 u_quad_t *uqp; 418 int signedconv; 419 { 420 char *ep; 421 int rval; 422 423 if (!*gargv) { 424 *qp = 0; 425 return (0); 426 } 427 if (**gargv == '"' || **gargv == '\'') { 428 if (signedconv) 429 *qp = asciicode(); 430 else 431 *uqp = asciicode(); 432 return (0); 433 } 434 rval = 0; 435 errno = 0; 436 if (signedconv) 437 *qp = strtoq(*gargv, &ep, 0); 438 else 439 *uqp = strtouq(*gargv, &ep, 0); 440 if (ep == *gargv) { 441 warnx2("%s: expected numeric value", *gargv, NULL); 442 rval = 1; 443 } 444 else if (*ep != '\0') { 445 warnx2("%s: not completely converted", *gargv, NULL); 446 rval = 1; 447 } 448 if (errno == ERANGE) { 449 warnx3("%s: %s", *gargv, strerror(ERANGE)); 450 rval = 1; 451 } 452 ++gargv; 453 return (rval); 454 } 455 456 static int 457 getdouble(dp) 458 double *dp; 459 { 460 char *ep; 461 int rval; 462 463 if (!*gargv) 464 return (0); 465 if (**gargv == '"' || **gargv == '\'') { 466 *dp = asciicode(); 467 return (0); 468 } 469 rval = 1; 470 errno = 0; 471 *dp = strtod(*gargv, &ep); 472 if (ep == *gargv) { 473 warnx2("%s: expected numeric value", *gargv, NULL); 474 rval = 1; 475 } else if (*ep != '\0') { 476 warnx2("%s: not completely converted", *gargv, NULL); 477 rval = 1; 478 } 479 if (errno == ERANGE) { 480 warnx3("%s: %s", *gargv, strerror(ERANGE)); 481 rval = 1; 482 } 483 ++gargv; 484 return (rval); 485 } 486 487 static int 488 asciicode() 489 { 490 register int ch; 491 492 ch = **gargv; 493 if (ch == '\'' || ch == '"') 494 ch = (*gargv)[1]; 495 ++gargv; 496 return (ch); 497 } 498 499 static void 500 usage() 501 { 502 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 503 } 504