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