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