xref: /csrg-svn/usr.bin/printf/printf.c (revision 42758)
136725Sbostic /*
236725Sbostic  * Copyright (c) 1989 The Regents of the University of California.
336725Sbostic  * All rights reserved.
436725Sbostic  *
5*42758Sbostic  * %sccs.include.redist.c%
636725Sbostic  */
736725Sbostic 
836725Sbostic #ifndef lint
936725Sbostic char copyright[] =
1036725Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
1136725Sbostic  All rights reserved.\n";
1236725Sbostic #endif /* not lint */
1336725Sbostic 
1436725Sbostic #ifndef lint
15*42758Sbostic static char sccsid[] = "@(#)printf.c	5.9 (Berkeley) 06/01/90";
1636725Sbostic #endif /* not lint */
1736725Sbostic 
1836725Sbostic #include <sys/types.h>
1936725Sbostic #include <stdio.h>
2036725Sbostic 
2136725Sbostic #define PF(f, func) { \
2236725Sbostic 	if (fieldwidth) \
2336725Sbostic 		if (precision) \
2436811Sbostic 			(void)printf(f, fieldwidth, precision, func); \
2536725Sbostic 		else \
2636811Sbostic 			(void)printf(f, fieldwidth, func); \
2736725Sbostic 	else if (precision) \
2836811Sbostic 		(void)printf(f, precision, func); \
2936725Sbostic 	else \
3036811Sbostic 		(void)printf(f, func); \
3136725Sbostic }
3236725Sbostic 
3336725Sbostic char **gargv;
3436725Sbostic 
3536725Sbostic main(argc, argv)
3636725Sbostic 	int argc;
3736725Sbostic 	char **argv;
3836725Sbostic {
3936725Sbostic 	static char *skip1, *skip2;
4036725Sbostic 	register char *format, *fmt, *start;
4136811Sbostic 	register int end, fieldwidth, precision;
4236725Sbostic 	char convch, nextch, *getstr(), *index(), *mklong();
4336725Sbostic 	double getdouble();
4436725Sbostic 	long getlong();
4536725Sbostic 
4636725Sbostic 	if (argc < 2) {
4736725Sbostic 		fprintf(stderr, "usage: printf format [arg ...]\n");
4836811Sbostic 		exit(1);
4936725Sbostic 	}
5036725Sbostic 
5136725Sbostic 	/*
5236725Sbostic 	 * Basic algorithm is to scan the format string for conversion
5336725Sbostic 	 * specifications -- once one is found, find out if the field
5436725Sbostic 	 * width or precision is a '*'; if it is, gather up value.  Note,
5536725Sbostic 	 * format strings are reused as necessary to use up the provided
5636725Sbostic 	 * arguments, arguments of zero/null string are provided to use
5736725Sbostic 	 * up the format string.
5836725Sbostic 	 */
5936725Sbostic 	skip1 = "#-+ 0";
6036725Sbostic 	skip2 = "*0123456789";
6136725Sbostic 
6236725Sbostic 	escape(fmt = format = *++argv);		/* backslash interpretation */
6336725Sbostic 	gargv = ++argv;
6436811Sbostic 	for (;;) {
6536725Sbostic 		end = 0;
6636725Sbostic 		/* find next format specification */
6736725Sbostic next:		for (start = fmt;; ++fmt) {
6836725Sbostic 			if (!*fmt) {
6936725Sbostic 				/* avoid infinite loop */
7036725Sbostic 				if (end == 1) {
7136725Sbostic 					fprintf(stderr,
7236725Sbostic 					    "printf: missing format character.\n");
7336811Sbostic 					exit(1);
7436725Sbostic 				}
7536725Sbostic 				end = 1;
7636725Sbostic 				if (fmt > start)
7736811Sbostic 					(void)printf("%s", start);
7836725Sbostic 				if (!*gargv)
7936811Sbostic 					exit(0);
8036725Sbostic 				fmt = format;
8136725Sbostic 				goto next;
8236725Sbostic 			}
8336725Sbostic 			/* %% prints a % */
8438480Sbostic 			if (*fmt == '%') {
8538480Sbostic 				if (*++fmt != '%')
8638480Sbostic 					break;
8738480Sbostic 				*fmt++ = '\0';
8838480Sbostic 				(void)printf("%s", start);
8938480Sbostic 				goto next;
9038480Sbostic 			}
9136725Sbostic 		}
9236725Sbostic 
9336725Sbostic 		/* skip to field width */
9436725Sbostic 		for (; index(skip1, *fmt); ++fmt);
9536879Sbostic 		fieldwidth = *fmt == '*' ? getint() : 0;
9636879Sbostic 
9736879Sbostic 		/* skip to possible '.', get following precision */
9836725Sbostic 		for (; index(skip2, *fmt); ++fmt);
9936725Sbostic 		if (*fmt == '.')
10036725Sbostic 			++fmt;
10136879Sbostic 		precision = *fmt == '*' ? getint() : 0;
10236879Sbostic 
10336725Sbostic 		/* skip to conversion char */
10436725Sbostic 		for (; index(skip2, *fmt); ++fmt);
10536725Sbostic 		if (!*fmt) {
10636725Sbostic 			fprintf(stderr, "printf: missing format character.\n");
10736811Sbostic 			exit(1);
10836725Sbostic 		}
10936725Sbostic 
11036725Sbostic 		convch = *fmt;
11136725Sbostic 		nextch = *++fmt;
11236725Sbostic 		*fmt = '\0';
11336725Sbostic 		switch(convch) {
11436725Sbostic 		case 'c': {
11536725Sbostic 			char p = getchr();
11636725Sbostic 			PF(start, p);
11736725Sbostic 			break;
11836725Sbostic 		}
11936725Sbostic 		case 's': {
12036725Sbostic 			char *p = getstr();
12136725Sbostic 			PF(start, p);
12236725Sbostic 			break;
12336725Sbostic 		}
12436729Sbostic 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
12536725Sbostic 			char *f = mklong(start, convch);
12636725Sbostic 			long p = getlong();
12736725Sbostic 			PF(f, p);
12836725Sbostic 			break;
12936725Sbostic 		}
13036725Sbostic 		case 'e': case 'E': case 'f': case 'g': case 'G': {
13136725Sbostic 			double p = getdouble();
13236725Sbostic 			PF(start, p);
13336725Sbostic 			break;
13436725Sbostic 		}
13536725Sbostic 		default:
13636725Sbostic 			fprintf(stderr, "printf: illegal format character.\n");
13736811Sbostic 			exit(1);
13836725Sbostic 		}
13936725Sbostic 		*fmt = nextch;
14036725Sbostic 	}
14136725Sbostic 	/* NOTREACHED */
14236725Sbostic }
14336725Sbostic 
14436725Sbostic char *
14536725Sbostic mklong(str, ch)
14636725Sbostic 	char *str, ch;
14736725Sbostic {
14836725Sbostic 	int len;
14936725Sbostic 	char *copy, *malloc();
15036725Sbostic 
15136725Sbostic 	len = strlen(str) + 2;
15236725Sbostic 	if (!(copy = malloc((u_int)len))) {	/* never freed; XXX */
15336725Sbostic 		fprintf(stderr, "printf: out of memory.\n");
15436811Sbostic 		exit(1);
15536725Sbostic 	}
15636725Sbostic 	bcopy(str, copy, len - 3);
15736725Sbostic 	copy[len - 3] = 'l';
15836725Sbostic 	copy[len - 2] = ch;
15936725Sbostic 	copy[len - 1] = '\0';
16036725Sbostic 	return(copy);
16136725Sbostic }
16236725Sbostic 
16336725Sbostic escape(fmt)
16436725Sbostic 	register char *fmt;
16536725Sbostic {
16636725Sbostic 	register char *store;
16736725Sbostic 	register int value, c;
16836725Sbostic 
16936725Sbostic 	for (store = fmt; c = *fmt; ++fmt, ++store) {
17036725Sbostic 		if (c != '\\') {
17136725Sbostic 			*store = c;
17236725Sbostic 			continue;
17336725Sbostic 		}
17436725Sbostic 		switch (*++fmt) {
17536725Sbostic 		case '\0':		/* EOS, user error */
17636725Sbostic 			*store = '\\';
17736725Sbostic 			*++store = '\0';
17836725Sbostic 			return;
17936725Sbostic 		case '\\':		/* backslash */
18036728Sbostic 		case '\'':		/* single quote */
18136728Sbostic 			*store = *fmt;
18236725Sbostic 			break;
18336725Sbostic 		case 'a':		/* bell/alert */
18436725Sbostic 			*store = '\7';
18536725Sbostic 			break;
18636725Sbostic 		case 'b':		/* backspace */
18736725Sbostic 			*store = '\b';
18836725Sbostic 			break;
18936725Sbostic 		case 'f':		/* form-feed */
19036725Sbostic 			*store = '\f';
19136725Sbostic 			break;
19236725Sbostic 		case 'n':		/* newline */
19336725Sbostic 			*store = '\n';
19436725Sbostic 			break;
19536725Sbostic 		case 'r':		/* carriage-return */
19636725Sbostic 			*store = '\r';
19736725Sbostic 			break;
19836725Sbostic 		case 't':		/* horizontal tab */
19936725Sbostic 			*store = '\t';
20036725Sbostic 			break;
20136725Sbostic 		case 'v':		/* vertical tab */
20236725Sbostic 			*store = '\13';
20336725Sbostic 			break;
20436725Sbostic 					/* octal constant */
20536725Sbostic 		case '0': case '1': case '2': case '3':
20636725Sbostic 		case '4': case '5': case '6': case '7':
20736725Sbostic 			for (c = 3, value = 0;
20836725Sbostic 			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
20936725Sbostic 				value <<= 3;
21036725Sbostic 				value += *fmt - '0';
21136725Sbostic 			}
21236725Sbostic 			--fmt;
21336725Sbostic 			*store = value;
21436725Sbostic 			break;
21536725Sbostic 		default:
21636725Sbostic 			*store = *fmt;
21736725Sbostic 			break;
21836725Sbostic 		}
21936725Sbostic 	}
22036725Sbostic 	*store = '\0';
22136725Sbostic }
22236725Sbostic 
22336725Sbostic getchr()
22436725Sbostic {
22536725Sbostic 	if (!*gargv)
22636725Sbostic 		return((int)'\0');
22736725Sbostic 	return((int)**gargv++);
22836725Sbostic }
22936725Sbostic 
23036725Sbostic char *
23136725Sbostic getstr()
23236725Sbostic {
23336725Sbostic 	if (!*gargv)
23436725Sbostic 		return("");
23536725Sbostic 	return(*gargv++);
23636725Sbostic }
23736725Sbostic 
23838481Sbostic static char *number = "+-.0123456789";
23936725Sbostic getint()
24036725Sbostic {
24136725Sbostic 	if (!*gargv)
24236725Sbostic 		return(0);
24336725Sbostic 	if (index(number, **gargv))
24436725Sbostic 		return(atoi(*gargv++));
24536725Sbostic 	return(asciicode());
24636725Sbostic }
24736725Sbostic 
24836725Sbostic long
24936725Sbostic getlong()
25036725Sbostic {
25136725Sbostic 	long atol();
25236725Sbostic 
25336725Sbostic 	if (!*gargv)
25436725Sbostic 		return((long)0);
25536725Sbostic 	if (index(number, **gargv))
25640959Sbostic 		return(strtol(*gargv++, (char **)NULL, 0));
25736725Sbostic 	return((long)asciicode());
25836725Sbostic }
25936725Sbostic 
26036725Sbostic double
26136725Sbostic getdouble()
26236725Sbostic {
26336725Sbostic 	double atof();
26436725Sbostic 
26536725Sbostic 	if (!*gargv)
26636725Sbostic 		return((double)0);
26736725Sbostic 	if (index(number, **gargv))
26836725Sbostic 		return(atof(*gargv++));
26936725Sbostic 	return((double)asciicode());
27036725Sbostic }
27136725Sbostic 
27236725Sbostic asciicode()
27336725Sbostic {
27436725Sbostic 	register char ch;
27536725Sbostic 
27636725Sbostic 	ch = **gargv;
27736725Sbostic 	if (ch == '\'' || ch == '"')
27836725Sbostic 		ch = (*gargv)[1];
27936725Sbostic 	++gargv;
28036725Sbostic 	return(ch);
28136725Sbostic }
282