xref: /original-bsd/usr.bin/printf/printf.c (revision 7eb91141)
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