xref: /csrg-svn/usr.bin/printf/printf.c (revision 68574)
136725Sbostic /*
263940Sbostic  * Copyright (c) 1989, 1993
363940Sbostic  *	The Regents of the University of California.  All rights reserved.
436725Sbostic  *
542758Sbostic  * %sccs.include.redist.c%
636725Sbostic  */
736725Sbostic 
859549Sbostic #if !defined(BUILTIN) && !defined(SHELL)
936725Sbostic #ifndef lint
1063940Sbostic static char copyright[] =
1163940Sbostic "@(#) Copyright (c) 1989, 1993\n\
1263940Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1336725Sbostic #endif /* not lint */
1450477Sbostic #endif
1536725Sbostic 
1636725Sbostic #ifndef lint
17*68574Schristos static char sccsid[] = "@(#)printf.c	8.2 (Berkeley) 03/22/95";
1836725Sbostic #endif /* not lint */
1936725Sbostic 
2059549Sbostic #include <sys/types.h>
2153564Smarc 
2259549Sbostic #include <err.h>
2350477Sbostic #include <errno.h>
2459549Sbostic #include <limits.h>
2559549Sbostic #ifdef SHELL
2659549Sbostic #define	EOF	-1
2759549Sbostic #else
2836725Sbostic #include <stdio.h>
2953564Smarc #endif
3050477Sbostic #include <stdlib.h>
3150477Sbostic #include <string.h>
3236725Sbostic 
3355444Smarc /*
3455444Smarc  * XXX
3555444Smarc  * This *has* to go away.  TK.
3655444Smarc  */
3753564Smarc #ifdef SHELL
3853564Smarc #define main printfcmd
3959551Sbostic #define warnx(a, b, c) {						\
4059549Sbostic 	char buf[64];							\
4159551Sbostic 	(void)sprintf(buf, sizeof(buf), a, b, c);			\
4259549Sbostic 	error(buf);							\
4359549Sbostic }
4455444Smarc #include "../../bin/sh/bltin/bltin.h"
4553564Smarc #endif
4653564Smarc 
4736725Sbostic #define PF(f, func) { \
4836725Sbostic 	if (fieldwidth) \
4936725Sbostic 		if (precision) \
5036811Sbostic 			(void)printf(f, fieldwidth, precision, func); \
5136725Sbostic 		else \
5236811Sbostic 			(void)printf(f, fieldwidth, func); \
5336725Sbostic 	else if (precision) \
5436811Sbostic 		(void)printf(f, precision, func); \
5536725Sbostic 	else \
5636811Sbostic 		(void)printf(f, func); \
5736725Sbostic }
5836725Sbostic 
5950477Sbostic static int	 asciicode __P((void));
6050477Sbostic static void	 escape __P((char *));
6150477Sbostic static int	 getchr __P((void));
6250477Sbostic static double	 getdouble __P((void));
6359549Sbostic static int	 getint __P((int *));
6459549Sbostic static int	 getlong __P((long *));
6550477Sbostic static char	*getstr __P((void));
6650477Sbostic static char	*mklong __P((char *, int));
6759549Sbostic static void	 usage __P((void));
6836725Sbostic 
6950477Sbostic static char **gargv;
7050477Sbostic 
7150477Sbostic int
7250477Sbostic #ifdef BUILTIN
progprintf(argc,argv)7350477Sbostic progprintf(argc, argv)
7450477Sbostic #else
7536725Sbostic main(argc, argv)
7650477Sbostic #endif
7736725Sbostic 	int argc;
7859549Sbostic 	char *argv[];
7936725Sbostic {
8059550Sbostic 	extern int optind;
8136725Sbostic 	static char *skip1, *skip2;
8259549Sbostic 	int ch, end, fieldwidth, precision;
8359549Sbostic 	char convch, nextch, *format, *fmt, *start;
8436725Sbostic 
8559549Sbostic 	while ((ch = getopt(argc, argv, "")) != EOF)
8659549Sbostic 		switch (ch) {
8759549Sbostic 		case '?':
8859549Sbostic 		default:
8959549Sbostic 			usage();
9059549Sbostic 			return (1);
9159549Sbostic 		}
9259549Sbostic 	argc -= optind;
9359549Sbostic 	argv += optind;
9459549Sbostic 
9559549Sbostic 	if (argc < 1) {
9659549Sbostic 		usage();
9750477Sbostic 		return (1);
9836725Sbostic 	}
9936725Sbostic 
10036725Sbostic 	/*
10136725Sbostic 	 * Basic algorithm is to scan the format string for conversion
10236725Sbostic 	 * specifications -- once one is found, find out if the field
10336725Sbostic 	 * width or precision is a '*'; if it is, gather up value.  Note,
10436725Sbostic 	 * format strings are reused as necessary to use up the provided
10536725Sbostic 	 * arguments, arguments of zero/null string are provided to use
10636725Sbostic 	 * up the format string.
10736725Sbostic 	 */
10836725Sbostic 	skip1 = "#-+ 0";
10936725Sbostic 	skip2 = "*0123456789";
11036725Sbostic 
11159549Sbostic 	escape(fmt = format = *argv);		/* backslash interpretation */
11236725Sbostic 	gargv = ++argv;
11336811Sbostic 	for (;;) {
11436725Sbostic 		end = 0;
11536725Sbostic 		/* find next format specification */
11636725Sbostic next:		for (start = fmt;; ++fmt) {
11736725Sbostic 			if (!*fmt) {
11836725Sbostic 				/* avoid infinite loop */
11936725Sbostic 				if (end == 1) {
12059551Sbostic 					warnx("missing format character",
12159551Sbostic 					    NULL, NULL);
12250477Sbostic 					return (1);
12336725Sbostic 				}
12436725Sbostic 				end = 1;
12536725Sbostic 				if (fmt > start)
12636811Sbostic 					(void)printf("%s", start);
12736725Sbostic 				if (!*gargv)
12850477Sbostic 					return (0);
12936725Sbostic 				fmt = format;
13036725Sbostic 				goto next;
13136725Sbostic 			}
13236725Sbostic 			/* %% prints a % */
13338480Sbostic 			if (*fmt == '%') {
13438480Sbostic 				if (*++fmt != '%')
13538480Sbostic 					break;
13638480Sbostic 				*fmt++ = '\0';
13738480Sbostic 				(void)printf("%s", start);
13838480Sbostic 				goto next;
13938480Sbostic 			}
14036725Sbostic 		}
14136725Sbostic 
14236725Sbostic 		/* skip to field width */
14359549Sbostic 		for (; strchr(skip1, *fmt); ++fmt);
14459549Sbostic 		if (*fmt == '*') {
14559549Sbostic 			if (getint(&fieldwidth))
14659549Sbostic 				return (1);
14759549Sbostic 		} else
14859549Sbostic 			fieldwidth = 0;
14936879Sbostic 
15036879Sbostic 		/* skip to possible '.', get following precision */
15159549Sbostic 		for (; strchr(skip2, *fmt); ++fmt);
15236725Sbostic 		if (*fmt == '.')
15336725Sbostic 			++fmt;
15459549Sbostic 		if (*fmt == '*') {
15559549Sbostic 			if (getint(&precision))
15659549Sbostic 				return (1);
15759549Sbostic 		} else
15859549Sbostic 			precision = 0;
15936879Sbostic 
16036725Sbostic 		/* skip to conversion char */
16159549Sbostic 		for (; strchr(skip2, *fmt); ++fmt);
16236725Sbostic 		if (!*fmt) {
16359551Sbostic 			warnx("missing format character", NULL, NULL);
16450477Sbostic 			return (1);
16536725Sbostic 		}
16636725Sbostic 
16736725Sbostic 		convch = *fmt;
16836725Sbostic 		nextch = *++fmt;
16936725Sbostic 		*fmt = '\0';
17036725Sbostic 		switch(convch) {
17136725Sbostic 		case 'c': {
17250477Sbostic 			char p;
17350477Sbostic 
17450477Sbostic 			p = getchr();
17536725Sbostic 			PF(start, p);
17636725Sbostic 			break;
17736725Sbostic 		}
17836725Sbostic 		case 's': {
17950477Sbostic 			char *p;
18050477Sbostic 
18150477Sbostic 			p = getstr();
18236725Sbostic 			PF(start, p);
18336725Sbostic 			break;
18436725Sbostic 		}
18536729Sbostic 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
18650477Sbostic 			long p;
18750477Sbostic 			char *f;
18850477Sbostic 
18950477Sbostic 			if ((f = mklong(start, convch)) == NULL)
19050477Sbostic 				return (1);
19159549Sbostic 			if (getlong(&p))
19259549Sbostic 				return (1);
19336725Sbostic 			PF(f, p);
19436725Sbostic 			break;
19536725Sbostic 		}
19636725Sbostic 		case 'e': case 'E': case 'f': case 'g': case 'G': {
19750477Sbostic 			double p;
19850477Sbostic 
19950477Sbostic 			p = getdouble();
20036725Sbostic 			PF(start, p);
20136725Sbostic 			break;
20236725Sbostic 		}
20336725Sbostic 		default:
20459551Sbostic 			warnx("illegal format character", NULL, NULL);
20550477Sbostic 			return (1);
20636725Sbostic 		}
20736725Sbostic 		*fmt = nextch;
20836725Sbostic 	}
20936725Sbostic 	/* NOTREACHED */
21036725Sbostic }
21136725Sbostic 
21250477Sbostic static char *
mklong(str,ch)21336725Sbostic mklong(str, ch)
21450477Sbostic 	char *str;
21550477Sbostic 	int ch;
21636725Sbostic {
21753564Smarc 	static char copy[64];
21836725Sbostic 	int len;
21936725Sbostic 
22036725Sbostic 	len = strlen(str) + 2;
22159549Sbostic 	memmove(copy, str, len - 3);
22253564Smarc 	copy[len - 3] = 'l';
22353564Smarc 	copy[len - 2] = ch;
22453564Smarc 	copy[len - 1] = '\0';
22559549Sbostic 	return (copy);
22636725Sbostic }
22736725Sbostic 
22850477Sbostic static void
escape(fmt)22936725Sbostic escape(fmt)
23036725Sbostic 	register char *fmt;
23136725Sbostic {
23236725Sbostic 	register char *store;
23336725Sbostic 	register int value, c;
23436725Sbostic 
235*68574Schristos 	for (store = fmt; (c = *fmt) != '\0'; ++fmt, ++store) {
23636725Sbostic 		if (c != '\\') {
23736725Sbostic 			*store = c;
23836725Sbostic 			continue;
23936725Sbostic 		}
24036725Sbostic 		switch (*++fmt) {
24136725Sbostic 		case '\0':		/* EOS, user error */
24236725Sbostic 			*store = '\\';
24336725Sbostic 			*++store = '\0';
24436725Sbostic 			return;
24536725Sbostic 		case '\\':		/* backslash */
24636728Sbostic 		case '\'':		/* single quote */
24736728Sbostic 			*store = *fmt;
24836725Sbostic 			break;
24936725Sbostic 		case 'a':		/* bell/alert */
25036725Sbostic 			*store = '\7';
25136725Sbostic 			break;
25236725Sbostic 		case 'b':		/* backspace */
25336725Sbostic 			*store = '\b';
25436725Sbostic 			break;
25536725Sbostic 		case 'f':		/* form-feed */
25636725Sbostic 			*store = '\f';
25736725Sbostic 			break;
25836725Sbostic 		case 'n':		/* newline */
25936725Sbostic 			*store = '\n';
26036725Sbostic 			break;
26136725Sbostic 		case 'r':		/* carriage-return */
26236725Sbostic 			*store = '\r';
26336725Sbostic 			break;
26436725Sbostic 		case 't':		/* horizontal tab */
26536725Sbostic 			*store = '\t';
26636725Sbostic 			break;
26736725Sbostic 		case 'v':		/* vertical tab */
26836725Sbostic 			*store = '\13';
26936725Sbostic 			break;
27036725Sbostic 					/* octal constant */
27136725Sbostic 		case '0': case '1': case '2': case '3':
27236725Sbostic 		case '4': case '5': case '6': case '7':
27336725Sbostic 			for (c = 3, value = 0;
27436725Sbostic 			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
27536725Sbostic 				value <<= 3;
27636725Sbostic 				value += *fmt - '0';
27736725Sbostic 			}
27836725Sbostic 			--fmt;
27936725Sbostic 			*store = value;
28036725Sbostic 			break;
28136725Sbostic 		default:
28236725Sbostic 			*store = *fmt;
28336725Sbostic 			break;
28436725Sbostic 		}
28536725Sbostic 	}
28636725Sbostic 	*store = '\0';
28736725Sbostic }
28836725Sbostic 
28950477Sbostic static int
getchr()29036725Sbostic getchr()
29136725Sbostic {
29236725Sbostic 	if (!*gargv)
29359549Sbostic 		return ('\0');
29459549Sbostic 	return ((int)**gargv++);
29536725Sbostic }
29636725Sbostic 
29750477Sbostic static char *
getstr()29836725Sbostic getstr()
29936725Sbostic {
30036725Sbostic 	if (!*gargv)
30159549Sbostic 		return ("");
30259549Sbostic 	return (*gargv++);
30336725Sbostic }
30436725Sbostic 
30553564Smarc static char *Number = "+-.0123456789";
30650477Sbostic static int
getint(ip)30759549Sbostic getint(ip)
30859549Sbostic 	int *ip;
30936725Sbostic {
31059549Sbostic 	long val;
31159549Sbostic 
31259549Sbostic 	if (getlong(&val))
31359549Sbostic 		return (1);
31459549Sbostic 	if (val > INT_MAX) {
31563939Sbostic 		warnx("%s: %s", *gargv, strerror(ERANGE));
31659549Sbostic 		return (1);
31759549Sbostic 	}
31859549Sbostic 	*ip = val;
31959549Sbostic 	return (0);
32036725Sbostic }
32136725Sbostic 
32259549Sbostic static int
getlong(lp)32359549Sbostic getlong(lp)
32459549Sbostic 	long *lp;
32536725Sbostic {
32659549Sbostic 	long val;
32759549Sbostic 	char *ep;
32859549Sbostic 
32959549Sbostic 	if (!*gargv) {
33059549Sbostic 		*lp = 0;
33159549Sbostic 		return (0);
33259549Sbostic 	}
33359549Sbostic 	if (strchr(Number, **gargv)) {
33459549Sbostic 		errno = 0;
33559549Sbostic 		val = strtol(*gargv, &ep, 0);
33659549Sbostic 		if (*ep != '\0') {
33759551Sbostic 			warnx("%s: illegal number", *gargv, NULL);
33859549Sbostic 			return (1);
33959549Sbostic 		}
34059549Sbostic 		if (errno == ERANGE)
34159549Sbostic 			if (val == LONG_MAX) {
34263939Sbostic 				warnx("%s: %s", *gargv, strerror(ERANGE));
34359549Sbostic 				return (1);
34459549Sbostic 			}
34559549Sbostic 			if (val == LONG_MIN) {
34663939Sbostic 				warnx("%s: %s", *gargv, strerror(ERANGE));
34759549Sbostic 				return (1);
34859549Sbostic 			}
34959549Sbostic 
35059549Sbostic 		*lp = val;
35159549Sbostic 		++gargv;
35259549Sbostic 		return (0);
35359549Sbostic 	}
35459549Sbostic 	*lp =  (long)asciicode();
35559549Sbostic 	return (0);
35636725Sbostic }
35736725Sbostic 
35850477Sbostic static double
getdouble()35936725Sbostic getdouble()
36036725Sbostic {
36136725Sbostic 	if (!*gargv)
36259549Sbostic 		return ((double)0);
36359549Sbostic 	if (strchr(Number, **gargv))
36459549Sbostic 		return (atof(*gargv++));
36559549Sbostic 	return ((double)asciicode());
36636725Sbostic }
36736725Sbostic 
36850477Sbostic static int
asciicode()36936725Sbostic asciicode()
37036725Sbostic {
37150477Sbostic 	register int ch;
37236725Sbostic 
37336725Sbostic 	ch = **gargv;
37436725Sbostic 	if (ch == '\'' || ch == '"')
37536725Sbostic 		ch = (*gargv)[1];
37636725Sbostic 	++gargv;
37759549Sbostic 	return (ch);
37836725Sbostic }
37950477Sbostic 
38050477Sbostic static void
usage()38159549Sbostic usage()
38250477Sbostic {
38359549Sbostic 	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
38450477Sbostic }
385