1 /* $OpenBSD: printf.c,v 1.5 2001/11/19 19:02:15 mpech Exp $ */ 2 3 /* 4 * Copyright (c) 1989 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if !defined(SHELL) && !defined(BUILTIN) 38 char copyright[] = 39 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 40 All rights reserved.\n"; 41 #endif 42 #endif /* not lint */ 43 44 #ifndef lint 45 /*static char sccsid[] = "from: @(#)printf.c 5.9 (Berkeley) 6/1/90";*/ 46 static char rcsid[] = "$OpenBSD: printf.c,v 1.5 2001/11/19 19:02:15 mpech Exp $"; 47 #endif /* not lint */ 48 49 #include <ctype.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <limits.h> 54 #include <locale.h> 55 #include <errno.h> 56 #include <err.h> 57 58 static int print_escape_str __P((const char *)); 59 static int print_escape __P((const char *)); 60 61 static int getchr __P((void)); 62 static double getdouble __P((void)); 63 static int getint __P((void)); 64 static long getlong __P((void)); 65 static unsigned long getulong __P ((void)); 66 static char *getstr __P((void)); 67 static char *mklong __P((const char *, int)); 68 static void check_conversion __P((const char *, const char *)); 69 static void usage __P((void)); 70 71 static int rval; 72 static char **gargv; 73 74 #define isodigit(c) ((c) >= '0' && (c) <= '7') 75 #define octtobin(c) ((c) - '0') 76 #define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0') 77 78 #ifdef SHELL 79 #define main printfcmd 80 #include "../../bin/sh/bltin/bltin.h" 81 82 #ifdef __STDC__ 83 #include <stdarg.h> 84 #else 85 #include <vararg.h> 86 #endif 87 88 static void 89 #ifdef __STDC__ 90 warnx(const char *fmt, ...) 91 #else 92 warnx(fmt, va_alist) 93 const char *fmt; 94 va_dcl 95 #endif 96 { 97 98 char buf[64]; 99 va_list ap; 100 101 #ifdef __STDC__ 102 va_start(ap, fmt); 103 #else 104 va_start(ap); 105 #endif 106 vsprintf(buf, fmt, ap); 107 va_end(ap); 108 109 error(buf); 110 } 111 #endif /* SHELL */ 112 113 #define PF(f, func) { \ 114 if (fieldwidth) \ 115 if (precision) \ 116 (void)printf(f, fieldwidth, precision, func); \ 117 else \ 118 (void)printf(f, fieldwidth, func); \ 119 else if (precision) \ 120 (void)printf(f, precision, func); \ 121 else \ 122 (void)printf(f, func); \ 123 } 124 125 int 126 #ifdef BUILTIN 127 progprintf(argc, argv) 128 #else 129 main(argc, argv) 130 #endif 131 int argc; 132 char **argv; 133 { 134 char *fmt, *start; 135 int fieldwidth, precision; 136 char convch, nextch; 137 char *format; 138 int ch; 139 140 #if !defined(SHELL) && !defined(BUILTIN) 141 setlocale (LC_ALL, ""); 142 #endif 143 144 while ((ch = getopt(argc, argv, "")) != -1) { 145 switch (ch) { 146 case '?': 147 default: 148 usage(); 149 return (1); 150 } 151 } 152 argc -= optind; 153 argv += optind; 154 155 if (argc < 1) { 156 usage(); 157 return (1); 158 } 159 160 format = *argv; 161 gargv = ++argv; 162 163 #define SKIP1 "#-+ 0" 164 #define SKIP2 "*0123456789" 165 do { 166 /* 167 * Basic algorithm is to scan the format string for conversion 168 * specifications -- once one is found, find out if the field 169 * width or precision is a '*'; if it is, gather up value. 170 * Note, format strings are reused as necessary to use up the 171 * provided arguments, arguments of zero/null string are 172 * provided to use up the format string. 173 */ 174 175 /* find next format specification */ 176 for (fmt = format; *fmt; fmt++) { 177 switch (*fmt) { 178 case '%': 179 start = fmt++; 180 181 if (*fmt == '%') { 182 putchar ('%'); 183 break; 184 } else if (*fmt == 'b') { 185 char *p = getstr(); 186 if (print_escape_str(p)) { 187 return (rval); 188 } 189 break; 190 } 191 192 /* skip to field width */ 193 for (; strchr(SKIP1, *fmt); ++fmt) ; 194 fieldwidth = *fmt == '*' ? getint() : 0; 195 196 /* skip to possible '.', get following precision */ 197 for (; strchr(SKIP2, *fmt); ++fmt) ; 198 if (*fmt == '.') 199 ++fmt; 200 precision = *fmt == '*' ? getint() : 0; 201 202 for (; strchr(SKIP2, *fmt); ++fmt) ; 203 if (!*fmt) { 204 warnx ("missing format character"); 205 return(1); 206 } 207 208 convch = *fmt; 209 nextch = *(fmt + 1); 210 *(fmt + 1) = '\0'; 211 switch(convch) { 212 case 'c': { 213 char p = getchr(); 214 PF(start, p); 215 break; 216 } 217 case 's': { 218 char *p = getstr(); 219 PF(start, p); 220 break; 221 } 222 case 'd': 223 case 'i': { 224 long p; 225 char *f = mklong(start, convch); 226 if (!f) { 227 warnx("out of memory"); 228 return (1); 229 } 230 p = getlong(); 231 PF(f, p); 232 break; 233 } 234 case 'o': 235 case 'u': 236 case 'x': 237 case 'X': { 238 unsigned long p; 239 char *f = mklong(start, convch); 240 if (!f) { 241 warnx("out of memory"); 242 return (1); 243 } 244 p = getulong(); 245 PF(f, p); 246 break; 247 } 248 case 'e': 249 case 'E': 250 case 'f': 251 case 'g': 252 case 'G': { 253 double p = getdouble(); 254 PF(start, p); 255 break; 256 } 257 default: 258 warnx ("%s: invalid directive", start); 259 return(1); 260 } 261 *(fmt + 1) = nextch; 262 break; 263 264 case '\\': 265 fmt += print_escape(fmt); 266 break; 267 268 default: 269 putchar (*fmt); 270 break; 271 } 272 } 273 } while (gargv > argv && *gargv); 274 275 return (rval); 276 } 277 278 279 /* 280 * Print SysV echo(1) style escape string 281 * Halts processing string and returns 1 if a \c escape is encountered. 282 */ 283 static int 284 print_escape_str(str) 285 const char *str; 286 { 287 int value; 288 int c; 289 290 while (*str) { 291 if (*str == '\\') { 292 str++; 293 /* 294 * %b string octal constants are not like those in C. 295 * They start with a \0, and are followed by 0, 1, 2, 296 * or 3 octal digits. 297 */ 298 if (*str == '0') { 299 str++; 300 for (c = 3, value = 0; c-- && isodigit(*str); str++) { 301 value <<= 3; 302 value += octtobin(*str); 303 } 304 putchar (value); 305 str--; 306 } else if (*str == 'c') { 307 return 1; 308 } else { 309 str--; 310 str += print_escape(str); 311 } 312 } else { 313 putchar (*str); 314 } 315 str++; 316 } 317 318 return 0; 319 } 320 321 /* 322 * Print "standard" escape characters 323 */ 324 static int 325 print_escape(str) 326 const char *str; 327 { 328 const char *start = str; 329 int value; 330 int c; 331 332 str++; 333 334 switch (*str) { 335 case '0': case '1': case '2': case '3': 336 case '4': case '5': case '6': case '7': 337 for (c = 3, value = 0; c-- && isodigit(*str); str++) { 338 value <<= 3; 339 value += octtobin(*str); 340 } 341 putchar(value); 342 return str - start - 1; 343 /* NOTREACHED */ 344 345 case 'x': 346 str++; 347 for (value = 0; isxdigit(*str); str++) { 348 value <<= 4; 349 value += hextobin(*str); 350 } 351 if (value > UCHAR_MAX) { 352 warnx ("escape sequence out of range for character"); 353 rval = 1; 354 } 355 putchar (value); 356 return str - start - 1; 357 /* NOTREACHED */ 358 359 case '\\': /* backslash */ 360 putchar('\\'); 361 break; 362 363 case '\'': /* single quote */ 364 putchar('\''); 365 break; 366 367 case '"': /* double quote */ 368 putchar('"'); 369 break; 370 371 case 'a': /* alert */ 372 #ifdef __STDC__ 373 putchar('\a'); 374 #else 375 putchar(007); 376 #endif 377 break; 378 379 case 'b': /* backspace */ 380 putchar('\b'); 381 break; 382 383 case 'e': /* escape */ 384 #ifdef __GNUC__ 385 putchar('\e'); 386 #else 387 putchar(033); 388 #endif 389 break; 390 391 case 'f': /* form-feed */ 392 putchar('\f'); 393 break; 394 395 case 'n': /* newline */ 396 putchar('\n'); 397 break; 398 399 case 'r': /* carriage-return */ 400 putchar('\r'); 401 break; 402 403 case 't': /* tab */ 404 putchar('\t'); 405 break; 406 407 case 'v': /* vertical-tab */ 408 putchar('\v'); 409 break; 410 411 default: 412 putchar(*str); 413 warnx("unknown escape sequence `\\%c'", *str); 414 rval = 1; 415 } 416 417 return 1; 418 } 419 420 static char * 421 mklong(str, ch) 422 const char *str; 423 char ch; 424 { 425 static char *copy; 426 static int copysize; 427 int len; 428 429 len = strlen(str) + 2; 430 if (copysize < len) { 431 char *newcopy; 432 copysize = len + 256; 433 434 newcopy = realloc(copy, copysize); 435 if (newcopy == NULL) { 436 copysize = 0; 437 free(copy); 438 copy = NULL; 439 return (NULL); 440 } 441 copy = newcopy; 442 } 443 (void) memmove(copy, str, len - 3); 444 copy[len - 3] = 'l'; 445 copy[len - 2] = ch; 446 copy[len - 1] = '\0'; 447 return (copy); 448 } 449 450 static int 451 getchr() 452 { 453 if (!*gargv) 454 return((int)'\0'); 455 return((int)**gargv++); 456 } 457 458 static char * 459 getstr() 460 { 461 if (!*gargv) 462 return(""); 463 return(*gargv++); 464 } 465 466 static char *number = "+-.0123456789"; 467 static int 468 getint() 469 { 470 if (!*gargv) 471 return(0); 472 473 if (strchr(number, **gargv)) 474 return(atoi(*gargv++)); 475 476 return 0; 477 } 478 479 static long 480 getlong() 481 { 482 long val; 483 char *ep; 484 485 if (!*gargv) 486 return(0L); 487 488 if (**gargv == '\"' || **gargv == '\'') 489 return (long) *((*gargv++)+1); 490 491 errno = 0; 492 val = strtol (*gargv, &ep, 0); 493 check_conversion(*gargv++, ep); 494 return val; 495 } 496 497 static unsigned long 498 getulong() 499 { 500 unsigned long val; 501 char *ep; 502 503 if (!*gargv) 504 return(0UL); 505 506 if (**gargv == '\"' || **gargv == '\'') 507 return (unsigned long) *((*gargv++)+1); 508 509 errno = 0; 510 val = strtoul (*gargv, &ep, 0); 511 check_conversion(*gargv++, ep); 512 return val; 513 } 514 515 static double 516 getdouble() 517 { 518 double val; 519 char *ep; 520 521 if (!*gargv) 522 return(0.0); 523 524 if (**gargv == '\"' || **gargv == '\'') 525 return (double) *((*gargv++)+1); 526 527 errno = 0; 528 val = strtod (*gargv, &ep); 529 check_conversion(*gargv++, ep); 530 return val; 531 } 532 533 static void 534 check_conversion(s, ep) 535 const char *s; 536 const char *ep; 537 { 538 if (*ep) { 539 if (ep == s) 540 warnx ("%s: expected numeric value", s); 541 else 542 warnx ("%s: not completely converted", s); 543 rval = 1; 544 } else if (errno == ERANGE) { 545 warnx ("%s: %s", s, strerror(ERANGE)); 546 rval = 1; 547 } 548 } 549 550 static void 551 usage() 552 { 553 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 554 } 555