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