Home | History | Annotate | Line # | Download | only in printf
printf.c revision 1.52.2.1
      1  1.52.2.1      cjep /*	$NetBSD: printf.c,v 1.52.2.1 2021/05/31 22:15:25 cjep Exp $	*/
      2      1.14       tls 
      3       1.1       cgd /*
      4      1.17       mrg  * Copyright (c) 1989, 1993
      5      1.17       mrg  *	The Regents of the University of California.  All rights reserved.
      6       1.1       cgd  *
      7       1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8       1.1       cgd  * modification, are permitted provided that the following conditions
      9       1.1       cgd  * are met:
     10       1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11       1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12       1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13       1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14       1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15      1.29       agc  * 3. Neither the name of the University nor the names of its contributors
     16       1.1       cgd  *    may be used to endorse or promote products derived from this software
     17       1.1       cgd  *    without specific prior written permission.
     18       1.1       cgd  *
     19       1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20       1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21       1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22       1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23       1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24       1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25       1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26       1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27       1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28       1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29       1.1       cgd  * SUCH DAMAGE.
     30       1.1       cgd  */
     31       1.1       cgd 
     32      1.16  christos #include <sys/cdefs.h>
     33       1.1       cgd #ifndef lint
     34      1.17       mrg #if !defined(BUILTIN) && !defined(SHELL)
     35      1.33     lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
     36      1.33     lukem  The Regents of the University of California.  All rights reserved.");
     37      1.17       mrg #endif
     38       1.5       jtc #endif
     39       1.1       cgd 
     40       1.1       cgd #ifndef lint
     41      1.16  christos #if 0
     42      1.17       mrg static char sccsid[] = "@(#)printf.c	8.2 (Berkeley) 3/22/95";
     43      1.16  christos #else
     44  1.52.2.1      cjep __RCSID("$NetBSD: printf.c,v 1.52.2.1 2021/05/31 22:15:25 cjep Exp $");
     45      1.16  christos #endif
     46       1.1       cgd #endif /* not lint */
     47       1.1       cgd 
     48      1.17       mrg #include <sys/types.h>
     49      1.17       mrg 
     50       1.4       jtc #include <ctype.h>
     51      1.19     perry #include <err.h>
     52      1.19     perry #include <errno.h>
     53      1.22    kleink #include <inttypes.h>
     54      1.19     perry #include <limits.h>
     55      1.19     perry #include <locale.h>
     56      1.23       wiz #include <stdarg.h>
     57       1.1       cgd #include <stdio.h>
     58       1.4       jtc #include <stdlib.h>
     59       1.2   mycroft #include <string.h>
     60      1.19     perry #include <unistd.h>
     61       1.4       jtc 
     62      1.25  christos #ifdef __GNUC__
     63      1.25  christos #define ESCAPE '\e'
     64      1.25  christos #else
     65      1.25  christos #define ESCAPE 033
     66      1.25  christos #endif
     67       1.5       jtc 
     68      1.39       kre static void	 conv_escape_str(char *, void (*)(int), int);
     69      1.39       kre static char	*conv_escape(char *, char *, int);
     70      1.25  christos static char	*conv_expand(const char *);
     71      1.36  christos static char	 getchr(void);
     72      1.23       wiz static double	 getdouble(void);
     73      1.25  christos static int	 getwidth(void);
     74      1.23       wiz static intmax_t	 getintmax(void);
     75      1.23       wiz static char	*getstr(void);
     76      1.36  christos static char	*mklong(const char *, char);
     77  1.52.2.1      cjep static intmax_t	 wide_char(const char *);
     78      1.23       wiz static void      check_conversion(const char *, const char *);
     79      1.41       kre static void	 usage(void);
     80      1.25  christos 
     81      1.26       dsl static void	b_count(int);
     82      1.26       dsl static void	b_output(int);
     83      1.30  christos static size_t	b_length;
     84      1.26       dsl static char	*b_fmt;
     85      1.26       dsl 
     86       1.5       jtc static int	rval;
     87       1.5       jtc static char  **gargv;
     88       1.4       jtc 
     89      1.25  christos #ifdef BUILTIN		/* csh builtin */
     90      1.25  christos #define main progprintf
     91      1.16  christos #endif
     92      1.16  christos 
     93      1.25  christos #ifdef SHELL		/* sh (aka ash) builtin */
     94       1.5       jtc #define main printfcmd
     95       1.5       jtc #include "../../bin/sh/bltin/bltin.h"
     96       1.5       jtc #endif /* SHELL */
     97       1.5       jtc 
     98       1.1       cgd #define PF(f, func) { \
     99      1.25  christos 	if (fieldwidth != -1) { \
    100      1.25  christos 		if (precision != -1) \
    101      1.32  christos 			error = printf(f, fieldwidth, precision, func); \
    102       1.1       cgd 		else \
    103      1.32  christos 			error = printf(f, fieldwidth, func); \
    104      1.25  christos 	} else if (precision != -1) \
    105      1.32  christos 		error = printf(f, precision, func); \
    106       1.1       cgd 	else \
    107      1.32  christos 		error = printf(f, func); \
    108       1.1       cgd }
    109       1.1       cgd 
    110      1.26       dsl #define APF(cpp, f, func) { \
    111      1.26       dsl 	if (fieldwidth != -1) { \
    112      1.26       dsl 		if (precision != -1) \
    113      1.32  christos 			error = asprintf(cpp, f, fieldwidth, precision, func); \
    114      1.26       dsl 		else \
    115      1.32  christos 			error = asprintf(cpp, f, fieldwidth, func); \
    116      1.26       dsl 	} else if (precision != -1) \
    117      1.32  christos 		error = asprintf(cpp, f, precision, func); \
    118      1.26       dsl 	else \
    119      1.32  christos 		error = asprintf(cpp, f, func); \
    120      1.26       dsl }
    121      1.26       dsl 
    122      1.51  christos #define isodigit(c)	((c) >= '0' && (c) <= '7')
    123      1.52  christos #define octtobin(c)	((c) - '0')
    124      1.52  christos #define check(c, a)	(c) >= (a) && (c) <= (a) + 5 ? (c) - (a) + 10
    125      1.52  christos #define hextobin(c)	(check(c, 'a') : check(c, 'A') : (c) - '0')
    126      1.32  christos #ifdef main
    127      1.32  christos int main(int, char *[]);
    128      1.32  christos #endif
    129      1.44       kre 
    130      1.44       kre int
    131      1.44       kre main(int argc, char *argv[])
    132       1.1       cgd {
    133      1.18     lukem 	char *fmt, *start;
    134      1.18     lukem 	int fieldwidth, precision;
    135      1.25  christos 	char nextch;
    136       1.4       jtc 	char *format;
    137      1.36  christos 	char ch;
    138      1.49       kre 	int error;
    139       1.4       jtc 
    140       1.5       jtc #if !defined(SHELL) && !defined(BUILTIN)
    141      1.15       cgd 	(void)setlocale (LC_ALL, "");
    142       1.5       jtc #endif
    143       1.1       cgd 
    144      1.46       kre 	rval = 0;	/* clear for builtin versions (avoid holdover) */
    145  1.52.2.1      cjep 	clearerr(stdout);	/* for the builtin version */
    146      1.46       kre 
    147      1.49       kre 	/*
    148      1.49       kre 	 * printf does not comply with Posix XBD 12.2 - there are no opts,
    149      1.49       kre 	 * not even the -- end of options marker.   Do not run getoot().
    150      1.49       kre 	 */
    151      1.50       kre 	if (argc > 2 && strchr(argv[1], '%') == NULL) {
    152      1.50       kre 		int o;
    153      1.50       kre 
    154      1.50       kre 		/*
    155      1.50       kre 		 * except that if there are multiple args and
    156      1.50       kre 		 * the first (the nominal format) contains no '%'
    157      1.50       kre 		 * conversions (which we will approximate as no '%'
    158      1.50       kre 		 * characters at all, conversions or not) then the
    159      1.50       kre 		 * results are unspecified, and we can do what we
    160      1.50       kre 		 * like.   So in that case, for some backward compat
    161      1.50       kre 		 * to scripts which (stupidly) do:
    162      1.50       kre 		 *	printf -- format args
    163      1.50       kre 		 * process this case the old way.
    164      1.50       kre 		 */
    165      1.50       kre 
    166      1.50       kre 		while ((o = getopt(argc, argv, "")) != -1) {
    167      1.50       kre 			switch (o) {
    168      1.50       kre 			case '?':
    169      1.50       kre 			default:
    170      1.50       kre 				usage();
    171      1.50       kre 				return 1;
    172      1.50       kre 			}
    173       1.5       jtc 		}
    174      1.50       kre 		argc -= optind;
    175      1.50       kre 		argv += optind;
    176      1.50       kre 	} else {
    177      1.50       kre 		argc -= 1;	/* drop argv[0] (the program name) */
    178      1.50       kre 		argv += 1;
    179       1.1       cgd 	}
    180       1.1       cgd 
    181       1.5       jtc 	if (argc < 1) {
    182       1.5       jtc 		usage();
    183      1.30  christos 		return 1;
    184       1.5       jtc 	}
    185       1.5       jtc 
    186       1.5       jtc 	format = *argv;
    187       1.5       jtc 	gargv = ++argv;
    188       1.4       jtc 
    189      1.35  christos #define SKIP1	"#-+ 0'"
    190      1.34  christos #define SKIP2	"0123456789"
    191       1.4       jtc 	do {
    192       1.6       jtc 		/*
    193       1.6       jtc 		 * Basic algorithm is to scan the format string for conversion
    194       1.6       jtc 		 * specifications -- once one is found, find out if the field
    195      1.41       kre 		 * width or precision is a '*'; if it is, gather up value.
    196       1.6       jtc 		 * Note, format strings are reused as necessary to use up the
    197      1.41       kre 		 * provided arguments, arguments of zero/null string are
    198       1.6       jtc 		 * provided to use up the format string.
    199       1.6       jtc 		 */
    200       1.6       jtc 
    201       1.6       jtc 		/* find next format specification */
    202      1.30  christos 		for (fmt = format; (ch = *fmt++) != '\0';) {
    203      1.25  christos 			if (ch == '\\') {
    204      1.25  christos 				char c_ch;
    205      1.39       kre 				fmt = conv_escape(fmt, &c_ch, 0);
    206      1.25  christos 				putchar(c_ch);
    207      1.25  christos 				continue;
    208      1.25  christos 			}
    209      1.25  christos 			if (ch != '%' || (*fmt == '%' && ++fmt)) {
    210      1.25  christos 				(void)putchar(ch);
    211      1.25  christos 				continue;
    212      1.25  christos 			}
    213      1.25  christos 
    214      1.41       kre 			/*
    215      1.41       kre 			 * Ok - we've found a format specification,
    216      1.41       kre 			 * Save its address for a later printf().
    217      1.41       kre 			 */
    218      1.25  christos 			start = fmt - 1;
    219      1.25  christos 
    220      1.25  christos 			/* skip to field width */
    221      1.25  christos 			fmt += strspn(fmt, SKIP1);
    222      1.34  christos 			if (*fmt == '*') {
    223      1.34  christos 				fmt++;
    224      1.34  christos 				fieldwidth = getwidth();
    225      1.44       kre 			} else {
    226      1.34  christos 				fieldwidth = -1;
    227      1.25  christos 
    228      1.44       kre 				/* skip to possible '.' for precision */
    229      1.44       kre 				fmt += strspn(fmt, SKIP2);
    230      1.44       kre 			}
    231      1.44       kre 
    232      1.34  christos 			if (*fmt == '.') {
    233      1.44       kre 				 /* get following precision */
    234      1.34  christos 				fmt++;
    235      1.34  christos 				if (*fmt == '*') {
    236      1.34  christos 					fmt++;
    237      1.34  christos 					precision = getwidth();
    238      1.44       kre 				} else {
    239      1.34  christos 					precision = -1;
    240      1.44       kre 					fmt += strspn(fmt, SKIP2);
    241      1.44       kre 				}
    242      1.34  christos 			} else
    243      1.34  christos 				precision = -1;
    244      1.25  christos 
    245      1.25  christos 			ch = *fmt;
    246      1.25  christos 			if (!ch) {
    247      1.44       kre 				warnx("%s: missing format character", start);
    248      1.42       kre 				return 1;
    249      1.25  christos 			}
    250      1.44       kre 
    251      1.41       kre 			/*
    252      1.41       kre 			 * null terminate format string to we can use it
    253      1.41       kre 			 * as an argument to printf.
    254      1.41       kre 			 */
    255      1.25  christos 			nextch = fmt[1];
    256      1.25  christos 			fmt[1] = 0;
    257      1.44       kre 
    258      1.25  christos 			switch (ch) {
    259      1.25  christos 
    260      1.25  christos 			case 'B': {
    261      1.25  christos 				const char *p = conv_expand(getstr());
    262      1.41       kre 
    263      1.32  christos 				if (p == NULL)
    264      1.32  christos 					goto out;
    265      1.25  christos 				*fmt = 's';
    266      1.25  christos 				PF(start, p);
    267      1.32  christos 				if (error < 0)
    268      1.32  christos 					goto out;
    269       1.6       jtc 				break;
    270      1.25  christos 			}
    271      1.25  christos 			case 'b': {
    272      1.41       kre 				/*
    273      1.41       kre 				 * There has to be a better way to do this,
    274      1.26       dsl 				 * but the string we generate might have
    275      1.41       kre 				 * embedded nulls
    276      1.41       kre 				 */
    277      1.26       dsl 				static char *a, *t;
    278      1.26       dsl 				char *cp = getstr();
    279      1.41       kre 
    280      1.26       dsl 				/* Free on entry in case shell longjumped out */
    281      1.26       dsl 				if (a != NULL)
    282      1.26       dsl 					free(a);
    283      1.26       dsl 				a = NULL;
    284      1.26       dsl 				if (t != NULL)
    285      1.26       dsl 					free(t);
    286      1.26       dsl 				t = NULL;
    287      1.41       kre 
    288      1.26       dsl 				/* Count number of bytes we want to output */
    289      1.26       dsl 				b_length = 0;
    290      1.39       kre 				conv_escape_str(cp, b_count, 0);
    291      1.26       dsl 				t = malloc(b_length + 1);
    292      1.26       dsl 				if (t == NULL)
    293      1.32  christos 					goto out;
    294      1.32  christos 				(void)memset(t, 'x', b_length);
    295      1.26       dsl 				t[b_length] = 0;
    296      1.41       kre 
    297      1.26       dsl 				/* Get printf to calculate the lengths */
    298      1.25  christos 				*fmt = 's';
    299      1.26       dsl 				APF(&a, start, t);
    300      1.32  christos 				if (error == -1)
    301      1.32  christos 					goto out;
    302      1.26       dsl 				b_fmt = a;
    303      1.41       kre 
    304      1.26       dsl 				/* Output leading spaces and data bytes */
    305      1.39       kre 				conv_escape_str(cp, b_output, 1);
    306      1.41       kre 
    307      1.26       dsl 				/* Add any trailing spaces */
    308      1.26       dsl 				printf("%s", b_fmt);
    309      1.25  christos 				break;
    310      1.25  christos 			}
    311      1.25  christos 			case 'c': {
    312      1.25  christos 				char p = getchr();
    313      1.41       kre 
    314      1.25  christos 				PF(start, p);
    315      1.32  christos 				if (error < 0)
    316      1.32  christos 					goto out;
    317      1.25  christos 				break;
    318      1.25  christos 			}
    319      1.25  christos 			case 's': {
    320      1.25  christos 				char *p = getstr();
    321      1.41       kre 
    322      1.25  christos 				PF(start, p);
    323      1.32  christos 				if (error < 0)
    324      1.32  christos 					goto out;
    325      1.25  christos 				break;
    326      1.25  christos 			}
    327      1.25  christos 			case 'd':
    328      1.25  christos 			case 'i': {
    329      1.25  christos 				intmax_t p = getintmax();
    330      1.25  christos 				char *f = mklong(start, ch);
    331      1.41       kre 
    332      1.25  christos 				PF(f, p);
    333      1.32  christos 				if (error < 0)
    334      1.32  christos 					goto out;
    335      1.25  christos 				break;
    336      1.25  christos 			}
    337      1.25  christos 			case 'o':
    338      1.25  christos 			case 'u':
    339      1.25  christos 			case 'x':
    340      1.25  christos 			case 'X': {
    341      1.43       kre 				uintmax_t p = (uintmax_t)getintmax();
    342      1.25  christos 				char *f = mklong(start, ch);
    343      1.41       kre 
    344      1.25  christos 				PF(f, p);
    345      1.32  christos 				if (error < 0)
    346      1.32  christos 					goto out;
    347      1.25  christos 				break;
    348      1.25  christos 			}
    349      1.40       kre 			case 'a':
    350      1.40       kre 			case 'A':
    351      1.25  christos 			case 'e':
    352      1.25  christos 			case 'E':
    353      1.25  christos 			case 'f':
    354      1.40       kre 			case 'F':
    355      1.25  christos 			case 'g':
    356      1.25  christos 			case 'G': {
    357      1.25  christos 				double p = getdouble();
    358      1.41       kre 
    359      1.25  christos 				PF(start, p);
    360      1.32  christos 				if (error < 0)
    361      1.32  christos 					goto out;
    362       1.4       jtc 				break;
    363      1.25  christos 			}
    364      1.44       kre 			case '%':
    365      1.44       kre 				/* Don't ask, but this is useful ... */
    366      1.44       kre 				if (fieldwidth == 'N' && precision == 'B')
    367      1.44       kre 					return 0;
    368      1.44       kre 				/* FALLTHROUGH */
    369       1.6       jtc 			default:
    370      1.25  christos 				warnx("%s: invalid directive", start);
    371      1.30  christos 				return 1;
    372       1.4       jtc 			}
    373      1.25  christos 			*fmt++ = ch;
    374      1.25  christos 			*fmt = nextch;
    375      1.25  christos 			/* escape if a \c was encountered */
    376      1.25  christos 			if (rval & 0x100)
    377  1.52.2.1      cjep 				goto done;
    378       1.6       jtc 		}
    379      1.25  christos 	} while (gargv != argv && *gargv);
    380       1.1       cgd 
    381  1.52.2.1      cjep   done:
    382  1.52.2.1      cjep 	(void)fflush(stdout);
    383  1.52.2.1      cjep 	if (ferror(stdout)) {
    384  1.52.2.1      cjep 		clearerr(stdout);
    385  1.52.2.1      cjep 		err(1, "write error");
    386  1.52.2.1      cjep 	}
    387      1.32  christos 	return rval & ~0x100;
    388      1.41       kre   out:
    389      1.32  christos 	warn("print failed");
    390      1.32  christos 	return 1;
    391       1.4       jtc }
    392       1.4       jtc 
    393      1.26       dsl /* helper functions for conv_escape_str */
    394      1.26       dsl 
    395      1.26       dsl static void
    396      1.30  christos /*ARGSUSED*/
    397      1.26       dsl b_count(int ch)
    398      1.26       dsl {
    399      1.26       dsl 	b_length++;
    400      1.26       dsl }
    401      1.26       dsl 
    402      1.26       dsl /* Output one converted character for every 'x' in the 'format' */
    403      1.26       dsl 
    404      1.26       dsl static void
    405      1.26       dsl b_output(int ch)
    406      1.26       dsl {
    407      1.26       dsl 	for (;;) {
    408      1.26       dsl 		switch (*b_fmt++) {
    409      1.26       dsl 		case 0:
    410      1.26       dsl 			b_fmt--;
    411      1.26       dsl 			return;
    412      1.26       dsl 		case ' ':
    413      1.26       dsl 			putchar(' ');
    414      1.26       dsl 			break;
    415      1.26       dsl 		default:
    416      1.26       dsl 			putchar(ch);
    417      1.26       dsl 			return;
    418      1.26       dsl 		}
    419      1.26       dsl 	}
    420      1.26       dsl }
    421      1.26       dsl 
    422       1.4       jtc 
    423       1.4       jtc /*
    424      1.41       kre  * Print SysV echo(1) style escape string
    425      1.25  christos  *	Halts processing string if a \c escape is encountered.
    426       1.4       jtc  */
    427      1.26       dsl static void
    428      1.39       kre conv_escape_str(char *str, void (*do_putchar)(int), int quiet)
    429       1.4       jtc {
    430       1.4       jtc 	int value;
    431      1.25  christos 	int ch;
    432      1.26       dsl 	char c;
    433      1.25  christos 
    434      1.30  christos 	while ((ch = *str++) != '\0') {
    435      1.25  christos 		if (ch != '\\') {
    436      1.26       dsl 			do_putchar(ch);
    437      1.25  christos 			continue;
    438      1.25  christos 		}
    439      1.25  christos 
    440      1.25  christos 		ch = *str++;
    441      1.25  christos 		if (ch == 'c') {
    442      1.25  christos 			/* \c as in SYSV echo - abort all processing.... */
    443      1.25  christos 			rval |= 0x100;
    444      1.25  christos 			break;
    445      1.25  christos 		}
    446      1.25  christos 
    447      1.41       kre 		/*
    448      1.25  christos 		 * %b string octal constants are not like those in C.
    449      1.41       kre 		 * They start with a \0, and are followed by 0, 1, 2,
    450      1.41       kre 		 * or 3 octal digits.
    451      1.25  christos 		 */
    452      1.25  christos 		if (ch == '0') {
    453      1.30  christos 			int octnum = 0, i;
    454      1.30  christos 			for (i = 0; i < 3; i++) {
    455      1.30  christos 				if (!isdigit((unsigned char)*str) || *str > '7')
    456      1.30  christos 					break;
    457      1.31       dsl 				octnum = (octnum << 3) | (*str++ - '0');
    458      1.30  christos 			}
    459      1.30  christos 			do_putchar(octnum);
    460      1.25  christos 			continue;
    461      1.25  christos 		}
    462      1.25  christos 
    463      1.25  christos 		/* \[M][^|-]C as defined by vis(3) */
    464      1.25  christos 		if (ch == 'M' && *str == '-') {
    465      1.26       dsl 			do_putchar(0200 | str[1]);
    466      1.25  christos 			str += 2;
    467      1.25  christos 			continue;
    468      1.25  christos 		}
    469      1.25  christos 		if (ch == 'M' && *str == '^') {
    470       1.4       jtc 			str++;
    471      1.25  christos 			value = 0200;
    472      1.25  christos 			ch = '^';
    473      1.25  christos 		} else
    474      1.25  christos 			value = 0;
    475      1.25  christos 		if (ch == '^') {
    476      1.25  christos 			ch = *str++;
    477      1.25  christos 			if (ch == '?')
    478      1.25  christos 				value |= 0177;
    479      1.25  christos 			else
    480      1.25  christos 				value |= ch & 037;
    481      1.26       dsl 			do_putchar(value);
    482      1.25  christos 			continue;
    483       1.1       cgd 		}
    484      1.25  christos 
    485      1.25  christos 		/* Finally test for sequences valid in the format string */
    486      1.39       kre 		str = conv_escape(str - 1, &c, quiet);
    487      1.26       dsl 		do_putchar(c);
    488       1.4       jtc 	}
    489       1.4       jtc }
    490       1.4       jtc 
    491       1.4       jtc /*
    492      1.41       kre  * Print "standard" escape characters
    493       1.4       jtc  */
    494      1.25  christos static char *
    495      1.39       kre conv_escape(char *str, char *conv_ch, int quiet)
    496       1.4       jtc {
    497      1.52  christos 	int value = 0;
    498      1.51  christos 	char ch, *begin;
    499      1.51  christos 	int c;
    500       1.4       jtc 
    501      1.25  christos 	ch = *str++;
    502       1.4       jtc 
    503      1.25  christos 	switch (ch) {
    504      1.38       kre 	case '\0':
    505      1.39       kre 		if (!quiet)
    506      1.39       kre 			warnx("incomplete escape sequence");
    507      1.38       kre 		rval = 1;
    508      1.38       kre 		value = '\\';
    509      1.38       kre 		--str;
    510      1.38       kre 		break;
    511      1.38       kre 
    512       1.4       jtc 	case '0': case '1': case '2': case '3':
    513       1.4       jtc 	case '4': case '5': case '6': case '7':
    514      1.51  christos 		str--;
    515      1.51  christos 		for (c = 3; c-- && isodigit(*str); str++) {
    516      1.51  christos 			value <<= 3;
    517      1.51  christos 			value += octtobin(*str);
    518      1.51  christos 		}
    519      1.25  christos 		break;
    520       1.4       jtc 
    521       1.4       jtc 	case 'x':
    522      1.41       kre 		/*
    523      1.41       kre 		 * Hexadecimal character constants are not required to be
    524      1.41       kre 		 * supported (by SuS v1) because there is no consistent
    525      1.41       kre 		 * way to detect the end of the constant.
    526      1.41       kre 		 * Supporting 2 byte constants is a compromise.
    527      1.41       kre 		 */
    528      1.51  christos 		begin = str;
    529      1.51  christos 		for (c = 2; c-- && isxdigit((unsigned char)*str); str++) {
    530      1.51  christos 			value <<= 4;
    531      1.52  christos 			value += hextobin(*str);
    532      1.51  christos 		}
    533      1.51  christos 		if (str == begin) {
    534      1.51  christos 			if (!quiet)
    535      1.51  christos 				warnx("\\x%s: missing hexadecimal number "
    536      1.51  christos 				    "in escape", begin);
    537      1.51  christos 			rval = 1;
    538      1.51  christos 		}
    539      1.25  christos 		break;
    540      1.25  christos 
    541      1.25  christos 	case '\\':	value = '\\';	break;	/* backslash */
    542      1.25  christos 	case '\'':	value = '\'';	break;	/* single quote */
    543      1.25  christos 	case '"':	value = '"';	break;	/* double quote */
    544      1.25  christos 	case 'a':	value = '\a';	break;	/* alert */
    545      1.25  christos 	case 'b':	value = '\b';	break;	/* backspace */
    546      1.25  christos 	case 'e':	value = ESCAPE;	break;	/* escape */
    547      1.45       kre 	case 'E':	value = ESCAPE;	break;	/* escape */
    548      1.25  christos 	case 'f':	value = '\f';	break;	/* form-feed */
    549      1.25  christos 	case 'n':	value = '\n';	break;	/* newline */
    550      1.25  christos 	case 'r':	value = '\r';	break;	/* carriage-return */
    551      1.25  christos 	case 't':	value = '\t';	break;	/* tab */
    552      1.25  christos 	case 'v':	value = '\v';	break;	/* vertical-tab */
    553       1.4       jtc 
    554      1.25  christos 	default:
    555      1.39       kre 		if (!quiet)
    556      1.39       kre 			warnx("unknown escape sequence `\\%c'", ch);
    557      1.25  christos 		rval = 1;
    558      1.26       dsl 		value = ch;
    559       1.4       jtc 		break;
    560      1.25  christos 	}
    561       1.4       jtc 
    562      1.52  christos 	*conv_ch = (char)value;
    563      1.25  christos 	return str;
    564      1.25  christos }
    565       1.4       jtc 
    566      1.25  christos /* expand a string so that everything is printable */
    567       1.4       jtc 
    568      1.25  christos static char *
    569      1.25  christos conv_expand(const char *str)
    570      1.25  christos {
    571      1.25  christos 	static char *conv_str;
    572      1.25  christos 	char *cp;
    573      1.36  christos 	char ch;
    574       1.4       jtc 
    575      1.25  christos 	if (conv_str)
    576      1.25  christos 		free(conv_str);
    577      1.25  christos 	/* get a buffer that is definitely large enough.... */
    578      1.25  christos 	conv_str = malloc(4 * strlen(str) + 1);
    579      1.25  christos 	if (!conv_str)
    580      1.32  christos 		return NULL;
    581      1.25  christos 	cp = conv_str;
    582       1.4       jtc 
    583      1.36  christos 	while ((ch = *(const char *)str++) != '\0') {
    584      1.25  christos 		switch (ch) {
    585      1.25  christos 		/* Use C escapes for expected control characters */
    586      1.25  christos 		case '\\':	ch = '\\';	break;	/* backslash */
    587      1.25  christos 		case '\'':	ch = '\'';	break;	/* single quote */
    588      1.25  christos 		case '"':	ch = '"';	break;	/* double quote */
    589      1.25  christos 		case '\a':	ch = 'a';	break;	/* alert */
    590      1.25  christos 		case '\b':	ch = 'b';	break;	/* backspace */
    591      1.25  christos 		case ESCAPE:	ch = 'e';	break;	/* escape */
    592      1.25  christos 		case '\f':	ch = 'f';	break;	/* form-feed */
    593      1.25  christos 		case '\n':	ch = 'n';	break;	/* newline */
    594      1.25  christos 		case '\r':	ch = 'r';	break;	/* carriage-return */
    595      1.25  christos 		case '\t':	ch = 't';	break;	/* tab */
    596      1.25  christos 		case '\v':	ch = 'v';	break;	/* vertical-tab */
    597      1.25  christos 		default:
    598      1.25  christos 			/* Copy anything printable */
    599      1.36  christos 			if (isprint((unsigned char)ch)) {
    600      1.25  christos 				*cp++ = ch;
    601      1.25  christos 				continue;
    602      1.25  christos 			}
    603      1.25  christos 			/* Use vis(3) encodings for the rest */
    604      1.25  christos 			*cp++ = '\\';
    605      1.25  christos 			if (ch & 0200) {
    606      1.25  christos 				*cp++ = 'M';
    607      1.36  christos 				ch &= (char)~0200;
    608      1.25  christos 			}
    609      1.25  christos 			if (ch == 0177) {
    610      1.25  christos 				*cp++ = '^';
    611      1.25  christos 				*cp++ = '?';
    612      1.25  christos 				continue;
    613      1.25  christos 			}
    614      1.25  christos 			if (ch < 040) {
    615      1.25  christos 				*cp++ = '^';
    616      1.25  christos 				*cp++ = ch | 0100;
    617      1.25  christos 				continue;
    618      1.25  christos 			}
    619      1.25  christos 			*cp++ = '-';
    620      1.25  christos 			*cp++ = ch;
    621      1.25  christos 			continue;
    622      1.25  christos 		}
    623      1.25  christos 		*cp++ = '\\';
    624      1.25  christos 		*cp++ = ch;
    625       1.1       cgd 	}
    626       1.4       jtc 
    627      1.25  christos 	*cp = 0;
    628      1.25  christos 	return conv_str;
    629       1.1       cgd }
    630       1.1       cgd 
    631       1.5       jtc static char *
    632      1.36  christos mklong(const char *str, char ch)
    633       1.1       cgd {
    634       1.5       jtc 	static char copy[64];
    635      1.15       cgd 	size_t len;
    636       1.1       cgd 
    637       1.1       cgd 	len = strlen(str) + 2;
    638      1.25  christos 	if (len > sizeof copy) {
    639  1.52.2.1      cjep 		warnx("format \"%s\" too complex", str);
    640      1.25  christos 		len = 4;
    641  1.52.2.1      cjep 		rval = 1;
    642      1.25  christos 	}
    643      1.15       cgd 	(void)memmove(copy, str, len - 3);
    644      1.22    kleink 	copy[len - 3] = 'j';
    645       1.1       cgd 	copy[len - 2] = ch;
    646       1.1       cgd 	copy[len - 1] = '\0';
    647      1.30  christos 	return copy;
    648       1.1       cgd }
    649       1.1       cgd 
    650      1.36  christos static char
    651      1.23       wiz getchr(void)
    652       1.1       cgd {
    653       1.1       cgd 	if (!*gargv)
    654      1.30  christos 		return 0;
    655      1.36  christos 	return **gargv++;
    656       1.1       cgd }
    657       1.1       cgd 
    658       1.5       jtc static char *
    659      1.23       wiz getstr(void)
    660       1.1       cgd {
    661      1.30  christos 	static char empty[] = "";
    662       1.1       cgd 	if (!*gargv)
    663      1.30  christos 		return empty;
    664      1.30  christos 	return *gargv++;
    665       1.1       cgd }
    666       1.1       cgd 
    667       1.5       jtc static int
    668      1.25  christos getwidth(void)
    669       1.1       cgd {
    670      1.36  christos 	unsigned long val;
    671      1.25  christos 	char *s, *ep;
    672      1.25  christos 
    673      1.25  christos 	s = *gargv;
    674      1.44       kre 	if (s == NULL)
    675      1.42       kre 		return 0;
    676      1.25  christos 	gargv++;
    677       1.4       jtc 
    678      1.25  christos 	errno = 0;
    679      1.25  christos 	val = strtoul(s, &ep, 0);
    680      1.25  christos 	check_conversion(s, ep);
    681       1.4       jtc 
    682      1.25  christos 	/* Arbitrarily 'restrict' field widths to 1Mbyte */
    683      1.36  christos 	if (val > 1 << 20) {
    684      1.25  christos 		warnx("%s: invalid field width", s);
    685      1.25  christos 		return 0;
    686      1.25  christos 	}
    687      1.25  christos 
    688      1.36  christos 	return (int)val;
    689       1.1       cgd }
    690       1.1       cgd 
    691      1.22    kleink static intmax_t
    692      1.23       wiz getintmax(void)
    693       1.1       cgd {
    694      1.22    kleink 	intmax_t val;
    695      1.25  christos 	char *cp, *ep;
    696       1.1       cgd 
    697      1.25  christos 	cp = *gargv;
    698      1.25  christos 	if (cp == NULL)
    699      1.25  christos 		return 0;
    700      1.25  christos 	gargv++;
    701       1.4       jtc 
    702      1.25  christos 	if (*cp == '\"' || *cp == '\'')
    703  1.52.2.1      cjep 		return wide_char(cp);
    704       1.4       jtc 
    705      1.11       jtc 	errno = 0;
    706      1.25  christos 	val = strtoimax(cp, &ep, 0);
    707      1.25  christos 	check_conversion(cp, ep);
    708      1.11       jtc 	return val;
    709      1.11       jtc }
    710      1.11       jtc 
    711       1.5       jtc static double
    712      1.23       wiz getdouble(void)
    713       1.1       cgd {
    714       1.4       jtc 	double val;
    715       1.4       jtc 	char *ep;
    716       1.1       cgd 
    717       1.1       cgd 	if (!*gargv)
    718      1.42       kre 		return 0.0;
    719       1.4       jtc 
    720  1.52.2.1      cjep 	/* This is a NetBSD extension, not required by POSIX (it is useless) */
    721  1.52.2.1      cjep 	if (*(ep = *gargv) == '\"' || *ep == '\'')
    722  1.52.2.1      cjep 		return (double)wide_char(ep);
    723       1.1       cgd 
    724      1.11       jtc 	errno = 0;
    725      1.48       kre 	val = strtod(*gargv, &ep);
    726      1.12       jtc 	check_conversion(*gargv++, ep);
    727      1.12       jtc 	return val;
    728      1.12       jtc }
    729      1.12       jtc 
    730  1.52.2.1      cjep /*
    731  1.52.2.1      cjep  * XXX This is just a placeholder for a later version which
    732  1.52.2.1      cjep  *     will do mbtowc() on p+1 (and after checking that all of the
    733  1.52.2.1      cjep  *     string has been consumed) return that value.
    734  1.52.2.1      cjep  *
    735  1.52.2.1      cjep  * This (mbtowc) behaviour is required by POSIX (as is the check
    736  1.52.2.1      cjep  * that the whole arg is consumed).
    737  1.52.2.1      cjep  *
    738  1.52.2.1      cjep  * What follows is actually correct if we assume that LC_CTYPE=C
    739  1.52.2.1      cjep  * (or something else similar that is a single byte charset).
    740  1.52.2.1      cjep  */
    741  1.52.2.1      cjep static intmax_t
    742  1.52.2.1      cjep wide_char(const char *p)
    743  1.52.2.1      cjep {
    744  1.52.2.1      cjep 	intmax_t ch = (intmax_t)(unsigned char)p[1];
    745  1.52.2.1      cjep 
    746  1.52.2.1      cjep 	if (ch != 0 && p[2] != '\0') {
    747  1.52.2.1      cjep 		warnx("%s: not completely converted", p);
    748  1.52.2.1      cjep 		rval = 1;
    749  1.52.2.1      cjep 	}
    750  1.52.2.1      cjep 
    751  1.52.2.1      cjep 	return ch;
    752  1.52.2.1      cjep }
    753  1.52.2.1      cjep 
    754      1.12       jtc static void
    755      1.23       wiz check_conversion(const char *s, const char *ep)
    756      1.12       jtc {
    757       1.4       jtc 	if (*ep) {
    758      1.12       jtc 		if (ep == s)
    759      1.25  christos 			warnx("%s: expected numeric value", s);
    760      1.11       jtc 		else
    761      1.25  christos 			warnx("%s: not completely converted", s);
    762      1.11       jtc 		rval = 1;
    763      1.11       jtc 	} else if (errno == ERANGE) {
    764      1.25  christos 		warnx("%s: %s", s, strerror(ERANGE));
    765       1.4       jtc 		rval = 1;
    766       1.4       jtc 	}
    767       1.5       jtc }
    768       1.5       jtc 
    769       1.5       jtc static void
    770      1.23       wiz usage(void)
    771       1.5       jtc {
    772      1.30  christos 	(void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname());
    773       1.1       cgd }
    774