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