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