1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #if !defined(BUILTIN) && !defined(SHELL)
9 #ifndef lint
10 static char copyright[] =
11 "@(#) Copyright (c) 1989, 1993\n\
12 The Regents of the University of California. All rights reserved.\n";
13 #endif /* not lint */
14 #endif
15
16 #ifndef lint
17 static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 03/22/95";
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
progprintf(argc,argv)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 *
mklong(str,ch)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
escape(fmt)229 escape(fmt)
230 register char *fmt;
231 {
232 register char *store;
233 register int value, c;
234
235 for (store = fmt; (c = *fmt) != '\0'; ++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
getchr()290 getchr()
291 {
292 if (!*gargv)
293 return ('\0');
294 return ((int)**gargv++);
295 }
296
297 static char *
getstr()298 getstr()
299 {
300 if (!*gargv)
301 return ("");
302 return (*gargv++);
303 }
304
305 static char *Number = "+-.0123456789";
306 static int
getint(ip)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: %s", *gargv, strerror(ERANGE));
316 return (1);
317 }
318 *ip = val;
319 return (0);
320 }
321
322 static int
getlong(lp)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: %s", *gargv, strerror(ERANGE));
343 return (1);
344 }
345 if (val == LONG_MIN) {
346 warnx("%s: %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
getdouble()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
asciicode()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
usage()381 usage()
382 {
383 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
384 }
385