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