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