1 /* $OpenBSD: printf.c,v 1.3 1997/01/17 07:13:06 millert 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.3 1997/01/17 07:13:06 millert 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 register char *fmt, *start; 135 register 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 char *f = mklong(start, convch); 225 long p = getlong(); 226 PF(f, p); 227 break; 228 } 229 case 'o': 230 case 'u': 231 case 'x': 232 case 'X': { 233 char *f = mklong(start, convch); 234 unsigned long p = getulong(); 235 PF(f, p); 236 break; 237 } 238 case 'e': 239 case 'E': 240 case 'f': 241 case 'g': 242 case 'G': { 243 double p = getdouble(); 244 PF(start, p); 245 break; 246 } 247 default: 248 warnx ("%s: invalid directive", start); 249 return(1); 250 } 251 *(fmt + 1) = nextch; 252 break; 253 254 case '\\': 255 fmt += print_escape(fmt); 256 break; 257 258 default: 259 putchar (*fmt); 260 break; 261 } 262 } 263 } while (gargv > argv && *gargv); 264 265 return (rval); 266 } 267 268 269 /* 270 * Print SysV echo(1) style escape string 271 * Halts processing string and returns 1 if a \c escape is encountered. 272 */ 273 static int 274 print_escape_str(str) 275 register const char *str; 276 { 277 int value; 278 int c; 279 280 while (*str) { 281 if (*str == '\\') { 282 str++; 283 /* 284 * %b string octal constants are not like those in C. 285 * They start with a \0, and are followed by 0, 1, 2, 286 * or 3 octal digits. 287 */ 288 if (*str == '0') { 289 str++; 290 for (c = 3, value = 0; c-- && isodigit(*str); str++) { 291 value <<= 3; 292 value += octtobin(*str); 293 } 294 putchar (value); 295 str--; 296 } else if (*str == 'c') { 297 return 1; 298 } else { 299 str--; 300 str += print_escape(str); 301 } 302 } else { 303 putchar (*str); 304 } 305 str++; 306 } 307 308 return 0; 309 } 310 311 /* 312 * Print "standard" escape characters 313 */ 314 static int 315 print_escape(str) 316 register const char *str; 317 { 318 const char *start = str; 319 int value; 320 int c; 321 322 str++; 323 324 switch (*str) { 325 case '0': case '1': case '2': case '3': 326 case '4': case '5': case '6': case '7': 327 for (c = 3, value = 0; c-- && isodigit(*str); str++) { 328 value <<= 3; 329 value += octtobin(*str); 330 } 331 putchar(value); 332 return str - start - 1; 333 /* NOTREACHED */ 334 335 case 'x': 336 str++; 337 for (value = 0; isxdigit(*str); str++) { 338 value <<= 4; 339 value += hextobin(*str); 340 } 341 if (value > UCHAR_MAX) { 342 warnx ("escape sequence out of range for character"); 343 rval = 1; 344 } 345 putchar (value); 346 return str - start - 1; 347 /* NOTREACHED */ 348 349 case '\\': /* backslash */ 350 putchar('\\'); 351 break; 352 353 case '\'': /* single quote */ 354 putchar('\''); 355 break; 356 357 case '"': /* double quote */ 358 putchar('"'); 359 break; 360 361 case 'a': /* alert */ 362 #ifdef __STDC__ 363 putchar('\a'); 364 #else 365 putchar(007); 366 #endif 367 break; 368 369 case 'b': /* backspace */ 370 putchar('\b'); 371 break; 372 373 case 'e': /* escape */ 374 #ifdef __GNUC__ 375 putchar('\e'); 376 #else 377 putchar(033); 378 #endif 379 break; 380 381 case 'f': /* form-feed */ 382 putchar('\f'); 383 break; 384 385 case 'n': /* newline */ 386 putchar('\n'); 387 break; 388 389 case 'r': /* carriage-return */ 390 putchar('\r'); 391 break; 392 393 case 't': /* tab */ 394 putchar('\t'); 395 break; 396 397 case 'v': /* vertical-tab */ 398 putchar('\v'); 399 break; 400 401 default: 402 putchar(*str); 403 warnx("unknown escape sequence `\\%c'", *str); 404 rval = 1; 405 } 406 407 return 1; 408 } 409 410 static char * 411 mklong(str, ch) 412 const char *str; 413 char ch; 414 { 415 static char copy[64]; 416 int len; 417 418 len = strlen(str) + 2; 419 (void) memmove(copy, str, len - 3); 420 copy[len - 3] = 'l'; 421 copy[len - 2] = ch; 422 copy[len - 1] = '\0'; 423 return (copy); 424 } 425 426 static int 427 getchr() 428 { 429 if (!*gargv) 430 return((int)'\0'); 431 return((int)**gargv++); 432 } 433 434 static char * 435 getstr() 436 { 437 if (!*gargv) 438 return(""); 439 return(*gargv++); 440 } 441 442 static char *number = "+-.0123456789"; 443 static int 444 getint() 445 { 446 if (!*gargv) 447 return(0); 448 449 if (strchr(number, **gargv)) 450 return(atoi(*gargv++)); 451 452 return 0; 453 } 454 455 static long 456 getlong() 457 { 458 long val; 459 char *ep; 460 461 if (!*gargv) 462 return(0L); 463 464 if (**gargv == '\"' || **gargv == '\'') 465 return (long) *((*gargv++)+1); 466 467 errno = 0; 468 val = strtol (*gargv, &ep, 0); 469 check_conversion(*gargv++, ep); 470 return val; 471 } 472 473 static unsigned long 474 getulong() 475 { 476 unsigned long val; 477 char *ep; 478 479 if (!*gargv) 480 return(0UL); 481 482 if (**gargv == '\"' || **gargv == '\'') 483 return (unsigned long) *((*gargv++)+1); 484 485 errno = 0; 486 val = strtoul (*gargv, &ep, 0); 487 check_conversion(*gargv++, ep); 488 return val; 489 } 490 491 static double 492 getdouble() 493 { 494 double val; 495 char *ep; 496 497 if (!*gargv) 498 return(0.0); 499 500 if (**gargv == '\"' || **gargv == '\'') 501 return (double) *((*gargv++)+1); 502 503 errno = 0; 504 val = strtod (*gargv, &ep); 505 check_conversion(*gargv++, ep); 506 return val; 507 } 508 509 static void 510 check_conversion(s, ep) 511 const char *s; 512 const char *ep; 513 { 514 if (*ep) { 515 if (ep == s) 516 warnx ("%s: expected numeric value", s); 517 else 518 warnx ("%s: not completely converted", s); 519 rval = 1; 520 } else if (errno == ERANGE) { 521 warnx ("%s: %s", s, strerror(ERANGE)); 522 rval = 1; 523 } 524 } 525 526 static void 527 usage() 528 { 529 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 530 } 531