xref: /minix3/usr.bin/printf/printf.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: printf.c,v 1.37 2015/06/16 22:54:10 christos Exp $	*/
22c96f054SLionel Sambuc 
32c96f054SLionel Sambuc /*
42c96f054SLionel Sambuc  * Copyright (c) 1989, 1993
52c96f054SLionel Sambuc  *	The Regents of the University of California.  All rights reserved.
62c96f054SLionel Sambuc  *
72c96f054SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
82c96f054SLionel Sambuc  * modification, are permitted provided that the following conditions
92c96f054SLionel Sambuc  * are met:
102c96f054SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
112c96f054SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
122c96f054SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
132c96f054SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
142c96f054SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
152c96f054SLionel Sambuc  * 3. Neither the name of the University nor the names of its contributors
162c96f054SLionel Sambuc  *    may be used to endorse or promote products derived from this software
172c96f054SLionel Sambuc  *    without specific prior written permission.
182c96f054SLionel Sambuc  *
192c96f054SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
202c96f054SLionel Sambuc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
212c96f054SLionel Sambuc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
222c96f054SLionel Sambuc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
232c96f054SLionel Sambuc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
242c96f054SLionel Sambuc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
252c96f054SLionel Sambuc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
262c96f054SLionel Sambuc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
272c96f054SLionel Sambuc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
282c96f054SLionel Sambuc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
292c96f054SLionel Sambuc  * SUCH DAMAGE.
302c96f054SLionel Sambuc  */
312c96f054SLionel Sambuc 
322c96f054SLionel Sambuc #include <sys/cdefs.h>
332c96f054SLionel Sambuc #ifndef lint
342c96f054SLionel Sambuc #if !defined(BUILTIN) && !defined(SHELL)
352c96f054SLionel Sambuc __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
362c96f054SLionel Sambuc  The Regents of the University of California.  All rights reserved.");
372c96f054SLionel Sambuc #endif
382c96f054SLionel Sambuc #endif
392c96f054SLionel Sambuc 
402c96f054SLionel Sambuc #ifndef lint
412c96f054SLionel Sambuc #if 0
422c96f054SLionel Sambuc static char sccsid[] = "@(#)printf.c	8.2 (Berkeley) 3/22/95";
432c96f054SLionel Sambuc #else
44*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: printf.c,v 1.37 2015/06/16 22:54:10 christos Exp $");
452c96f054SLionel Sambuc #endif
462c96f054SLionel Sambuc #endif /* not lint */
472c96f054SLionel Sambuc 
482c96f054SLionel Sambuc #include <sys/types.h>
492c96f054SLionel Sambuc 
502c96f054SLionel Sambuc #include <ctype.h>
512c96f054SLionel Sambuc #include <err.h>
522c96f054SLionel Sambuc #include <errno.h>
532c96f054SLionel Sambuc #include <inttypes.h>
542c96f054SLionel Sambuc #include <limits.h>
552c96f054SLionel Sambuc #include <locale.h>
562c96f054SLionel Sambuc #include <stdarg.h>
572c96f054SLionel Sambuc #include <stdio.h>
582c96f054SLionel Sambuc #include <stdlib.h>
592c96f054SLionel Sambuc #include <string.h>
602c96f054SLionel Sambuc #include <unistd.h>
612c96f054SLionel Sambuc 
622c96f054SLionel Sambuc #ifdef __GNUC__
632c96f054SLionel Sambuc #define ESCAPE '\e'
642c96f054SLionel Sambuc #else
652c96f054SLionel Sambuc #define ESCAPE 033
662c96f054SLionel Sambuc #endif
672c96f054SLionel Sambuc 
682c96f054SLionel Sambuc static void	 conv_escape_str(char *, void (*)(int));
692c96f054SLionel Sambuc static char	*conv_escape(char *, char *);
702c96f054SLionel Sambuc static char	*conv_expand(const char *);
7184d9c625SLionel Sambuc static char	 getchr(void);
722c96f054SLionel Sambuc static double	 getdouble(void);
732c96f054SLionel Sambuc static int	 getwidth(void);
742c96f054SLionel Sambuc static intmax_t	 getintmax(void);
752c96f054SLionel Sambuc static uintmax_t getuintmax(void);
762c96f054SLionel Sambuc static char	*getstr(void);
7784d9c625SLionel Sambuc static char	*mklong(const char *, char);
782c96f054SLionel Sambuc static void      check_conversion(const char *, const char *);
792c96f054SLionel Sambuc static void	 usage(void);
802c96f054SLionel Sambuc 
812c96f054SLionel Sambuc static void	b_count(int);
822c96f054SLionel Sambuc static void	b_output(int);
832c96f054SLionel Sambuc static size_t	b_length;
842c96f054SLionel Sambuc static char	*b_fmt;
852c96f054SLionel Sambuc 
862c96f054SLionel Sambuc static int	rval;
872c96f054SLionel Sambuc static char  **gargv;
882c96f054SLionel Sambuc 
892c96f054SLionel Sambuc #ifdef BUILTIN		/* csh builtin */
902c96f054SLionel Sambuc #define main progprintf
912c96f054SLionel Sambuc #endif
922c96f054SLionel Sambuc 
932c96f054SLionel Sambuc #ifdef SHELL		/* sh (aka ash) builtin */
942c96f054SLionel Sambuc #define main printfcmd
952c96f054SLionel Sambuc #include "../../bin/sh/bltin/bltin.h"
962c96f054SLionel Sambuc #endif /* SHELL */
972c96f054SLionel Sambuc 
982c96f054SLionel Sambuc #define PF(f, func) { \
992c96f054SLionel Sambuc 	if (fieldwidth != -1) { \
1002c96f054SLionel Sambuc 		if (precision != -1) \
1012c96f054SLionel Sambuc 			error = printf(f, fieldwidth, precision, func); \
1022c96f054SLionel Sambuc 		else \
1032c96f054SLionel Sambuc 			error = printf(f, fieldwidth, func); \
1042c96f054SLionel Sambuc 	} else if (precision != -1) \
1052c96f054SLionel Sambuc 		error = printf(f, precision, func); \
1062c96f054SLionel Sambuc 	else \
1072c96f054SLionel Sambuc 		error = printf(f, func); \
1082c96f054SLionel Sambuc }
1092c96f054SLionel Sambuc 
1102c96f054SLionel Sambuc #define APF(cpp, f, func) { \
1112c96f054SLionel Sambuc 	if (fieldwidth != -1) { \
1122c96f054SLionel Sambuc 		if (precision != -1) \
1132c96f054SLionel Sambuc 			error = asprintf(cpp, f, fieldwidth, precision, func); \
1142c96f054SLionel Sambuc 		else \
1152c96f054SLionel Sambuc 			error = asprintf(cpp, f, fieldwidth, func); \
1162c96f054SLionel Sambuc 	} else if (precision != -1) \
1172c96f054SLionel Sambuc 		error = asprintf(cpp, f, precision, func); \
1182c96f054SLionel Sambuc 	else \
1192c96f054SLionel Sambuc 		error = asprintf(cpp, f, func); \
1202c96f054SLionel Sambuc }
1212c96f054SLionel Sambuc 
1222c96f054SLionel Sambuc #ifdef main
1232c96f054SLionel Sambuc int main(int, char *[]);
1242c96f054SLionel Sambuc #endif
main(int argc,char * argv[])1252c96f054SLionel Sambuc int main(int argc, char *argv[])
1262c96f054SLionel Sambuc {
1272c96f054SLionel Sambuc 	char *fmt, *start;
1282c96f054SLionel Sambuc 	int fieldwidth, precision;
1292c96f054SLionel Sambuc 	char nextch;
1302c96f054SLionel Sambuc 	char *format;
13184d9c625SLionel Sambuc 	char ch;
13284d9c625SLionel Sambuc 	int error, o;
1332c96f054SLionel Sambuc 
1342c96f054SLionel Sambuc #if !defined(SHELL) && !defined(BUILTIN)
1352c96f054SLionel Sambuc 	(void)setlocale (LC_ALL, "");
1362c96f054SLionel Sambuc #endif
1372c96f054SLionel Sambuc 
13884d9c625SLionel Sambuc 	while ((o = getopt(argc, argv, "")) != -1) {
13984d9c625SLionel Sambuc 		switch (o) {
1402c96f054SLionel Sambuc 		case '?':
1412c96f054SLionel Sambuc 		default:
1422c96f054SLionel Sambuc 			usage();
1432c96f054SLionel Sambuc 			return 1;
1442c96f054SLionel Sambuc 		}
1452c96f054SLionel Sambuc 	}
1462c96f054SLionel Sambuc 	argc -= optind;
1472c96f054SLionel Sambuc 	argv += optind;
1482c96f054SLionel Sambuc 
1492c96f054SLionel Sambuc 	if (argc < 1) {
1502c96f054SLionel Sambuc 		usage();
1512c96f054SLionel Sambuc 		return 1;
1522c96f054SLionel Sambuc 	}
1532c96f054SLionel Sambuc 
1542c96f054SLionel Sambuc 	format = *argv;
1552c96f054SLionel Sambuc 	gargv = ++argv;
1562c96f054SLionel Sambuc 
1572c96f054SLionel Sambuc #define SKIP1	"#-+ 0'"
1582c96f054SLionel Sambuc #define SKIP2	"0123456789"
1592c96f054SLionel Sambuc 	do {
1602c96f054SLionel Sambuc 		/*
1612c96f054SLionel Sambuc 		 * Basic algorithm is to scan the format string for conversion
1622c96f054SLionel Sambuc 		 * specifications -- once one is found, find out if the field
1632c96f054SLionel Sambuc 		 * width or precision is a '*'; if it is, gather up value.
1642c96f054SLionel Sambuc 		 * Note, format strings are reused as necessary to use up the
1652c96f054SLionel Sambuc 		 * provided arguments, arguments of zero/null string are
1662c96f054SLionel Sambuc 		 * provided to use up the format string.
1672c96f054SLionel Sambuc 		 */
1682c96f054SLionel Sambuc 
1692c96f054SLionel Sambuc 		/* find next format specification */
1702c96f054SLionel Sambuc 		for (fmt = format; (ch = *fmt++) != '\0';) {
1712c96f054SLionel Sambuc 			if (ch == '\\') {
1722c96f054SLionel Sambuc 				char c_ch;
1732c96f054SLionel Sambuc 				fmt = conv_escape(fmt, &c_ch);
1742c96f054SLionel Sambuc 				putchar(c_ch);
1752c96f054SLionel Sambuc 				continue;
1762c96f054SLionel Sambuc 			}
1772c96f054SLionel Sambuc 			if (ch != '%' || (*fmt == '%' && ++fmt)) {
1782c96f054SLionel Sambuc 				(void)putchar(ch);
1792c96f054SLionel Sambuc 				continue;
1802c96f054SLionel Sambuc 			}
1812c96f054SLionel Sambuc 
1822c96f054SLionel Sambuc 			/* Ok - we've found a format specification,
1832c96f054SLionel Sambuc 			   Save its address for a later printf(). */
1842c96f054SLionel Sambuc 			start = fmt - 1;
1852c96f054SLionel Sambuc 
1862c96f054SLionel Sambuc 			/* skip to field width */
1872c96f054SLionel Sambuc 			fmt += strspn(fmt, SKIP1);
1882c96f054SLionel Sambuc 			if (*fmt == '*') {
1892c96f054SLionel Sambuc 				fmt++;
1902c96f054SLionel Sambuc 				fieldwidth = getwidth();
1912c96f054SLionel Sambuc 			} else
1922c96f054SLionel Sambuc 				fieldwidth = -1;
1932c96f054SLionel Sambuc 
1942c96f054SLionel Sambuc 			/* skip to possible '.', get following precision */
1952c96f054SLionel Sambuc 			fmt += strspn(fmt, SKIP2);
1962c96f054SLionel Sambuc 			if (*fmt == '.') {
1972c96f054SLionel Sambuc 				fmt++;
1982c96f054SLionel Sambuc 				if (*fmt == '*') {
1992c96f054SLionel Sambuc 					fmt++;
2002c96f054SLionel Sambuc 					precision = getwidth();
2012c96f054SLionel Sambuc 				} else
2022c96f054SLionel Sambuc 					precision = -1;
2032c96f054SLionel Sambuc 			} else
2042c96f054SLionel Sambuc 				precision = -1;
2052c96f054SLionel Sambuc 
2062c96f054SLionel Sambuc 			fmt += strspn(fmt, SKIP2);
2072c96f054SLionel Sambuc 
2082c96f054SLionel Sambuc 			ch = *fmt;
2092c96f054SLionel Sambuc 			if (!ch) {
2102c96f054SLionel Sambuc 				warnx("missing format character");
2112c96f054SLionel Sambuc 				return (1);
2122c96f054SLionel Sambuc 			}
2132c96f054SLionel Sambuc 			/* null terminate format string to we can use it
2142c96f054SLionel Sambuc 			   as an argument to printf. */
2152c96f054SLionel Sambuc 			nextch = fmt[1];
2162c96f054SLionel Sambuc 			fmt[1] = 0;
2172c96f054SLionel Sambuc 			switch (ch) {
2182c96f054SLionel Sambuc 
2192c96f054SLionel Sambuc 			case 'B': {
2202c96f054SLionel Sambuc 				const char *p = conv_expand(getstr());
2212c96f054SLionel Sambuc 				if (p == NULL)
2222c96f054SLionel Sambuc 					goto out;
2232c96f054SLionel Sambuc 				*fmt = 's';
2242c96f054SLionel Sambuc 				PF(start, p);
2252c96f054SLionel Sambuc 				if (error < 0)
2262c96f054SLionel Sambuc 					goto out;
2272c96f054SLionel Sambuc 				break;
2282c96f054SLionel Sambuc 			}
2292c96f054SLionel Sambuc 			case 'b': {
2302c96f054SLionel Sambuc 				/* There has to be a better way to do this,
2312c96f054SLionel Sambuc 				 * but the string we generate might have
2322c96f054SLionel Sambuc 				 * embedded nulls. */
2332c96f054SLionel Sambuc 				static char *a, *t;
2342c96f054SLionel Sambuc 				char *cp = getstr();
2352c96f054SLionel Sambuc 				/* Free on entry in case shell longjumped out */
2362c96f054SLionel Sambuc 				if (a != NULL)
2372c96f054SLionel Sambuc 					free(a);
2382c96f054SLionel Sambuc 				a = NULL;
2392c96f054SLionel Sambuc 				if (t != NULL)
2402c96f054SLionel Sambuc 					free(t);
2412c96f054SLionel Sambuc 				t = NULL;
2422c96f054SLionel Sambuc 				/* Count number of bytes we want to output */
2432c96f054SLionel Sambuc 				b_length = 0;
2442c96f054SLionel Sambuc 				conv_escape_str(cp, b_count);
2452c96f054SLionel Sambuc 				t = malloc(b_length + 1);
2462c96f054SLionel Sambuc 				if (t == NULL)
2472c96f054SLionel Sambuc 					goto out;
2482c96f054SLionel Sambuc 				(void)memset(t, 'x', b_length);
2492c96f054SLionel Sambuc 				t[b_length] = 0;
2502c96f054SLionel Sambuc 				/* Get printf to calculate the lengths */
2512c96f054SLionel Sambuc 				*fmt = 's';
2522c96f054SLionel Sambuc 				APF(&a, start, t);
2532c96f054SLionel Sambuc 				if (error == -1)
2542c96f054SLionel Sambuc 					goto out;
2552c96f054SLionel Sambuc 				b_fmt = a;
2562c96f054SLionel Sambuc 				/* Output leading spaces and data bytes */
2572c96f054SLionel Sambuc 				conv_escape_str(cp, b_output);
2582c96f054SLionel Sambuc 				/* Add any trailing spaces */
2592c96f054SLionel Sambuc 				printf("%s", b_fmt);
2602c96f054SLionel Sambuc 				break;
2612c96f054SLionel Sambuc 			}
2622c96f054SLionel Sambuc 			case 'c': {
2632c96f054SLionel Sambuc 				char p = getchr();
2642c96f054SLionel Sambuc 				PF(start, p);
2652c96f054SLionel Sambuc 				if (error < 0)
2662c96f054SLionel Sambuc 					goto out;
2672c96f054SLionel Sambuc 				break;
2682c96f054SLionel Sambuc 			}
2692c96f054SLionel Sambuc 			case 's': {
2702c96f054SLionel Sambuc 				char *p = getstr();
2712c96f054SLionel Sambuc 				PF(start, p);
2722c96f054SLionel Sambuc 				if (error < 0)
2732c96f054SLionel Sambuc 					goto out;
2742c96f054SLionel Sambuc 				break;
2752c96f054SLionel Sambuc 			}
2762c96f054SLionel Sambuc 			case 'd':
2772c96f054SLionel Sambuc 			case 'i': {
2782c96f054SLionel Sambuc 				intmax_t p = getintmax();
2792c96f054SLionel Sambuc 				char *f = mklong(start, ch);
2802c96f054SLionel Sambuc 				PF(f, p);
2812c96f054SLionel Sambuc 				if (error < 0)
2822c96f054SLionel Sambuc 					goto out;
2832c96f054SLionel Sambuc 				break;
2842c96f054SLionel Sambuc 			}
2852c96f054SLionel Sambuc 			case 'o':
2862c96f054SLionel Sambuc 			case 'u':
2872c96f054SLionel Sambuc 			case 'x':
2882c96f054SLionel Sambuc 			case 'X': {
2892c96f054SLionel Sambuc 				uintmax_t p = getuintmax();
2902c96f054SLionel Sambuc 				char *f = mklong(start, ch);
2912c96f054SLionel Sambuc 				PF(f, p);
2922c96f054SLionel Sambuc 				if (error < 0)
2932c96f054SLionel Sambuc 					goto out;
2942c96f054SLionel Sambuc 				break;
2952c96f054SLionel Sambuc 			}
2962c96f054SLionel Sambuc 			case 'e':
2972c96f054SLionel Sambuc 			case 'E':
2982c96f054SLionel Sambuc 			case 'f':
2992c96f054SLionel Sambuc 			case 'g':
3002c96f054SLionel Sambuc 			case 'G': {
3012c96f054SLionel Sambuc 				double p = getdouble();
3022c96f054SLionel Sambuc 				PF(start, p);
3032c96f054SLionel Sambuc 				if (error < 0)
3042c96f054SLionel Sambuc 					goto out;
3052c96f054SLionel Sambuc 				break;
3062c96f054SLionel Sambuc 			}
3072c96f054SLionel Sambuc 			default:
3082c96f054SLionel Sambuc 				warnx("%s: invalid directive", start);
3092c96f054SLionel Sambuc 				return 1;
3102c96f054SLionel Sambuc 			}
3112c96f054SLionel Sambuc 			*fmt++ = ch;
3122c96f054SLionel Sambuc 			*fmt = nextch;
3132c96f054SLionel Sambuc 			/* escape if a \c was encountered */
3142c96f054SLionel Sambuc 			if (rval & 0x100)
3152c96f054SLionel Sambuc 				return rval & ~0x100;
3162c96f054SLionel Sambuc 		}
3172c96f054SLionel Sambuc 	} while (gargv != argv && *gargv);
3182c96f054SLionel Sambuc 
3192c96f054SLionel Sambuc 	return rval & ~0x100;
3202c96f054SLionel Sambuc out:
3212c96f054SLionel Sambuc 	warn("print failed");
3222c96f054SLionel Sambuc 	return 1;
3232c96f054SLionel Sambuc }
3242c96f054SLionel Sambuc 
3252c96f054SLionel Sambuc /* helper functions for conv_escape_str */
3262c96f054SLionel Sambuc 
3272c96f054SLionel Sambuc static void
3282c96f054SLionel Sambuc /*ARGSUSED*/
b_count(int ch)3292c96f054SLionel Sambuc b_count(int ch)
3302c96f054SLionel Sambuc {
3312c96f054SLionel Sambuc 	b_length++;
3322c96f054SLionel Sambuc }
3332c96f054SLionel Sambuc 
3342c96f054SLionel Sambuc /* Output one converted character for every 'x' in the 'format' */
3352c96f054SLionel Sambuc 
3362c96f054SLionel Sambuc static void
b_output(int ch)3372c96f054SLionel Sambuc b_output(int ch)
3382c96f054SLionel Sambuc {
3392c96f054SLionel Sambuc 	for (;;) {
3402c96f054SLionel Sambuc 		switch (*b_fmt++) {
3412c96f054SLionel Sambuc 		case 0:
3422c96f054SLionel Sambuc 			b_fmt--;
3432c96f054SLionel Sambuc 			return;
3442c96f054SLionel Sambuc 		case ' ':
3452c96f054SLionel Sambuc 			putchar(' ');
3462c96f054SLionel Sambuc 			break;
3472c96f054SLionel Sambuc 		default:
3482c96f054SLionel Sambuc 			putchar(ch);
3492c96f054SLionel Sambuc 			return;
3502c96f054SLionel Sambuc 		}
3512c96f054SLionel Sambuc 	}
3522c96f054SLionel Sambuc }
3532c96f054SLionel Sambuc 
3542c96f054SLionel Sambuc 
3552c96f054SLionel Sambuc /*
3562c96f054SLionel Sambuc  * Print SysV echo(1) style escape string
3572c96f054SLionel Sambuc  *	Halts processing string if a \c escape is encountered.
3582c96f054SLionel Sambuc  */
3592c96f054SLionel Sambuc static void
conv_escape_str(char * str,void (* do_putchar)(int))3602c96f054SLionel Sambuc conv_escape_str(char *str, void (*do_putchar)(int))
3612c96f054SLionel Sambuc {
3622c96f054SLionel Sambuc 	int value;
3632c96f054SLionel Sambuc 	int ch;
3642c96f054SLionel Sambuc 	char c;
3652c96f054SLionel Sambuc 
3662c96f054SLionel Sambuc 	while ((ch = *str++) != '\0') {
3672c96f054SLionel Sambuc 		if (ch != '\\') {
3682c96f054SLionel Sambuc 			do_putchar(ch);
3692c96f054SLionel Sambuc 			continue;
3702c96f054SLionel Sambuc 		}
3712c96f054SLionel Sambuc 
3722c96f054SLionel Sambuc 		ch = *str++;
3732c96f054SLionel Sambuc 		if (ch == 'c') {
3742c96f054SLionel Sambuc 			/* \c as in SYSV echo - abort all processing.... */
3752c96f054SLionel Sambuc 			rval |= 0x100;
3762c96f054SLionel Sambuc 			break;
3772c96f054SLionel Sambuc 		}
3782c96f054SLionel Sambuc 
3792c96f054SLionel Sambuc 		/*
3802c96f054SLionel Sambuc 		 * %b string octal constants are not like those in C.
3812c96f054SLionel Sambuc 		 * They start with a \0, and are followed by 0, 1, 2,
3822c96f054SLionel Sambuc 		 * or 3 octal digits.
3832c96f054SLionel Sambuc 		 */
3842c96f054SLionel Sambuc 		if (ch == '0') {
3852c96f054SLionel Sambuc 			int octnum = 0, i;
3862c96f054SLionel Sambuc 			for (i = 0; i < 3; i++) {
3872c96f054SLionel Sambuc 				if (!isdigit((unsigned char)*str) || *str > '7')
3882c96f054SLionel Sambuc 					break;
3892c96f054SLionel Sambuc 				octnum = (octnum << 3) | (*str++ - '0');
3902c96f054SLionel Sambuc 			}
3912c96f054SLionel Sambuc 			do_putchar(octnum);
3922c96f054SLionel Sambuc 			continue;
3932c96f054SLionel Sambuc 		}
3942c96f054SLionel Sambuc 
3952c96f054SLionel Sambuc 		/* \[M][^|-]C as defined by vis(3) */
3962c96f054SLionel Sambuc 		if (ch == 'M' && *str == '-') {
3972c96f054SLionel Sambuc 			do_putchar(0200 | str[1]);
3982c96f054SLionel Sambuc 			str += 2;
3992c96f054SLionel Sambuc 			continue;
4002c96f054SLionel Sambuc 		}
4012c96f054SLionel Sambuc 		if (ch == 'M' && *str == '^') {
4022c96f054SLionel Sambuc 			str++;
4032c96f054SLionel Sambuc 			value = 0200;
4042c96f054SLionel Sambuc 			ch = '^';
4052c96f054SLionel Sambuc 		} else
4062c96f054SLionel Sambuc 			value = 0;
4072c96f054SLionel Sambuc 		if (ch == '^') {
4082c96f054SLionel Sambuc 			ch = *str++;
4092c96f054SLionel Sambuc 			if (ch == '?')
4102c96f054SLionel Sambuc 				value |= 0177;
4112c96f054SLionel Sambuc 			else
4122c96f054SLionel Sambuc 				value |= ch & 037;
4132c96f054SLionel Sambuc 			do_putchar(value);
4142c96f054SLionel Sambuc 			continue;
4152c96f054SLionel Sambuc 		}
4162c96f054SLionel Sambuc 
4172c96f054SLionel Sambuc 		/* Finally test for sequences valid in the format string */
4182c96f054SLionel Sambuc 		str = conv_escape(str - 1, &c);
4192c96f054SLionel Sambuc 		do_putchar(c);
4202c96f054SLionel Sambuc 	}
4212c96f054SLionel Sambuc }
4222c96f054SLionel Sambuc 
4232c96f054SLionel Sambuc /*
4242c96f054SLionel Sambuc  * Print "standard" escape characters
4252c96f054SLionel Sambuc  */
4262c96f054SLionel Sambuc static char *
conv_escape(char * str,char * conv_ch)4272c96f054SLionel Sambuc conv_escape(char *str, char *conv_ch)
4282c96f054SLionel Sambuc {
42984d9c625SLionel Sambuc 	char value;
43084d9c625SLionel Sambuc 	char ch;
4312c96f054SLionel Sambuc 	char num_buf[4], *num_end;
4322c96f054SLionel Sambuc 
4332c96f054SLionel Sambuc 	ch = *str++;
4342c96f054SLionel Sambuc 
4352c96f054SLionel Sambuc 	switch (ch) {
4362c96f054SLionel Sambuc 	case '0': case '1': case '2': case '3':
4372c96f054SLionel Sambuc 	case '4': case '5': case '6': case '7':
4382c96f054SLionel Sambuc 		num_buf[0] = ch;
4392c96f054SLionel Sambuc 		ch = str[0];
4402c96f054SLionel Sambuc 		num_buf[1] = ch;
44184d9c625SLionel Sambuc 		num_buf[2] = (char)(ch != '\0' ? str[1] : '\0');
44284d9c625SLionel Sambuc 		num_buf[3] = '\0';
44384d9c625SLionel Sambuc 		value = (char)strtoul(num_buf, &num_end, 8);
4442c96f054SLionel Sambuc 		str += num_end  - (num_buf + 1);
4452c96f054SLionel Sambuc 		break;
4462c96f054SLionel Sambuc 
4472c96f054SLionel Sambuc 	case 'x':
4482c96f054SLionel Sambuc 		/* Hexadecimal character constants are not required to be
4492c96f054SLionel Sambuc 		   supported (by SuS v1) because there is no consistent
4502c96f054SLionel Sambuc 		   way to detect the end of the constant.
4512c96f054SLionel Sambuc 		   Supporting 2 byte constants is a compromise. */
4522c96f054SLionel Sambuc 		ch = str[0];
4532c96f054SLionel Sambuc 		num_buf[0] = ch;
45484d9c625SLionel Sambuc 		num_buf[1] = (char)(ch != '\0' ? str[1] : '\0');
45584d9c625SLionel Sambuc 		num_buf[2] = '\0';
45684d9c625SLionel Sambuc 		value = (char)strtoul(num_buf, &num_end, 16);
4572c96f054SLionel Sambuc 		str += num_end - num_buf;
4582c96f054SLionel Sambuc 		break;
4592c96f054SLionel Sambuc 
4602c96f054SLionel Sambuc 	case '\\':	value = '\\';	break;	/* backslash */
4612c96f054SLionel Sambuc 	case '\'':	value = '\'';	break;	/* single quote */
4622c96f054SLionel Sambuc 	case '"':	value = '"';	break;	/* double quote */
4632c96f054SLionel Sambuc 	case 'a':	value = '\a';	break;	/* alert */
4642c96f054SLionel Sambuc 	case 'b':	value = '\b';	break;	/* backspace */
4652c96f054SLionel Sambuc 	case 'e':	value = ESCAPE;	break;	/* escape */
4662c96f054SLionel Sambuc 	case 'f':	value = '\f';	break;	/* form-feed */
4672c96f054SLionel Sambuc 	case 'n':	value = '\n';	break;	/* newline */
4682c96f054SLionel Sambuc 	case 'r':	value = '\r';	break;	/* carriage-return */
4692c96f054SLionel Sambuc 	case 't':	value = '\t';	break;	/* tab */
4702c96f054SLionel Sambuc 	case 'v':	value = '\v';	break;	/* vertical-tab */
4712c96f054SLionel Sambuc 
4722c96f054SLionel Sambuc 	default:
4732c96f054SLionel Sambuc 		warnx("unknown escape sequence `\\%c'", ch);
4742c96f054SLionel Sambuc 		rval = 1;
4752c96f054SLionel Sambuc 		value = ch;
4762c96f054SLionel Sambuc 		break;
4772c96f054SLionel Sambuc 	}
4782c96f054SLionel Sambuc 
4792c96f054SLionel Sambuc 	*conv_ch = value;
4802c96f054SLionel Sambuc 	return str;
4812c96f054SLionel Sambuc }
4822c96f054SLionel Sambuc 
4832c96f054SLionel Sambuc /* expand a string so that everything is printable */
4842c96f054SLionel Sambuc 
4852c96f054SLionel Sambuc static char *
conv_expand(const char * str)4862c96f054SLionel Sambuc conv_expand(const char *str)
4872c96f054SLionel Sambuc {
4882c96f054SLionel Sambuc 	static char *conv_str;
4892c96f054SLionel Sambuc 	char *cp;
49084d9c625SLionel Sambuc 	char ch;
4912c96f054SLionel Sambuc 
4922c96f054SLionel Sambuc 	if (conv_str)
4932c96f054SLionel Sambuc 		free(conv_str);
4942c96f054SLionel Sambuc 	/* get a buffer that is definitely large enough.... */
4952c96f054SLionel Sambuc 	conv_str = malloc(4 * strlen(str) + 1);
4962c96f054SLionel Sambuc 	if (!conv_str)
4972c96f054SLionel Sambuc 		return NULL;
4982c96f054SLionel Sambuc 	cp = conv_str;
4992c96f054SLionel Sambuc 
50084d9c625SLionel Sambuc 	while ((ch = *(const char *)str++) != '\0') {
5012c96f054SLionel Sambuc 		switch (ch) {
5022c96f054SLionel Sambuc 		/* Use C escapes for expected control characters */
5032c96f054SLionel Sambuc 		case '\\':	ch = '\\';	break;	/* backslash */
5042c96f054SLionel Sambuc 		case '\'':	ch = '\'';	break;	/* single quote */
5052c96f054SLionel Sambuc 		case '"':	ch = '"';	break;	/* double quote */
5062c96f054SLionel Sambuc 		case '\a':	ch = 'a';	break;	/* alert */
5072c96f054SLionel Sambuc 		case '\b':	ch = 'b';	break;	/* backspace */
5082c96f054SLionel Sambuc 		case ESCAPE:	ch = 'e';	break;	/* escape */
5092c96f054SLionel Sambuc 		case '\f':	ch = 'f';	break;	/* form-feed */
5102c96f054SLionel Sambuc 		case '\n':	ch = 'n';	break;	/* newline */
5112c96f054SLionel Sambuc 		case '\r':	ch = 'r';	break;	/* carriage-return */
5122c96f054SLionel Sambuc 		case '\t':	ch = 't';	break;	/* tab */
5132c96f054SLionel Sambuc 		case '\v':	ch = 'v';	break;	/* vertical-tab */
5142c96f054SLionel Sambuc 		default:
5152c96f054SLionel Sambuc 			/* Copy anything printable */
51684d9c625SLionel Sambuc 			if (isprint((unsigned char)ch)) {
5172c96f054SLionel Sambuc 				*cp++ = ch;
5182c96f054SLionel Sambuc 				continue;
5192c96f054SLionel Sambuc 			}
5202c96f054SLionel Sambuc 			/* Use vis(3) encodings for the rest */
5212c96f054SLionel Sambuc 			*cp++ = '\\';
5222c96f054SLionel Sambuc 			if (ch & 0200) {
5232c96f054SLionel Sambuc 				*cp++ = 'M';
52484d9c625SLionel Sambuc 				ch &= (char)~0200;
5252c96f054SLionel Sambuc 			}
5262c96f054SLionel Sambuc 			if (ch == 0177) {
5272c96f054SLionel Sambuc 				*cp++ = '^';
5282c96f054SLionel Sambuc 				*cp++ = '?';
5292c96f054SLionel Sambuc 				continue;
5302c96f054SLionel Sambuc 			}
5312c96f054SLionel Sambuc 			if (ch < 040) {
5322c96f054SLionel Sambuc 				*cp++ = '^';
5332c96f054SLionel Sambuc 				*cp++ = ch | 0100;
5342c96f054SLionel Sambuc 				continue;
5352c96f054SLionel Sambuc 			}
5362c96f054SLionel Sambuc 			*cp++ = '-';
5372c96f054SLionel Sambuc 			*cp++ = ch;
5382c96f054SLionel Sambuc 			continue;
5392c96f054SLionel Sambuc 		}
5402c96f054SLionel Sambuc 		*cp++ = '\\';
5412c96f054SLionel Sambuc 		*cp++ = ch;
5422c96f054SLionel Sambuc 	}
5432c96f054SLionel Sambuc 
5442c96f054SLionel Sambuc 	*cp = 0;
5452c96f054SLionel Sambuc 	return conv_str;
5462c96f054SLionel Sambuc }
5472c96f054SLionel Sambuc 
5482c96f054SLionel Sambuc static char *
mklong(const char * str,char ch)54984d9c625SLionel Sambuc mklong(const char *str, char ch)
5502c96f054SLionel Sambuc {
5512c96f054SLionel Sambuc 	static char copy[64];
5522c96f054SLionel Sambuc 	size_t len;
5532c96f054SLionel Sambuc 
5542c96f054SLionel Sambuc 	len = strlen(str) + 2;
5552c96f054SLionel Sambuc 	if (len > sizeof copy) {
556*0a6a1f1dSLionel Sambuc 		warnx("format %s too complex", str);
5572c96f054SLionel Sambuc 		len = 4;
5582c96f054SLionel Sambuc 	}
5592c96f054SLionel Sambuc 	(void)memmove(copy, str, len - 3);
5602c96f054SLionel Sambuc 	copy[len - 3] = 'j';
5612c96f054SLionel Sambuc 	copy[len - 2] = ch;
5622c96f054SLionel Sambuc 	copy[len - 1] = '\0';
5632c96f054SLionel Sambuc 	return copy;
5642c96f054SLionel Sambuc }
5652c96f054SLionel Sambuc 
56684d9c625SLionel Sambuc static char
getchr(void)5672c96f054SLionel Sambuc getchr(void)
5682c96f054SLionel Sambuc {
5692c96f054SLionel Sambuc 	if (!*gargv)
5702c96f054SLionel Sambuc 		return 0;
57184d9c625SLionel Sambuc 	return **gargv++;
5722c96f054SLionel Sambuc }
5732c96f054SLionel Sambuc 
5742c96f054SLionel Sambuc static char *
getstr(void)5752c96f054SLionel Sambuc getstr(void)
5762c96f054SLionel Sambuc {
5772c96f054SLionel Sambuc 	static char empty[] = "";
5782c96f054SLionel Sambuc 	if (!*gargv)
5792c96f054SLionel Sambuc 		return empty;
5802c96f054SLionel Sambuc 	return *gargv++;
5812c96f054SLionel Sambuc }
5822c96f054SLionel Sambuc 
5832c96f054SLionel Sambuc static int
getwidth(void)5842c96f054SLionel Sambuc getwidth(void)
5852c96f054SLionel Sambuc {
58684d9c625SLionel Sambuc 	unsigned long val;
5872c96f054SLionel Sambuc 	char *s, *ep;
5882c96f054SLionel Sambuc 
5892c96f054SLionel Sambuc 	s = *gargv;
5902c96f054SLionel Sambuc 	if (!*gargv)
5912c96f054SLionel Sambuc 		return (0);
5922c96f054SLionel Sambuc 	gargv++;
5932c96f054SLionel Sambuc 
5942c96f054SLionel Sambuc 	errno = 0;
5952c96f054SLionel Sambuc 	val = strtoul(s, &ep, 0);
5962c96f054SLionel Sambuc 	check_conversion(s, ep);
5972c96f054SLionel Sambuc 
5982c96f054SLionel Sambuc 	/* Arbitrarily 'restrict' field widths to 1Mbyte */
59984d9c625SLionel Sambuc 	if (val > 1 << 20) {
6002c96f054SLionel Sambuc 		warnx("%s: invalid field width", s);
6012c96f054SLionel Sambuc 		return 0;
6022c96f054SLionel Sambuc 	}
6032c96f054SLionel Sambuc 
60484d9c625SLionel Sambuc 	return (int)val;
6052c96f054SLionel Sambuc }
6062c96f054SLionel Sambuc 
6072c96f054SLionel Sambuc static intmax_t
getintmax(void)6082c96f054SLionel Sambuc getintmax(void)
6092c96f054SLionel Sambuc {
6102c96f054SLionel Sambuc 	intmax_t val;
6112c96f054SLionel Sambuc 	char *cp, *ep;
6122c96f054SLionel Sambuc 
6132c96f054SLionel Sambuc 	cp = *gargv;
6142c96f054SLionel Sambuc 	if (cp == NULL)
6152c96f054SLionel Sambuc 		return 0;
6162c96f054SLionel Sambuc 	gargv++;
6172c96f054SLionel Sambuc 
6182c96f054SLionel Sambuc 	if (*cp == '\"' || *cp == '\'')
6192c96f054SLionel Sambuc 		return *(cp + 1);
6202c96f054SLionel Sambuc 
6212c96f054SLionel Sambuc 	errno = 0;
6222c96f054SLionel Sambuc 	val = strtoimax(cp, &ep, 0);
6232c96f054SLionel Sambuc 	check_conversion(cp, ep);
6242c96f054SLionel Sambuc 	return val;
6252c96f054SLionel Sambuc }
6262c96f054SLionel Sambuc 
6272c96f054SLionel Sambuc static uintmax_t
getuintmax(void)6282c96f054SLionel Sambuc getuintmax(void)
6292c96f054SLionel Sambuc {
6302c96f054SLionel Sambuc 	uintmax_t val;
6312c96f054SLionel Sambuc 	char *cp, *ep;
6322c96f054SLionel Sambuc 
6332c96f054SLionel Sambuc 	cp = *gargv;
6342c96f054SLionel Sambuc 	if (cp == NULL)
6352c96f054SLionel Sambuc 		return 0;
6362c96f054SLionel Sambuc 	gargv++;
6372c96f054SLionel Sambuc 
6382c96f054SLionel Sambuc 	if (*cp == '\"' || *cp == '\'')
63984d9c625SLionel Sambuc 		return (uintmax_t)*(cp + 1);
6402c96f054SLionel Sambuc 
6412c96f054SLionel Sambuc 	/* strtoumax won't error -ve values */
6422c96f054SLionel Sambuc 	while (isspace(*(unsigned char *)cp))
6432c96f054SLionel Sambuc 		cp++;
6442c96f054SLionel Sambuc 	if (*cp == '-') {
6452c96f054SLionel Sambuc 		warnx("%s: expected positive numeric value", cp);
6462c96f054SLionel Sambuc 		rval = 1;
6472c96f054SLionel Sambuc 		return 0;
6482c96f054SLionel Sambuc 	}
6492c96f054SLionel Sambuc 
6502c96f054SLionel Sambuc 	errno = 0;
6512c96f054SLionel Sambuc 	val = strtoumax(cp, &ep, 0);
6522c96f054SLionel Sambuc 	check_conversion(cp, ep);
6532c96f054SLionel Sambuc 	return val;
6542c96f054SLionel Sambuc }
6552c96f054SLionel Sambuc 
6562c96f054SLionel Sambuc static double
getdouble(void)6572c96f054SLionel Sambuc getdouble(void)
6582c96f054SLionel Sambuc {
6592c96f054SLionel Sambuc 	double val;
6602c96f054SLionel Sambuc 	char *ep;
6612c96f054SLionel Sambuc 
6622c96f054SLionel Sambuc 	if (!*gargv)
6632c96f054SLionel Sambuc 		return (0.0);
6642c96f054SLionel Sambuc 
6652c96f054SLionel Sambuc 	if (**gargv == '\"' || **gargv == '\'')
6662c96f054SLionel Sambuc 		return (double) *((*gargv++)+1);
6672c96f054SLionel Sambuc 
6682c96f054SLionel Sambuc 	errno = 0;
6692c96f054SLionel Sambuc 	val = strtod(*gargv, &ep);
6702c96f054SLionel Sambuc 	check_conversion(*gargv++, ep);
6712c96f054SLionel Sambuc 	return val;
6722c96f054SLionel Sambuc }
6732c96f054SLionel Sambuc 
6742c96f054SLionel Sambuc static void
check_conversion(const char * s,const char * ep)6752c96f054SLionel Sambuc check_conversion(const char *s, const char *ep)
6762c96f054SLionel Sambuc {
6772c96f054SLionel Sambuc 	if (*ep) {
6782c96f054SLionel Sambuc 		if (ep == s)
6792c96f054SLionel Sambuc 			warnx("%s: expected numeric value", s);
6802c96f054SLionel Sambuc 		else
6812c96f054SLionel Sambuc 			warnx("%s: not completely converted", s);
6822c96f054SLionel Sambuc 		rval = 1;
6832c96f054SLionel Sambuc 	} else if (errno == ERANGE) {
6842c96f054SLionel Sambuc 		warnx("%s: %s", s, strerror(ERANGE));
6852c96f054SLionel Sambuc 		rval = 1;
6862c96f054SLionel Sambuc 	}
6872c96f054SLionel Sambuc }
6882c96f054SLionel Sambuc 
6892c96f054SLionel Sambuc static void
usage(void)6902c96f054SLionel Sambuc usage(void)
6912c96f054SLionel Sambuc {
6922c96f054SLionel Sambuc 	(void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname());
6932c96f054SLionel Sambuc }
694