1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 #if !defined(SHELL) && !defined(BUILTIN) 36 char copyright[] = 37 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 38 All rights reserved.\n"; 39 #endif 40 #endif /* not lint */ 41 42 #ifndef lint 43 /*static char sccsid[] = "from: @(#)printf.c 5.9 (Berkeley) 6/1/90";*/ 44 static char rcsid[] = "$Id: printf.c,v 1.9 1993/11/25 04:42:11 jtc Exp $"; 45 #endif /* not lint */ 46 47 #include <ctype.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <limits.h> 52 #include <locale.h> 53 #include <err.h> 54 55 static int print_escape_str __P((const char *)); 56 static int print_escape __P((const char *)); 57 58 static int getchr __P((void)); 59 static double getdouble __P((void)); 60 static int getint __P((void)); 61 static long getlong __P((void)); 62 static char *getstr __P((void)); 63 static char *mklong __P((char *, int)); 64 static void usage __P((void)); 65 66 static int rval; 67 static char **gargv; 68 69 #define isodigit(c) ((c) >= '0' && (c) <= '7') 70 #define octtobin(c) ((c) - '0') 71 #define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0') 72 73 #ifdef SHELL 74 #define main printfcmd 75 #include "../../bin/sh/bltin/bltin.h" 76 77 #ifdef __STDC__ 78 #include <stdarg.h> 79 #else 80 #include <vararg.h> 81 #endif 82 83 static void 84 #ifdef __STDC__ 85 warnx(const char *fmt, ...) 86 #else 87 warnx(fmt, va_alist) 88 const char *fmt; 89 va_dcl 90 #endif 91 { 92 93 char buf[64]; 94 va_list ap; 95 96 #ifdef __STDC__ 97 va_start(ap, fmt); 98 #else 99 va_start(ap); 100 #endif 101 vsprintf(buf, fmt, ap); 102 va_end(ap); 103 104 error(buf); 105 } 106 #endif /* SHELL */ 107 108 #define PF(f, func) { \ 109 if (fieldwidth) \ 110 if (precision) \ 111 (void)printf(f, fieldwidth, precision, func); \ 112 else \ 113 (void)printf(f, fieldwidth, func); \ 114 else if (precision) \ 115 (void)printf(f, precision, func); \ 116 else \ 117 (void)printf(f, func); \ 118 } 119 120 int 121 #ifdef BUILTIN 122 progprintf(argc, argv) 123 #else 124 main(argc, argv) 125 #endif 126 int argc; 127 char **argv; 128 { 129 register char *fmt, *start; 130 register int fieldwidth, precision; 131 char convch, nextch; 132 char *format; 133 int ch; 134 135 #if !defined(SHELL) && !defined(BUILTIN) 136 setlocale (LC_ALL, ""); 137 #endif 138 139 while ((ch = getopt(argc, argv, "")) != -1) { 140 switch (ch) { 141 case '?': 142 default: 143 usage(); 144 return (1); 145 } 146 } 147 argc -= optind; 148 argv += optind; 149 150 if (argc < 1) { 151 usage(); 152 return (1); 153 } 154 155 format = *argv; 156 gargv = ++argv; 157 158 #define SKIP1 "#-+ 0" 159 #define SKIP2 "*0123456789" 160 do { 161 /* 162 * Basic algorithm is to scan the format string for conversion 163 * specifications -- once one is found, find out if the field 164 * width or precision is a '*'; if it is, gather up value. 165 * Note, format strings are reused as necessary to use up the 166 * provided arguments, arguments of zero/null string are 167 * provided to use up the format string. 168 */ 169 170 /* find next format specification */ 171 for (fmt = format; *fmt; fmt++) { 172 switch (*fmt) { 173 case '%': 174 start = fmt++; 175 176 if (*fmt == '%') { 177 putchar ('%'); 178 break; 179 } else if (*fmt == 'b') { 180 char *p = getstr(); 181 if (print_escape_str(p)) { 182 return (rval); 183 } 184 break; 185 } 186 187 /* skip to field width */ 188 for (; index(SKIP1, *fmt); ++fmt) ; 189 fieldwidth = *fmt == '*' ? getint() : 0; 190 191 /* skip to possible '.', get following precision */ 192 for (; index(SKIP2, *fmt); ++fmt) ; 193 if (*fmt == '.') 194 ++fmt; 195 precision = *fmt == '*' ? getint() : 0; 196 197 for (; index(SKIP2, *fmt); ++fmt) ; 198 if (!*fmt) { 199 warnx ("missing format character"); 200 rval = 1; 201 return; /* XXX */ 202 } 203 204 convch = *fmt; 205 nextch = *(fmt + 1); 206 *(fmt + 1) = '\0'; 207 switch(convch) { 208 case 'c': { 209 char p = getchr(); 210 PF(start, p); 211 break; 212 } 213 case 's': { 214 char *p = getstr(); 215 PF(start, p); 216 break; 217 } 218 case 'd': 219 case 'i': 220 case 'o': 221 case 'u': 222 case 'x': 223 case 'X': { 224 char *f = mklong(start, convch); 225 long p = getlong(); 226 PF(f, p); 227 break; 228 } 229 case 'e': 230 case 'E': 231 case 'f': 232 case 'g': 233 case 'G': { 234 double p = getdouble(); 235 PF(start, p); 236 break; 237 } 238 default: 239 warnx ("%s: invalid directive", start); 240 rval = 1; 241 } 242 *(fmt + 1) = nextch; 243 break; 244 245 case '\\': 246 fmt += print_escape(fmt); 247 break; 248 249 default: 250 putchar (*fmt); 251 break; 252 } 253 } 254 } while (gargv > argv && *gargv); 255 256 return (rval); 257 } 258 259 260 /* 261 * Print SysV echo(1) style escape string 262 * Halts processing string and returns 1 if a \c escape is encountered. 263 */ 264 static int 265 print_escape_str(str) 266 register const char *str; 267 { 268 int value; 269 int c; 270 271 while (*str) { 272 if (*str == '\\') { 273 str++; 274 /* 275 * %b string octal constants are not like those in C. 276 * They start with a \0, and are followed by 0, 1, 2, 277 * or 3 octal digits. 278 */ 279 if (*str == '0') { 280 str++; 281 for (c = 3, value = 0; c-- && isodigit(*str); str++) { 282 value <<= 3; 283 value += octtobin(*str); 284 } 285 putchar (value); 286 str--; 287 } else if (*str == 'c') { 288 return 1; 289 } else { 290 str--; 291 str += print_escape(str); 292 } 293 } else { 294 putchar (*str); 295 } 296 str++; 297 } 298 299 return 0; 300 } 301 302 /* 303 * Print "standard" escape characters 304 */ 305 static int 306 print_escape(str) 307 register const char *str; 308 { 309 const char *start = str; 310 int value; 311 int c; 312 313 str++; 314 315 switch (*str) { 316 case '0': case '1': case '2': case '3': 317 case '4': case '5': case '6': case '7': 318 for (c = 3, value = 0; c-- && isodigit(*str); str++) { 319 value <<= 3; 320 value += octtobin(*str); 321 } 322 putchar(value); 323 return str - start - 1; 324 /* NOTREACHED */ 325 326 case 'x': 327 str++; 328 for (value = 0; isxdigit(*str); str++) { 329 value <<= 4; 330 value += hextobin(*str); 331 } 332 if (value > UCHAR_MAX) { 333 warnx ("escape sequence out of range for character"); 334 rval = 1; 335 } 336 putchar (value); 337 return str - start - 1; 338 /* NOTREACHED */ 339 340 case '\\': /* backslash */ 341 putchar('\\'); 342 break; 343 344 case '\'': /* single quote */ 345 putchar('\''); 346 break; 347 348 case '"': /* double quote */ 349 putchar('"'); 350 break; 351 352 case 'a': /* alert */ 353 #ifdef __STDC__ 354 putchar('\a'); 355 #else 356 putchar(007); 357 #endif 358 break; 359 360 case 'b': /* backspace */ 361 putchar('\b'); 362 break; 363 364 case 'e': /* escape */ 365 #ifdef __GNUC__ 366 putchar('\e'); 367 #else 368 putchar(033); 369 #endif 370 break; 371 372 case 'f': /* form-feed */ 373 putchar('\f'); 374 break; 375 376 case 'n': /* newline */ 377 putchar('\n'); 378 break; 379 380 case 'r': /* carriage-return */ 381 putchar('\r'); 382 break; 383 384 case 't': /* tab */ 385 putchar('\t'); 386 break; 387 388 case 'v': /* vertical-tab */ 389 putchar('\v'); 390 break; 391 392 default: 393 putchar(*str); 394 warnx("unknown escape sequence `\\%c'", *str); 395 rval = 1; 396 } 397 398 return 1; 399 } 400 401 static char * 402 mklong(str, ch) 403 char *str, ch; 404 { 405 static char copy[64]; 406 int len; 407 408 len = strlen(str) + 2; 409 (void) memmove(copy, str, len - 3); 410 copy[len - 3] = 'l'; 411 copy[len - 2] = ch; 412 copy[len - 1] = '\0'; 413 return (copy); 414 } 415 416 static int 417 getchr() 418 { 419 if (!*gargv) 420 return((int)'\0'); 421 return((int)**gargv++); 422 } 423 424 static char * 425 getstr() 426 { 427 if (!*gargv) 428 return(""); 429 return(*gargv++); 430 } 431 432 static char *number = "+-.0123456789"; 433 static int 434 getint() 435 { 436 if (!*gargv) 437 return(0); 438 439 if (index(number, **gargv)) 440 return(atoi(*gargv++)); 441 442 return 0; 443 } 444 445 static long 446 getlong() 447 { 448 long val; 449 char *ep; 450 451 if (!*gargv) 452 return(0L); 453 454 if (**gargv == '\"' || **gargv == '\'') { 455 val = gargv[0][1]; 456 gargv++; 457 return val; 458 } 459 460 val = strtol (*gargv, &ep, 0); 461 if (*ep) { 462 warnx ("incompletely converted argument: %s", *gargv); 463 rval = 1; 464 } 465 466 gargv++; 467 return val; 468 } 469 470 static double 471 getdouble() 472 { 473 double val; 474 char *ep; 475 476 if (!*gargv) 477 return(0.0); 478 479 if (**gargv == '\"' || **gargv == '\'') { 480 val = gargv[0][1]; 481 gargv++; 482 return val; 483 } 484 485 val = strtod (*gargv, &ep); 486 if (*ep) { 487 warnx ("incompletely converted argument: %s", *gargv); 488 rval = 1; 489 } 490 491 gargv++; 492 return val; 493 } 494 495 static void 496 usage() 497 { 498 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 499 } 500