printf.c revision 95300
1/*
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#if !defined(BUILTIN) && !defined(SHELL)
35#ifndef lint
36static char const copyright[] =
37"@(#) Copyright (c) 1989, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40#endif
41
42#ifndef lint
43#if 0
44static char const sccsid[] = "@(#)printf.c	8.1 (Berkeley) 7/20/93";
45#endif
46static const char rcsid[] =
47  "$FreeBSD: head/usr.bin/printf/printf.c 95300 2002-04-23 02:56:16Z jmallett $";
48#endif /* not lint */
49
50#include <sys/types.h>
51
52#include <err.h>
53#include <errno.h>
54#include <limits.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59
60#ifdef SHELL
61#define main printfcmd
62#include "bltin/bltin.h"
63#include "memalloc.h"
64#else
65#define	warnx1(a, b, c)		warnx(a)
66#define	warnx2(a, b, c)		warnx(a, b)
67#define	warnx3(a, b, c)		warnx(a, b, c)
68#endif
69
70#ifndef BUILTIN
71#include <locale.h>
72#endif
73
74#define PF(f, func) { \
75	char *b = NULL; \
76	if (fieldwidth) \
77		if (precision) \
78			(void)asprintf(&b, f, fieldwidth, precision, func); \
79		else \
80			(void)asprintf(&b, f, fieldwidth, func); \
81	else if (precision) \
82		(void)asprintf(&b, f, precision, func); \
83	else \
84		(void)asprintf(&b, f, func); \
85	if (b) { \
86		(void)fputs(b, stdout); \
87		free(b); \
88	} \
89}
90
91static int	 asciicode(void);
92static int	 escape(char *);
93static int	 getchr(void);
94static double	 getdouble(void);
95static int	 getint(int *);
96static int	 getquad(quad_t *);
97static const char	*getstr(void);
98static char	*mklong(char *, int);
99static void	 usage(void);
100
101static char **gargv;
102
103int
104#ifdef BUILTIN
105progprintf(argc, argv)
106#else
107main(argc, argv)
108#endif
109	int argc;
110	char *argv[];
111{
112	static const char *skip1, *skip2;
113	int ch, chopped, end, fieldwidth, precision;
114	char convch, nextch, *format, *fmt, *start;
115
116#ifndef BUILTIN
117	(void) setlocale(LC_NUMERIC, "");
118#endif
119	while ((ch = getopt(argc, argv, "")) != -1)
120		switch (ch) {
121		case '?':
122		default:
123			usage();
124			return (1);
125		}
126	argc -= optind;
127	argv += optind;
128
129	if (argc < 1) {
130		usage();
131		return (1);
132	}
133
134	/*
135	 * Basic algorithm is to scan the format string for conversion
136	 * specifications -- once one is found, find out if the field
137	 * width or precision is a '*'; if it is, gather up value.  Note,
138	 * format strings are reused as necessary to use up the provided
139	 * arguments, arguments of zero/null string are provided to use
140	 * up the format string.
141	 */
142	skip1 = "#-+ 0";
143	skip2 = "0123456789";
144
145	chopped = escape(fmt = format = *argv);	/* backslash interpretation */
146	gargv = ++argv;
147	for (;;) {
148		end = 0;
149		/* find next format specification */
150next:		for (start = fmt;; ++fmt) {
151			if (!*fmt) {
152				/* avoid infinite loop */
153				if (chopped) {
154					(void)printf("%s", start);
155					return (0);
156				}
157				if (end == 1) {
158					warnx1("missing format character",
159					    NULL, NULL);
160					return (1);
161				}
162				end = 1;
163				if (fmt > start)
164					(void)printf("%s", start);
165				if (!*gargv)
166					return (0);
167				fmt = format;
168				goto next;
169			}
170			/* %% prints a % */
171			if (*fmt == '%') {
172				if (*++fmt != '%')
173					break;
174				*fmt++ = '\0';
175				(void)printf("%s", start);
176				goto next;
177			}
178		}
179
180		/* skip to field width */
181		for (; strchr(skip1, *fmt); ++fmt);
182		if (*fmt == '*') {
183			if (getint(&fieldwidth))
184				return (1);
185			++fmt;
186		} else {
187			fieldwidth = 0;
188
189			/* skip to possible '.', get following precision */
190			for (; strchr(skip2, *fmt); ++fmt);
191		}
192		if (*fmt == '.') {
193			/* precision present? */
194			++fmt;
195			if (*fmt == '*') {
196				if (getint(&precision))
197					return (1);
198				++fmt;
199			} else {
200				precision = 0;
201
202				/* skip to conversion char */
203				for (; strchr(skip2, *fmt); ++fmt);
204			}
205		} else
206			precision = 0;
207		if (!*fmt) {
208			warnx1("missing format character", NULL, NULL);
209			return (1);
210		}
211
212		convch = *fmt;
213		nextch = *++fmt;
214		*fmt = '\0';
215		switch(convch) {
216		case 'b': {
217			char *p;
218			int getout;
219
220			if ((p = strdup(getstr())) == NULL)
221				err(1, NULL);
222			getout = escape(p);
223			*(fmt - 1) = 's';
224			PF(start, p)
225			*(fmt - 1) = 'b';
226			free(p);
227			if (getout)
228				return (0);
229			break;
230		}
231		case 'c': {
232			char p;
233
234			p = getchr();
235			PF(start, p);
236			break;
237		}
238		case 's': {
239			const char *p;
240
241			p = getstr();
242			PF(start, p);
243			break;
244		}
245		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
246			quad_t p;
247			char *f;
248
249			if ((f = mklong(start, convch)) != NULL &&
250			    !getquad(&p)) {
251				PF(f, p);
252			}
253			break;
254		}
255		case 'e': case 'E': case 'f': case 'g': case 'G': {
256			double p;
257
258			p = getdouble();
259			PF(start, p);
260			break;
261		}
262		default:
263			warnx2("illegal format character %c", convch, NULL);
264			return (1);
265		}
266		*fmt = nextch;
267	}
268	/* NOTREACHED */
269}
270
271static char *
272mklong(str, ch)
273	char *str;
274	int ch;
275{
276	static char *copy;
277	static size_t copy_size;
278	size_t len, newlen;
279	char *newcopy;
280
281	len = strlen(str) + 2;
282	if (len > copy_size) {
283		newlen = ((len + 1023) >> 10) << 10;
284#ifdef SHELL
285		if ((newcopy = ckrealloc(copy, newlen)) == NULL)
286#else
287		if ((newcopy = realloc(copy, newlen)) == NULL)
288#endif
289			return (NULL);
290		copy = newcopy;
291		copy_size = newlen;
292	}
293
294	memmove(copy, str, len - 3);
295	copy[len - 3] = 'q';
296	copy[len - 2] = ch;
297	copy[len - 1] = '\0';
298	return (copy);
299}
300
301static int
302escape(fmt)
303	register char *fmt;
304{
305	register char *store;
306	register int value, c;
307
308	for (store = fmt; (c = *fmt); ++fmt, ++store) {
309		if (c != '\\') {
310			*store = c;
311			continue;
312		}
313		switch (*++fmt) {
314		case '\0':		/* EOS, user error */
315			*store = '\\';
316			*++store = '\0';
317			return (0);
318		case '\\':		/* backslash */
319		case '\'':		/* single quote */
320			*store = *fmt;
321			break;
322		case 'a':		/* bell/alert */
323			*store = '\7';
324			break;
325		case 'b':		/* backspace */
326			*store = '\b';
327			break;
328		case 'c':
329			*store = '\0';
330			return (1);
331		case 'f':		/* form-feed */
332			*store = '\f';
333			break;
334		case 'n':		/* newline */
335			*store = '\n';
336			break;
337		case 'r':		/* carriage-return */
338			*store = '\r';
339			break;
340		case 't':		/* horizontal tab */
341			*store = '\t';
342			break;
343		case 'v':		/* vertical tab */
344			*store = '\13';
345			break;
346					/* octal constant */
347		case '0': case '1': case '2': case '3':
348		case '4': case '5': case '6': case '7':
349			for (c = *fmt == '0' ? 4 : 3, value = 0;
350			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
351				value <<= 3;
352				value += *fmt - '0';
353			}
354			--fmt;
355			*store = value;
356			break;
357		default:
358			*store = *fmt;
359			break;
360		}
361	}
362	*store = '\0';
363	return (0);
364}
365
366static int
367getchr()
368{
369	if (!*gargv)
370		return ('\0');
371	return ((int)**gargv++);
372}
373
374static const char *
375getstr()
376{
377	if (!*gargv)
378		return ("");
379	return (*gargv++);
380}
381
382static int
383getint(ip)
384	int *ip;
385{
386	quad_t val;
387
388	if (getquad(&val))
389		return (1);
390	if (val < INT_MIN || val > INT_MAX)
391		warnx3("%s: %s", *gargv, strerror(ERANGE));
392	*ip = (int)val;
393	return (0);
394}
395
396static int
397getquad(lp)
398	quad_t *lp;
399{
400	quad_t val;
401	char *ep;
402
403	if (!*gargv) {
404		*lp = 0;
405		return (0);
406	}
407
408	if (**gargv == '"' || **gargv == '\'') {
409		*lp = (quad_t)asciicode();
410		return (0);
411	}
412
413	errno = 0;
414	val = strtoq(*gargv, &ep, 0);
415	if (ep == *gargv)
416		warnx2("%s: expected numeric value", *gargv, NULL);
417	else if (*ep != '\0')
418		warnx2("%s: not completely converted", *gargv, NULL);
419	if (errno == ERANGE)
420		warnx3("%s: %s", *gargv, strerror(ERANGE));
421	*lp = val;
422	++gargv;
423	return (0);
424}
425
426static double
427getdouble()
428{
429	double val;
430	char *ep;
431
432	if (!*gargv)
433		return ((double)0);
434
435	if (**gargv == '"' || **gargv == '\'') {
436		val = (double)asciicode();
437		return (val);
438	}
439
440	errno = 0;
441	val = strtod(*gargv, &ep);
442	if (ep == *gargv)
443		warnx2("%s: expected numeric value", *gargv, NULL);
444	else if (*ep != '\0')
445		warnx2("%s: not completely converted", *gargv, NULL);
446	if (errno == ERANGE)
447		warnx3("%s: %s", *gargv, strerror(ERANGE));
448	++gargv;
449	return (val);
450}
451
452static int
453asciicode()
454{
455	register int ch;
456
457	ch = **gargv;
458	if (ch == '\'' || ch == '"')
459		ch = (*gargv)[1];
460	++gargv;
461	return (ch);
462}
463
464static void
465usage()
466{
467	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
468}
469