xref: /csrg-svn/usr.bin/printf/printf.c (revision 36879)
136725Sbostic /*
236725Sbostic  * Copyright (c) 1989 The Regents of the University of California.
336725Sbostic  * All rights reserved.
436725Sbostic  *
536725Sbostic  * Redistribution and use in source and binary forms are permitted
636725Sbostic  * provided that the above copyright notice and this paragraph are
736725Sbostic  * duplicated in all such forms and that any documentation,
836725Sbostic  * advertising materials, and other materials related to such
936725Sbostic  * distribution and use acknowledge that the software was developed
1036725Sbostic  * by the University of California, Berkeley.  The name of the
1136725Sbostic  * University may not be used to endorse or promote products derived
1236725Sbostic  * from this software without specific prior written permission.
1336725Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1436725Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1536725Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1636725Sbostic  */
1736725Sbostic 
1836725Sbostic #ifndef lint
1936725Sbostic char copyright[] =
2036725Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
2136725Sbostic  All rights reserved.\n";
2236725Sbostic #endif /* not lint */
2336725Sbostic 
2436725Sbostic #ifndef lint
25*36879Sbostic static char sccsid[] = "@(#)printf.c	5.5 (Berkeley) 02/23/89";
2636725Sbostic #endif /* not lint */
2736725Sbostic 
2836725Sbostic #include <sys/types.h>
2936725Sbostic #include <stdio.h>
3036725Sbostic 
3136725Sbostic #define PF(f, func) { \
3236725Sbostic 	if (fieldwidth) \
3336725Sbostic 		if (precision) \
3436811Sbostic 			(void)printf(f, fieldwidth, precision, func); \
3536725Sbostic 		else \
3636811Sbostic 			(void)printf(f, fieldwidth, func); \
3736725Sbostic 	else if (precision) \
3836811Sbostic 		(void)printf(f, precision, func); \
3936725Sbostic 	else \
4036811Sbostic 		(void)printf(f, func); \
4136725Sbostic }
4236725Sbostic 
4336725Sbostic char **gargv;
4436725Sbostic 
4536725Sbostic main(argc, argv)
4636725Sbostic 	int argc;
4736725Sbostic 	char **argv;
4836725Sbostic {
4936725Sbostic 	static char *skip1, *skip2;
5036725Sbostic 	register char *format, *fmt, *start;
5136811Sbostic 	register int end, fieldwidth, precision;
5236725Sbostic 	char convch, nextch, *getstr(), *index(), *mklong();
5336725Sbostic 	double getdouble();
5436725Sbostic 	long getlong();
5536725Sbostic 
5636725Sbostic 	if (argc < 2) {
5736725Sbostic 		fprintf(stderr, "usage: printf format [arg ...]\n");
5836811Sbostic 		exit(1);
5936725Sbostic 	}
6036725Sbostic 
6136725Sbostic 	/*
6236725Sbostic 	 * Basic algorithm is to scan the format string for conversion
6336725Sbostic 	 * specifications -- once one is found, find out if the field
6436725Sbostic 	 * width or precision is a '*'; if it is, gather up value.  Note,
6536725Sbostic 	 * format strings are reused as necessary to use up the provided
6636725Sbostic 	 * arguments, arguments of zero/null string are provided to use
6736725Sbostic 	 * up the format string.
6836725Sbostic 	 */
6936725Sbostic 	skip1 = "#-+ 0";
7036725Sbostic 	skip2 = "*0123456789";
7136725Sbostic 
7236725Sbostic 	escape(fmt = format = *++argv);		/* backslash interpretation */
7336725Sbostic 	gargv = ++argv;
7436811Sbostic 	for (;;) {
7536725Sbostic 		end = 0;
7636725Sbostic 		/* find next format specification */
7736725Sbostic next:		for (start = fmt;; ++fmt) {
7836725Sbostic 			if (!*fmt) {
7936725Sbostic 				/* avoid infinite loop */
8036725Sbostic 				if (end == 1) {
8136725Sbostic 					fprintf(stderr,
8236725Sbostic 					    "printf: missing format character.\n");
8336811Sbostic 					exit(1);
8436725Sbostic 				}
8536725Sbostic 				end = 1;
8636725Sbostic 				if (fmt > start)
8736811Sbostic 					(void)printf("%s", start);
8836725Sbostic 				if (!*gargv)
8936811Sbostic 					exit(0);
9036725Sbostic 				fmt = format;
9136725Sbostic 				goto next;
9236725Sbostic 			}
9336725Sbostic 			/* %% prints a % */
9436725Sbostic 			if (*fmt == '%' && *++fmt != '%')
9536725Sbostic 				break;
9636725Sbostic 		}
9736725Sbostic 
9836725Sbostic 		/* skip to field width */
9936725Sbostic 		for (; index(skip1, *fmt); ++fmt);
100*36879Sbostic 		fieldwidth = *fmt == '*' ? getint() : 0;
101*36879Sbostic 
102*36879Sbostic 		/* skip to possible '.', get following precision */
10336725Sbostic 		for (; index(skip2, *fmt); ++fmt);
10436725Sbostic 		if (*fmt == '.')
10536725Sbostic 			++fmt;
106*36879Sbostic 		precision = *fmt == '*' ? getint() : 0;
107*36879Sbostic 
10836725Sbostic 		/* skip to conversion char */
10936725Sbostic 		for (; index(skip2, *fmt); ++fmt);
11036725Sbostic 		if (!*fmt) {
11136725Sbostic 			fprintf(stderr, "printf: missing format character.\n");
11236811Sbostic 			exit(1);
11336725Sbostic 		}
11436725Sbostic 
11536725Sbostic 		convch = *fmt;
11636725Sbostic 		nextch = *++fmt;
11736725Sbostic 		*fmt = '\0';
11836725Sbostic 		switch(convch) {
11936725Sbostic 		case 'c': {
12036725Sbostic 			char p = getchr();
12136725Sbostic 			PF(start, p);
12236725Sbostic 			break;
12336725Sbostic 		}
12436725Sbostic 		case 's': {
12536725Sbostic 			char *p = getstr();
12636725Sbostic 			PF(start, p);
12736725Sbostic 			break;
12836725Sbostic 		}
12936729Sbostic 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
13036725Sbostic 			char *f = mklong(start, convch);
13136725Sbostic 			long p = getlong();
13236725Sbostic 			PF(f, p);
13336725Sbostic 			break;
13436725Sbostic 		}
13536725Sbostic 		case 'e': case 'E': case 'f': case 'g': case 'G': {
13636725Sbostic 			double p = getdouble();
13736725Sbostic 			PF(start, p);
13836725Sbostic 			break;
13936725Sbostic 		}
14036725Sbostic 		default:
14136725Sbostic 			fprintf(stderr, "printf: illegal format character.\n");
14236811Sbostic 			exit(1);
14336725Sbostic 		}
14436725Sbostic 		*fmt = nextch;
14536725Sbostic 	}
14636725Sbostic 	/* NOTREACHED */
14736725Sbostic }
14836725Sbostic 
14936725Sbostic char *
15036725Sbostic mklong(str, ch)
15136725Sbostic 	char *str, ch;
15236725Sbostic {
15336725Sbostic 	int len;
15436725Sbostic 	char *copy, *malloc();
15536725Sbostic 
15636725Sbostic 	len = strlen(str) + 2;
15736725Sbostic 	if (!(copy = malloc((u_int)len))) {	/* never freed; XXX */
15836725Sbostic 		fprintf(stderr, "printf: out of memory.\n");
15936811Sbostic 		exit(1);
16036725Sbostic 	}
16136725Sbostic 	bcopy(str, copy, len - 3);
16236725Sbostic 	copy[len - 3] = 'l';
16336725Sbostic 	copy[len - 2] = ch;
16436725Sbostic 	copy[len - 1] = '\0';
16536725Sbostic 	return(copy);
16636725Sbostic }
16736725Sbostic 
16836725Sbostic escape(fmt)
16936725Sbostic 	register char *fmt;
17036725Sbostic {
17136725Sbostic 	register char *store;
17236725Sbostic 	register int value, c;
17336725Sbostic 
17436725Sbostic 	for (store = fmt; c = *fmt; ++fmt, ++store) {
17536725Sbostic 		if (c != '\\') {
17636725Sbostic 			*store = c;
17736725Sbostic 			continue;
17836725Sbostic 		}
17936725Sbostic 		switch (*++fmt) {
18036725Sbostic 		case '\0':		/* EOS, user error */
18136725Sbostic 			*store = '\\';
18236725Sbostic 			*++store = '\0';
18336725Sbostic 			return;
18436725Sbostic 		case '\\':		/* backslash */
18536728Sbostic 		case '\'':		/* single quote */
18636728Sbostic 			*store = *fmt;
18736725Sbostic 			break;
18836725Sbostic 		case 'a':		/* bell/alert */
18936725Sbostic 			*store = '\7';
19036725Sbostic 			break;
19136725Sbostic 		case 'b':		/* backspace */
19236725Sbostic 			*store = '\b';
19336725Sbostic 			break;
19436725Sbostic 		case 'f':		/* form-feed */
19536725Sbostic 			*store = '\f';
19636725Sbostic 			break;
19736725Sbostic 		case 'n':		/* newline */
19836725Sbostic 			*store = '\n';
19936725Sbostic 			break;
20036725Sbostic 		case 'r':		/* carriage-return */
20136725Sbostic 			*store = '\r';
20236725Sbostic 			break;
20336725Sbostic 		case 't':		/* horizontal tab */
20436725Sbostic 			*store = '\t';
20536725Sbostic 			break;
20636725Sbostic 		case 'v':		/* vertical tab */
20736725Sbostic 			*store = '\13';
20836725Sbostic 			break;
20936725Sbostic 					/* octal constant */
21036725Sbostic 		case '0': case '1': case '2': case '3':
21136725Sbostic 		case '4': case '5': case '6': case '7':
21236725Sbostic 			for (c = 3, value = 0;
21336725Sbostic 			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
21436725Sbostic 				value <<= 3;
21536725Sbostic 				value += *fmt - '0';
21636725Sbostic 			}
21736725Sbostic 			--fmt;
21836725Sbostic 			*store = value;
21936725Sbostic 			break;
22036725Sbostic 		default:
22136725Sbostic 			*store = *fmt;
22236725Sbostic 			break;
22336725Sbostic 		}
22436725Sbostic 	}
22536725Sbostic 	*store = '\0';
22636725Sbostic }
22736725Sbostic 
22836725Sbostic getchr()
22936725Sbostic {
23036725Sbostic 	if (!*gargv)
23136725Sbostic 		return((int)'\0');
23236725Sbostic 	return((int)**gargv++);
23336725Sbostic }
23436725Sbostic 
23536725Sbostic char *
23636725Sbostic getstr()
23736725Sbostic {
23836725Sbostic 	if (!*gargv)
23936725Sbostic 		return("");
24036725Sbostic 	return(*gargv++);
24136725Sbostic }
24236725Sbostic 
24336725Sbostic static char *number = "+-0123456789";
24436725Sbostic getint()
24536725Sbostic {
24636725Sbostic 	if (!*gargv)
24736725Sbostic 		return(0);
24836725Sbostic 	if (index(number, **gargv))
24936725Sbostic 		return(atoi(*gargv++));
25036725Sbostic 	return(asciicode());
25136725Sbostic }
25236725Sbostic 
25336725Sbostic long
25436725Sbostic getlong()
25536725Sbostic {
25636725Sbostic 	long atol();
25736725Sbostic 
25836725Sbostic 	if (!*gargv)
25936725Sbostic 		return((long)0);
26036725Sbostic 	if (index(number, **gargv))
26136725Sbostic 		return(atol(*gargv++));
26236725Sbostic 	return((long)asciicode());
26336725Sbostic }
26436725Sbostic 
26536725Sbostic double
26636725Sbostic getdouble()
26736725Sbostic {
26836725Sbostic 	double atof();
26936725Sbostic 
27036725Sbostic 	if (!*gargv)
27136725Sbostic 		return((double)0);
27236725Sbostic 	if (index(number, **gargv))
27336725Sbostic 		return(atof(*gargv++));
27436725Sbostic 	return((double)asciicode());
27536725Sbostic }
27636725Sbostic 
27736725Sbostic asciicode()
27836725Sbostic {
27936725Sbostic 	register char ch;
28036725Sbostic 
28136725Sbostic 	ch = **gargv;
28236725Sbostic 	if (ch == '\'' || ch == '"')
28336725Sbostic 		ch = (*gargv)[1];
28436725Sbostic 	++gargv;
28536725Sbostic 	return(ch);
28636725Sbostic }
287