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