xref: /csrg-svn/usr.bin/printf/printf.c (revision 55444)
136725Sbostic /*
236725Sbostic  * Copyright (c) 1989 The Regents of the University of California.
336725Sbostic  * All rights reserved.
436725Sbostic  *
542758Sbostic  * %sccs.include.redist.c%
636725Sbostic  */
736725Sbostic 
853564Smarc #if ! defined(BUILTIN) && ! defined(SHELL)
936725Sbostic #ifndef lint
1036725Sbostic char copyright[] =
1136725Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
1236725Sbostic  All rights reserved.\n";
1336725Sbostic #endif /* not lint */
1450477Sbostic #endif
1536725Sbostic 
1636725Sbostic #ifndef lint
17*55444Smarc static char sccsid[] = "@(#)printf.c	5.12 (Berkeley) 07/20/92";
1836725Sbostic #endif /* not lint */
1936725Sbostic 
2053564Smarc 
2136725Sbostic #include <sys/types.h>
2250477Sbostic #include <errno.h>
2353564Smarc #ifndef SHELL
2436725Sbostic #include <stdio.h>
2553564Smarc #endif
2650477Sbostic #include <stdlib.h>
2750477Sbostic #include <string.h>
2836725Sbostic 
29*55444Smarc /*
30*55444Smarc  * XXX
31*55444Smarc  * This *has* to go away.  TK.
32*55444Smarc  */
3353564Smarc #ifdef SHELL
3453564Smarc #define main printfcmd
3553564Smarc #define err error
36*55444Smarc #include "../../bin/sh/bltin/bltin.h"
3753564Smarc #endif
3853564Smarc 
3936725Sbostic #define PF(f, func) { \
4036725Sbostic 	if (fieldwidth) \
4136725Sbostic 		if (precision) \
4236811Sbostic 			(void)printf(f, fieldwidth, precision, func); \
4336725Sbostic 		else \
4436811Sbostic 			(void)printf(f, fieldwidth, func); \
4536725Sbostic 	else if (precision) \
4636811Sbostic 		(void)printf(f, precision, func); \
4736725Sbostic 	else \
4836811Sbostic 		(void)printf(f, func); \
4936725Sbostic }
5036725Sbostic 
5150477Sbostic static int	 asciicode __P((void));
5253564Smarc #ifndef SHELL
5350477Sbostic static void	 err __P((const char *fmt, ...));
5453564Smarc #endif
5550477Sbostic static void	 escape __P((char *));
5650477Sbostic static int	 getchr __P((void));
5750477Sbostic static double	 getdouble __P((void));
5850477Sbostic static int	 getint __P((void));
5950477Sbostic static long	 getlong __P((void));
6050477Sbostic static char	*getstr __P((void));
6150477Sbostic static char	*mklong __P((char *, int));
6236725Sbostic 
6350477Sbostic static char **gargv;
6450477Sbostic 
6550477Sbostic int
6650477Sbostic #ifdef BUILTIN
6750477Sbostic progprintf(argc, argv)
6850477Sbostic #else
6936725Sbostic main(argc, argv)
7050477Sbostic #endif
7136725Sbostic 	int argc;
7236725Sbostic 	char **argv;
7336725Sbostic {
7436725Sbostic 	static char *skip1, *skip2;
7536725Sbostic 	register char *format, *fmt, *start;
7636811Sbostic 	register int end, fieldwidth, precision;
7750477Sbostic 	char convch, nextch;
7836725Sbostic 
7936725Sbostic 	if (argc < 2) {
8050477Sbostic 		(void)fprintf(stderr, "usage: printf format [arg ...]\n");
8150477Sbostic 		return (1);
8236725Sbostic 	}
8336725Sbostic 
8436725Sbostic 	/*
8536725Sbostic 	 * Basic algorithm is to scan the format string for conversion
8636725Sbostic 	 * specifications -- once one is found, find out if the field
8736725Sbostic 	 * width or precision is a '*'; if it is, gather up value.  Note,
8836725Sbostic 	 * format strings are reused as necessary to use up the provided
8936725Sbostic 	 * arguments, arguments of zero/null string are provided to use
9036725Sbostic 	 * up the format string.
9136725Sbostic 	 */
9236725Sbostic 	skip1 = "#-+ 0";
9336725Sbostic 	skip2 = "*0123456789";
9436725Sbostic 
9536725Sbostic 	escape(fmt = format = *++argv);		/* backslash interpretation */
9636725Sbostic 	gargv = ++argv;
9736811Sbostic 	for (;;) {
9836725Sbostic 		end = 0;
9936725Sbostic 		/* find next format specification */
10036725Sbostic next:		for (start = fmt;; ++fmt) {
10136725Sbostic 			if (!*fmt) {
10236725Sbostic 				/* avoid infinite loop */
10336725Sbostic 				if (end == 1) {
10450477Sbostic 					err("missing format character");
10550477Sbostic 					return (1);
10636725Sbostic 				}
10736725Sbostic 				end = 1;
10836725Sbostic 				if (fmt > start)
10936811Sbostic 					(void)printf("%s", start);
11036725Sbostic 				if (!*gargv)
11150477Sbostic 					return (0);
11236725Sbostic 				fmt = format;
11336725Sbostic 				goto next;
11436725Sbostic 			}
11536725Sbostic 			/* %% prints a % */
11638480Sbostic 			if (*fmt == '%') {
11738480Sbostic 				if (*++fmt != '%')
11838480Sbostic 					break;
11938480Sbostic 				*fmt++ = '\0';
12038480Sbostic 				(void)printf("%s", start);
12138480Sbostic 				goto next;
12238480Sbostic 			}
12336725Sbostic 		}
12436725Sbostic 
12536725Sbostic 		/* skip to field width */
12636725Sbostic 		for (; index(skip1, *fmt); ++fmt);
12736879Sbostic 		fieldwidth = *fmt == '*' ? getint() : 0;
12836879Sbostic 
12936879Sbostic 		/* skip to possible '.', get following precision */
13036725Sbostic 		for (; index(skip2, *fmt); ++fmt);
13136725Sbostic 		if (*fmt == '.')
13236725Sbostic 			++fmt;
13336879Sbostic 		precision = *fmt == '*' ? getint() : 0;
13436879Sbostic 
13536725Sbostic 		/* skip to conversion char */
13636725Sbostic 		for (; index(skip2, *fmt); ++fmt);
13736725Sbostic 		if (!*fmt) {
13850477Sbostic 			err("missing format character");
13950477Sbostic 			return (1);
14036725Sbostic 		}
14136725Sbostic 
14236725Sbostic 		convch = *fmt;
14336725Sbostic 		nextch = *++fmt;
14436725Sbostic 		*fmt = '\0';
14536725Sbostic 		switch(convch) {
14636725Sbostic 		case 'c': {
14750477Sbostic 			char p;
14850477Sbostic 
14950477Sbostic 			p = getchr();
15036725Sbostic 			PF(start, p);
15136725Sbostic 			break;
15236725Sbostic 		}
15336725Sbostic 		case 's': {
15450477Sbostic 			char *p;
15550477Sbostic 
15650477Sbostic 			p = getstr();
15736725Sbostic 			PF(start, p);
15836725Sbostic 			break;
15936725Sbostic 		}
16036729Sbostic 		case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
16150477Sbostic 			long p;
16250477Sbostic 			char *f;
16350477Sbostic 
16450477Sbostic 			if ((f = mklong(start, convch)) == NULL)
16550477Sbostic 				return (1);
16650477Sbostic 			p = getlong();
16736725Sbostic 			PF(f, p);
16836725Sbostic 			break;
16936725Sbostic 		}
17036725Sbostic 		case 'e': case 'E': case 'f': case 'g': case 'G': {
17150477Sbostic 			double p;
17250477Sbostic 
17350477Sbostic 			p = getdouble();
17436725Sbostic 			PF(start, p);
17536725Sbostic 			break;
17636725Sbostic 		}
17736725Sbostic 		default:
17850477Sbostic 			err("illegal format character.\n");
17950477Sbostic 			return (1);
18036725Sbostic 		}
18136725Sbostic 		*fmt = nextch;
18236725Sbostic 	}
18336725Sbostic 	/* NOTREACHED */
18436725Sbostic }
18536725Sbostic 
18650477Sbostic static char *
18736725Sbostic mklong(str, ch)
18850477Sbostic 	char *str;
18950477Sbostic 	int ch;
19036725Sbostic {
19153564Smarc 	static char copy[64];
19236725Sbostic 	int len;
19336725Sbostic 
19436725Sbostic 	len = strlen(str) + 2;
19553564Smarc 	bcopy(str, copy, len - 3);
19653564Smarc 	copy[len - 3] = 'l';
19753564Smarc 	copy[len - 2] = ch;
19853564Smarc 	copy[len - 1] = '\0';
19936725Sbostic 	return(copy);
20036725Sbostic }
20136725Sbostic 
20250477Sbostic static void
20336725Sbostic escape(fmt)
20436725Sbostic 	register char *fmt;
20536725Sbostic {
20636725Sbostic 	register char *store;
20736725Sbostic 	register int value, c;
20836725Sbostic 
20936725Sbostic 	for (store = fmt; c = *fmt; ++fmt, ++store) {
21036725Sbostic 		if (c != '\\') {
21136725Sbostic 			*store = c;
21236725Sbostic 			continue;
21336725Sbostic 		}
21436725Sbostic 		switch (*++fmt) {
21536725Sbostic 		case '\0':		/* EOS, user error */
21636725Sbostic 			*store = '\\';
21736725Sbostic 			*++store = '\0';
21836725Sbostic 			return;
21936725Sbostic 		case '\\':		/* backslash */
22036728Sbostic 		case '\'':		/* single quote */
22136728Sbostic 			*store = *fmt;
22236725Sbostic 			break;
22336725Sbostic 		case 'a':		/* bell/alert */
22436725Sbostic 			*store = '\7';
22536725Sbostic 			break;
22636725Sbostic 		case 'b':		/* backspace */
22736725Sbostic 			*store = '\b';
22836725Sbostic 			break;
22936725Sbostic 		case 'f':		/* form-feed */
23036725Sbostic 			*store = '\f';
23136725Sbostic 			break;
23236725Sbostic 		case 'n':		/* newline */
23336725Sbostic 			*store = '\n';
23436725Sbostic 			break;
23536725Sbostic 		case 'r':		/* carriage-return */
23636725Sbostic 			*store = '\r';
23736725Sbostic 			break;
23836725Sbostic 		case 't':		/* horizontal tab */
23936725Sbostic 			*store = '\t';
24036725Sbostic 			break;
24136725Sbostic 		case 'v':		/* vertical tab */
24236725Sbostic 			*store = '\13';
24336725Sbostic 			break;
24436725Sbostic 					/* octal constant */
24536725Sbostic 		case '0': case '1': case '2': case '3':
24636725Sbostic 		case '4': case '5': case '6': case '7':
24736725Sbostic 			for (c = 3, value = 0;
24836725Sbostic 			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
24936725Sbostic 				value <<= 3;
25036725Sbostic 				value += *fmt - '0';
25136725Sbostic 			}
25236725Sbostic 			--fmt;
25336725Sbostic 			*store = value;
25436725Sbostic 			break;
25536725Sbostic 		default:
25636725Sbostic 			*store = *fmt;
25736725Sbostic 			break;
25836725Sbostic 		}
25936725Sbostic 	}
26036725Sbostic 	*store = '\0';
26136725Sbostic }
26236725Sbostic 
26350477Sbostic static int
26436725Sbostic getchr()
26536725Sbostic {
26636725Sbostic 	if (!*gargv)
26750477Sbostic 		return('\0');
26836725Sbostic 	return((int)**gargv++);
26936725Sbostic }
27036725Sbostic 
27150477Sbostic static char *
27236725Sbostic getstr()
27336725Sbostic {
27436725Sbostic 	if (!*gargv)
27536725Sbostic 		return("");
27636725Sbostic 	return(*gargv++);
27736725Sbostic }
27836725Sbostic 
27953564Smarc static char *Number = "+-.0123456789";
28050477Sbostic static int
28136725Sbostic getint()
28236725Sbostic {
28336725Sbostic 	if (!*gargv)
28436725Sbostic 		return(0);
28553564Smarc 	if (index(Number, **gargv))
28636725Sbostic 		return(atoi(*gargv++));
28736725Sbostic 	return(asciicode());
28836725Sbostic }
28936725Sbostic 
29050477Sbostic static long
29136725Sbostic getlong()
29236725Sbostic {
29336725Sbostic 	if (!*gargv)
29436725Sbostic 		return((long)0);
29553564Smarc 	if (index(Number, **gargv))
29640959Sbostic 		return(strtol(*gargv++, (char **)NULL, 0));
29736725Sbostic 	return((long)asciicode());
29836725Sbostic }
29936725Sbostic 
30050477Sbostic static double
30136725Sbostic getdouble()
30236725Sbostic {
30336725Sbostic 	if (!*gargv)
30436725Sbostic 		return((double)0);
30553564Smarc 	if (index(Number, **gargv))
30636725Sbostic 		return(atof(*gargv++));
30736725Sbostic 	return((double)asciicode());
30836725Sbostic }
30936725Sbostic 
31050477Sbostic static int
31136725Sbostic asciicode()
31236725Sbostic {
31350477Sbostic 	register int ch;
31436725Sbostic 
31536725Sbostic 	ch = **gargv;
31636725Sbostic 	if (ch == '\'' || ch == '"')
31736725Sbostic 		ch = (*gargv)[1];
31836725Sbostic 	++gargv;
31936725Sbostic 	return(ch);
32036725Sbostic }
32150477Sbostic 
32253564Smarc #ifndef SHELL
32350477Sbostic #if __STDC__
32450477Sbostic #include <stdarg.h>
32550477Sbostic #else
32650477Sbostic #include <varargs.h>
32750477Sbostic #endif
32850477Sbostic 
32950477Sbostic static void
33050477Sbostic #if __STDC__
33150477Sbostic err(const char *fmt, ...)
33250477Sbostic #else
33350477Sbostic err(fmt, va_alist)
33450477Sbostic 	char *fmt;
33550477Sbostic 	va_dcl
33650477Sbostic #endif
33750477Sbostic {
33850477Sbostic 	va_list ap;
33950477Sbostic #if __STDC__
34050477Sbostic 	va_start(ap, fmt);
34150477Sbostic #else
34250477Sbostic 	va_start(ap);
34350477Sbostic #endif
34450477Sbostic 	(void)fprintf(stderr, "printf: ");
34550477Sbostic 	(void)vfprintf(stderr, fmt, ap);
34650477Sbostic 	va_end(ap);
34750477Sbostic 	(void)fprintf(stderr, "\n");
34850477Sbostic }
34953564Smarc #endif /* !SHELL */
350