1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #if !defined(BUILTIN) && !defined(SHELL) 9 #ifndef lint 10 char copyright[] = 11 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 12 All rights reserved.\n"; 13 #endif /* not lint */ 14 #endif 15 16 #ifndef lint 17 static char sccsid[] = "@(#)printf.c 5.15 (Berkeley) 04/29/93"; 18 #endif /* not lint */ 19 20 #include <sys/types.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <limits.h> 25 #ifdef SHELL 26 #define EOF -1 27 #else 28 #include <stdio.h> 29 #endif 30 #include <stdlib.h> 31 #include <string.h> 32 33 /* 34 * XXX 35 * This *has* to go away. TK. 36 */ 37 #ifdef SHELL 38 #define main printfcmd 39 #define warnx(a, b, c) { \ 40 char buf[64]; \ 41 (void)sprintf(buf, sizeof(buf), a, b, c); \ 42 error(buf); \ 43 } 44 #include "../../bin/sh/bltin/bltin.h" 45 #endif 46 47 #define PF(f, func) { \ 48 if (fieldwidth) \ 49 if (precision) \ 50 (void)printf(f, fieldwidth, precision, func); \ 51 else \ 52 (void)printf(f, fieldwidth, func); \ 53 else if (precision) \ 54 (void)printf(f, precision, func); \ 55 else \ 56 (void)printf(f, func); \ 57 } 58 59 static int asciicode __P((void)); 60 static void escape __P((char *)); 61 static int getchr __P((void)); 62 static double getdouble __P((void)); 63 static int getint __P((int *)); 64 static int getlong __P((long *)); 65 static char *getstr __P((void)); 66 static char *mklong __P((char *, int)); 67 static void usage __P((void)); 68 69 static char **gargv; 70 71 int 72 #ifdef BUILTIN 73 progprintf(argc, argv) 74 #else 75 main(argc, argv) 76 #endif 77 int argc; 78 char *argv[]; 79 { 80 extern int optind; 81 static char *skip1, *skip2; 82 int ch, end, fieldwidth, precision; 83 char convch, nextch, *format, *fmt, *start; 84 85 while ((ch = getopt(argc, argv, "")) != EOF) 86 switch (ch) { 87 case '?': 88 default: 89 usage(); 90 return (1); 91 } 92 argc -= optind; 93 argv += optind; 94 95 if (argc < 1) { 96 usage(); 97 return (1); 98 } 99 100 /* 101 * Basic algorithm is to scan the format string for conversion 102 * specifications -- once one is found, find out if the field 103 * width or precision is a '*'; if it is, gather up value. Note, 104 * format strings are reused as necessary to use up the provided 105 * arguments, arguments of zero/null string are provided to use 106 * up the format string. 107 */ 108 skip1 = "#-+ 0"; 109 skip2 = "*0123456789"; 110 111 escape(fmt = format = *argv); /* backslash interpretation */ 112 gargv = ++argv; 113 for (;;) { 114 end = 0; 115 /* find next format specification */ 116 next: for (start = fmt;; ++fmt) { 117 if (!*fmt) { 118 /* avoid infinite loop */ 119 if (end == 1) { 120 warnx("missing format character", 121 NULL, NULL); 122 return (1); 123 } 124 end = 1; 125 if (fmt > start) 126 (void)printf("%s", start); 127 if (!*gargv) 128 return (0); 129 fmt = format; 130 goto next; 131 } 132 /* %% prints a % */ 133 if (*fmt == '%') { 134 if (*++fmt != '%') 135 break; 136 *fmt++ = '\0'; 137 (void)printf("%s", start); 138 goto next; 139 } 140 } 141 142 /* skip to field width */ 143 for (; strchr(skip1, *fmt); ++fmt); 144 if (*fmt == '*') { 145 if (getint(&fieldwidth)) 146 return (1); 147 } else 148 fieldwidth = 0; 149 150 /* skip to possible '.', get following precision */ 151 for (; strchr(skip2, *fmt); ++fmt); 152 if (*fmt == '.') 153 ++fmt; 154 if (*fmt == '*') { 155 if (getint(&precision)) 156 return (1); 157 } else 158 precision = 0; 159 160 /* skip to conversion char */ 161 for (; strchr(skip2, *fmt); ++fmt); 162 if (!*fmt) { 163 warnx("missing format character", NULL, NULL); 164 return (1); 165 } 166 167 convch = *fmt; 168 nextch = *++fmt; 169 *fmt = '\0'; 170 switch(convch) { 171 case 'c': { 172 char p; 173 174 p = getchr(); 175 PF(start, p); 176 break; 177 } 178 case 's': { 179 char *p; 180 181 p = getstr(); 182 PF(start, p); 183 break; 184 } 185 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 186 long p; 187 char *f; 188 189 if ((f = mklong(start, convch)) == NULL) 190 return (1); 191 if (getlong(&p)) 192 return (1); 193 PF(f, p); 194 break; 195 } 196 case 'e': case 'E': case 'f': case 'g': case 'G': { 197 double p; 198 199 p = getdouble(); 200 PF(start, p); 201 break; 202 } 203 default: 204 warnx("illegal format character", NULL, NULL); 205 return (1); 206 } 207 *fmt = nextch; 208 } 209 /* NOTREACHED */ 210 } 211 212 static char * 213 mklong(str, ch) 214 char *str; 215 int ch; 216 { 217 static char copy[64]; 218 int len; 219 220 len = strlen(str) + 2; 221 memmove(copy, str, len - 3); 222 copy[len - 3] = 'l'; 223 copy[len - 2] = ch; 224 copy[len - 1] = '\0'; 225 return (copy); 226 } 227 228 static void 229 escape(fmt) 230 register char *fmt; 231 { 232 register char *store; 233 register int value, c; 234 235 for (store = fmt; c = *fmt; ++fmt, ++store) { 236 if (c != '\\') { 237 *store = c; 238 continue; 239 } 240 switch (*++fmt) { 241 case '\0': /* EOS, user error */ 242 *store = '\\'; 243 *++store = '\0'; 244 return; 245 case '\\': /* backslash */ 246 case '\'': /* single quote */ 247 *store = *fmt; 248 break; 249 case 'a': /* bell/alert */ 250 *store = '\7'; 251 break; 252 case 'b': /* backspace */ 253 *store = '\b'; 254 break; 255 case 'f': /* form-feed */ 256 *store = '\f'; 257 break; 258 case 'n': /* newline */ 259 *store = '\n'; 260 break; 261 case 'r': /* carriage-return */ 262 *store = '\r'; 263 break; 264 case 't': /* horizontal tab */ 265 *store = '\t'; 266 break; 267 case 'v': /* vertical tab */ 268 *store = '\13'; 269 break; 270 /* octal constant */ 271 case '0': case '1': case '2': case '3': 272 case '4': case '5': case '6': case '7': 273 for (c = 3, value = 0; 274 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 275 value <<= 3; 276 value += *fmt - '0'; 277 } 278 --fmt; 279 *store = value; 280 break; 281 default: 282 *store = *fmt; 283 break; 284 } 285 } 286 *store = '\0'; 287 } 288 289 static int 290 getchr() 291 { 292 if (!*gargv) 293 return ('\0'); 294 return ((int)**gargv++); 295 } 296 297 static char * 298 getstr() 299 { 300 if (!*gargv) 301 return (""); 302 return (*gargv++); 303 } 304 305 static char *Number = "+-.0123456789"; 306 static int 307 getint(ip) 308 int *ip; 309 { 310 long val; 311 312 if (getlong(&val)) 313 return (1); 314 if (val > INT_MAX) { 315 warnx("%s", *gargv, strerror(ERANGE)); 316 return (1); 317 } 318 *ip = val; 319 return (0); 320 } 321 322 static int 323 getlong(lp) 324 long *lp; 325 { 326 long val; 327 char *ep; 328 329 if (!*gargv) { 330 *lp = 0; 331 return (0); 332 } 333 if (strchr(Number, **gargv)) { 334 errno = 0; 335 val = strtol(*gargv, &ep, 0); 336 if (*ep != '\0') { 337 warnx("%s: illegal number", *gargv, NULL); 338 return (1); 339 } 340 if (errno == ERANGE) 341 if (val == LONG_MAX) { 342 warnx("%s", *gargv, strerror(ERANGE)); 343 return (1); 344 } 345 if (val == LONG_MIN) { 346 warnx("%s", *gargv, strerror(ERANGE)); 347 return (1); 348 } 349 350 *lp = val; 351 ++gargv; 352 return (0); 353 } 354 *lp = (long)asciicode(); 355 return (0); 356 } 357 358 static double 359 getdouble() 360 { 361 if (!*gargv) 362 return ((double)0); 363 if (strchr(Number, **gargv)) 364 return (atof(*gargv++)); 365 return ((double)asciicode()); 366 } 367 368 static int 369 asciicode() 370 { 371 register int ch; 372 373 ch = **gargv; 374 if (ch == '\'' || ch == '"') 375 ch = (*gargv)[1]; 376 ++gargv; 377 return (ch); 378 } 379 380 static void 381 usage() 382 { 383 (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 384 } 385