xref: /csrg-svn/usr.bin/printf/printf.c (revision 38480)
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*38480Sbostic static char sccsid[] = "@(#)printf.c	5.6 (Berkeley) 07/20/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 % */
94*38480Sbostic 			if (*fmt == '%') {
95*38480Sbostic 				if (*++fmt != '%')
96*38480Sbostic 					break;
97*38480Sbostic 				*fmt++ = '\0';
98*38480Sbostic 				(void)printf("%s", start);
99*38480Sbostic 				goto next;
100*38480Sbostic 			}
10136725Sbostic 		}
10236725Sbostic 
10336725Sbostic 		/* skip to field width */
10436725Sbostic 		for (; index(skip1, *fmt); ++fmt);
10536879Sbostic 		fieldwidth = *fmt == '*' ? getint() : 0;
10636879Sbostic 
10736879Sbostic 		/* skip to possible '.', get following precision */
10836725Sbostic 		for (; index(skip2, *fmt); ++fmt);
10936725Sbostic 		if (*fmt == '.')
11036725Sbostic 			++fmt;
11136879Sbostic 		precision = *fmt == '*' ? getint() : 0;
11236879Sbostic 
11336725Sbostic 		/* skip to conversion char */
11436725Sbostic 		for (; index(skip2, *fmt); ++fmt);
11536725Sbostic 		if (!*fmt) {
11636725Sbostic 			fprintf(stderr, "printf: missing format character.\n");
11736811Sbostic 			exit(1);
11836725Sbostic 		}
11936725Sbostic 
12036725Sbostic 		convch = *fmt;
12136725Sbostic 		nextch = *++fmt;
12236725Sbostic 		*fmt = '\0';
12336725Sbostic 		switch(convch) {
12436725Sbostic 		case 'c': {
12536725Sbostic 			char p = getchr();
12636725Sbostic 			PF(start, p);
12736725Sbostic 			break;
12836725Sbostic 		}
12936725Sbostic 		case 's': {
13036725Sbostic 			char *p = getstr();
13136725Sbostic 			PF(start, p);
13236725Sbostic 			break;
13336725Sbostic 		}
13436729Sbostic 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
13536725Sbostic 			char *f = mklong(start, convch);
13636725Sbostic 			long p = getlong();
13736725Sbostic 			PF(f, p);
13836725Sbostic 			break;
13936725Sbostic 		}
14036725Sbostic 		case 'e': case 'E': case 'f': case 'g': case 'G': {
14136725Sbostic 			double p = getdouble();
14236725Sbostic 			PF(start, p);
14336725Sbostic 			break;
14436725Sbostic 		}
14536725Sbostic 		default:
14636725Sbostic 			fprintf(stderr, "printf: illegal format character.\n");
14736811Sbostic 			exit(1);
14836725Sbostic 		}
14936725Sbostic 		*fmt = nextch;
15036725Sbostic 	}
15136725Sbostic 	/* NOTREACHED */
15236725Sbostic }
15336725Sbostic 
15436725Sbostic char *
15536725Sbostic mklong(str, ch)
15636725Sbostic 	char *str, ch;
15736725Sbostic {
15836725Sbostic 	int len;
15936725Sbostic 	char *copy, *malloc();
16036725Sbostic 
16136725Sbostic 	len = strlen(str) + 2;
16236725Sbostic 	if (!(copy = malloc((u_int)len))) {	/* never freed; XXX */
16336725Sbostic 		fprintf(stderr, "printf: out of memory.\n");
16436811Sbostic 		exit(1);
16536725Sbostic 	}
16636725Sbostic 	bcopy(str, copy, len - 3);
16736725Sbostic 	copy[len - 3] = 'l';
16836725Sbostic 	copy[len - 2] = ch;
16936725Sbostic 	copy[len - 1] = '\0';
17036725Sbostic 	return(copy);
17136725Sbostic }
17236725Sbostic 
17336725Sbostic escape(fmt)
17436725Sbostic 	register char *fmt;
17536725Sbostic {
17636725Sbostic 	register char *store;
17736725Sbostic 	register int value, c;
17836725Sbostic 
17936725Sbostic 	for (store = fmt; c = *fmt; ++fmt, ++store) {
18036725Sbostic 		if (c != '\\') {
18136725Sbostic 			*store = c;
18236725Sbostic 			continue;
18336725Sbostic 		}
18436725Sbostic 		switch (*++fmt) {
18536725Sbostic 		case '\0':		/* EOS, user error */
18636725Sbostic 			*store = '\\';
18736725Sbostic 			*++store = '\0';
18836725Sbostic 			return;
18936725Sbostic 		case '\\':		/* backslash */
19036728Sbostic 		case '\'':		/* single quote */
19136728Sbostic 			*store = *fmt;
19236725Sbostic 			break;
19336725Sbostic 		case 'a':		/* bell/alert */
19436725Sbostic 			*store = '\7';
19536725Sbostic 			break;
19636725Sbostic 		case 'b':		/* backspace */
19736725Sbostic 			*store = '\b';
19836725Sbostic 			break;
19936725Sbostic 		case 'f':		/* form-feed */
20036725Sbostic 			*store = '\f';
20136725Sbostic 			break;
20236725Sbostic 		case 'n':		/* newline */
20336725Sbostic 			*store = '\n';
20436725Sbostic 			break;
20536725Sbostic 		case 'r':		/* carriage-return */
20636725Sbostic 			*store = '\r';
20736725Sbostic 			break;
20836725Sbostic 		case 't':		/* horizontal tab */
20936725Sbostic 			*store = '\t';
21036725Sbostic 			break;
21136725Sbostic 		case 'v':		/* vertical tab */
21236725Sbostic 			*store = '\13';
21336725Sbostic 			break;
21436725Sbostic 					/* octal constant */
21536725Sbostic 		case '0': case '1': case '2': case '3':
21636725Sbostic 		case '4': case '5': case '6': case '7':
21736725Sbostic 			for (c = 3, value = 0;
21836725Sbostic 			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
21936725Sbostic 				value <<= 3;
22036725Sbostic 				value += *fmt - '0';
22136725Sbostic 			}
22236725Sbostic 			--fmt;
22336725Sbostic 			*store = value;
22436725Sbostic 			break;
22536725Sbostic 		default:
22636725Sbostic 			*store = *fmt;
22736725Sbostic 			break;
22836725Sbostic 		}
22936725Sbostic 	}
23036725Sbostic 	*store = '\0';
23136725Sbostic }
23236725Sbostic 
23336725Sbostic getchr()
23436725Sbostic {
23536725Sbostic 	if (!*gargv)
23636725Sbostic 		return((int)'\0');
23736725Sbostic 	return((int)**gargv++);
23836725Sbostic }
23936725Sbostic 
24036725Sbostic char *
24136725Sbostic getstr()
24236725Sbostic {
24336725Sbostic 	if (!*gargv)
24436725Sbostic 		return("");
24536725Sbostic 	return(*gargv++);
24636725Sbostic }
24736725Sbostic 
24836725Sbostic static char *number = "+-0123456789";
24936725Sbostic getint()
25036725Sbostic {
25136725Sbostic 	if (!*gargv)
25236725Sbostic 		return(0);
25336725Sbostic 	if (index(number, **gargv))
25436725Sbostic 		return(atoi(*gargv++));
25536725Sbostic 	return(asciicode());
25636725Sbostic }
25736725Sbostic 
25836725Sbostic long
25936725Sbostic getlong()
26036725Sbostic {
26136725Sbostic 	long atol();
26236725Sbostic 
26336725Sbostic 	if (!*gargv)
26436725Sbostic 		return((long)0);
26536725Sbostic 	if (index(number, **gargv))
26636725Sbostic 		return(atol(*gargv++));
26736725Sbostic 	return((long)asciicode());
26836725Sbostic }
26936725Sbostic 
27036725Sbostic double
27136725Sbostic getdouble()
27236725Sbostic {
27336725Sbostic 	double atof();
27436725Sbostic 
27536725Sbostic 	if (!*gargv)
27636725Sbostic 		return((double)0);
27736725Sbostic 	if (index(number, **gargv))
27836725Sbostic 		return(atof(*gargv++));
27936725Sbostic 	return((double)asciicode());
28036725Sbostic }
28136725Sbostic 
28236725Sbostic asciicode()
28336725Sbostic {
28436725Sbostic 	register char ch;
28536725Sbostic 
28636725Sbostic 	ch = **gargv;
28736725Sbostic 	if (ch == '\'' || ch == '"')
28836725Sbostic 		ch = (*gargv)[1];
28936725Sbostic 	++gargv;
29036725Sbostic 	return(ch);
29136725Sbostic }
292