Home | History | Annotate | Line # | Download | only in printf
printf.c revision 1.38
      1  1.38       kre /*	$NetBSD: printf.c,v 1.38 2018/07/03 01:54:42 kre 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.38       kre __RCSID("$NetBSD: printf.c,v 1.38 2018/07/03 01:54:42 kre 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.26       dsl static void	 conv_escape_str(char *, void (*)(int));
     69  1.25  christos static char	*conv_escape(char *, char *);
     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.25  christos static uintmax_t getuintmax(void);
     76  1.23       wiz static char	*getstr(void);
     77  1.36  christos static char	*mklong(const char *, char);
     78  1.23       wiz static void      check_conversion(const char *, const char *);
     79  1.23       wiz 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.32  christos #ifdef main
    123  1.32  christos int main(int, char *[]);
    124  1.32  christos #endif
    125  1.25  christos int main(int argc, char *argv[])
    126   1.1       cgd {
    127  1.18     lukem 	char *fmt, *start;
    128  1.18     lukem 	int fieldwidth, precision;
    129  1.25  christos 	char nextch;
    130   1.4       jtc 	char *format;
    131  1.36  christos 	char ch;
    132  1.36  christos 	int error, o;
    133   1.4       jtc 
    134   1.5       jtc #if !defined(SHELL) && !defined(BUILTIN)
    135  1.15       cgd 	(void)setlocale (LC_ALL, "");
    136   1.5       jtc #endif
    137   1.1       cgd 
    138  1.36  christos 	while ((o = getopt(argc, argv, "")) != -1) {
    139  1.36  christos 		switch (o) {
    140   1.5       jtc 		case '?':
    141   1.5       jtc 		default:
    142   1.5       jtc 			usage();
    143  1.30  christos 			return 1;
    144   1.5       jtc 		}
    145   1.1       cgd 	}
    146   1.5       jtc 	argc -= optind;
    147   1.5       jtc 	argv += optind;
    148   1.1       cgd 
    149   1.5       jtc 	if (argc < 1) {
    150   1.5       jtc 		usage();
    151  1.30  christos 		return 1;
    152   1.5       jtc 	}
    153   1.5       jtc 
    154   1.5       jtc 	format = *argv;
    155   1.5       jtc 	gargv = ++argv;
    156   1.4       jtc 
    157  1.35  christos #define SKIP1	"#-+ 0'"
    158  1.34  christos #define SKIP2	"0123456789"
    159   1.4       jtc 	do {
    160   1.6       jtc 		/*
    161   1.6       jtc 		 * Basic algorithm is to scan the format string for conversion
    162   1.6       jtc 		 * specifications -- once one is found, find out if the field
    163   1.6       jtc 		 * width or precision is a '*'; if it is, gather up value.
    164   1.6       jtc 		 * Note, format strings are reused as necessary to use up the
    165   1.6       jtc 		 * provided arguments, arguments of zero/null string are
    166   1.6       jtc 		 * provided to use up the format string.
    167   1.6       jtc 		 */
    168   1.6       jtc 
    169   1.6       jtc 		/* find next format specification */
    170  1.30  christos 		for (fmt = format; (ch = *fmt++) != '\0';) {
    171  1.25  christos 			if (ch == '\\') {
    172  1.25  christos 				char c_ch;
    173  1.25  christos 				fmt = conv_escape(fmt, &c_ch);
    174  1.25  christos 				putchar(c_ch);
    175  1.25  christos 				continue;
    176  1.25  christos 			}
    177  1.25  christos 			if (ch != '%' || (*fmt == '%' && ++fmt)) {
    178  1.25  christos 				(void)putchar(ch);
    179  1.25  christos 				continue;
    180  1.25  christos 			}
    181  1.25  christos 
    182  1.25  christos 			/* Ok - we've found a format specification,
    183  1.25  christos 			   Save its address for a later printf(). */
    184  1.25  christos 			start = fmt - 1;
    185  1.25  christos 
    186  1.25  christos 			/* skip to field width */
    187  1.25  christos 			fmt += strspn(fmt, SKIP1);
    188  1.34  christos 			if (*fmt == '*') {
    189  1.34  christos 				fmt++;
    190  1.34  christos 				fieldwidth = getwidth();
    191  1.34  christos 			} else
    192  1.34  christos 				fieldwidth = -1;
    193  1.25  christos 
    194  1.25  christos 			/* skip to possible '.', get following precision */
    195  1.25  christos 			fmt += strspn(fmt, SKIP2);
    196  1.34  christos 			if (*fmt == '.') {
    197  1.34  christos 				fmt++;
    198  1.34  christos 				if (*fmt == '*') {
    199  1.34  christos 					fmt++;
    200  1.34  christos 					precision = getwidth();
    201  1.34  christos 				} else
    202  1.34  christos 					precision = -1;
    203  1.34  christos 			} else
    204  1.34  christos 				precision = -1;
    205  1.25  christos 
    206  1.25  christos 			fmt += strspn(fmt, SKIP2);
    207  1.25  christos 
    208  1.25  christos 			ch = *fmt;
    209  1.25  christos 			if (!ch) {
    210  1.25  christos 				warnx("missing format character");
    211  1.25  christos 				return (1);
    212  1.25  christos 			}
    213  1.25  christos 			/* null terminate format string to we can use it
    214  1.25  christos 			   as an argument to printf. */
    215  1.25  christos 			nextch = fmt[1];
    216  1.25  christos 			fmt[1] = 0;
    217  1.25  christos 			switch (ch) {
    218  1.25  christos 
    219  1.25  christos 			case 'B': {
    220  1.25  christos 				const char *p = conv_expand(getstr());
    221  1.32  christos 				if (p == NULL)
    222  1.32  christos 					goto out;
    223  1.25  christos 				*fmt = 's';
    224  1.25  christos 				PF(start, p);
    225  1.32  christos 				if (error < 0)
    226  1.32  christos 					goto out;
    227   1.6       jtc 				break;
    228  1.25  christos 			}
    229  1.25  christos 			case 'b': {
    230  1.26       dsl 				/* There has to be a better way to do this,
    231  1.26       dsl 				 * but the string we generate might have
    232  1.26       dsl 				 * embedded nulls. */
    233  1.26       dsl 				static char *a, *t;
    234  1.26       dsl 				char *cp = getstr();
    235  1.26       dsl 				/* Free on entry in case shell longjumped out */
    236  1.26       dsl 				if (a != NULL)
    237  1.26       dsl 					free(a);
    238  1.26       dsl 				a = NULL;
    239  1.26       dsl 				if (t != NULL)
    240  1.26       dsl 					free(t);
    241  1.26       dsl 				t = NULL;
    242  1.26       dsl 				/* Count number of bytes we want to output */
    243  1.26       dsl 				b_length = 0;
    244  1.26       dsl 				conv_escape_str(cp, b_count);
    245  1.26       dsl 				t = malloc(b_length + 1);
    246  1.26       dsl 				if (t == NULL)
    247  1.32  christos 					goto out;
    248  1.32  christos 				(void)memset(t, 'x', b_length);
    249  1.26       dsl 				t[b_length] = 0;
    250  1.26       dsl 				/* Get printf to calculate the lengths */
    251  1.25  christos 				*fmt = 's';
    252  1.26       dsl 				APF(&a, start, t);
    253  1.32  christos 				if (error == -1)
    254  1.32  christos 					goto out;
    255  1.26       dsl 				b_fmt = a;
    256  1.26       dsl 				/* Output leading spaces and data bytes */
    257  1.26       dsl 				conv_escape_str(cp, b_output);
    258  1.26       dsl 				/* Add any trailing spaces */
    259  1.26       dsl 				printf("%s", b_fmt);
    260  1.25  christos 				break;
    261  1.25  christos 			}
    262  1.25  christos 			case 'c': {
    263  1.25  christos 				char p = getchr();
    264  1.25  christos 				PF(start, p);
    265  1.32  christos 				if (error < 0)
    266  1.32  christos 					goto out;
    267  1.25  christos 				break;
    268  1.25  christos 			}
    269  1.25  christos 			case 's': {
    270  1.25  christos 				char *p = getstr();
    271  1.25  christos 				PF(start, p);
    272  1.32  christos 				if (error < 0)
    273  1.32  christos 					goto out;
    274  1.25  christos 				break;
    275  1.25  christos 			}
    276  1.25  christos 			case 'd':
    277  1.25  christos 			case 'i': {
    278  1.25  christos 				intmax_t p = getintmax();
    279  1.25  christos 				char *f = mklong(start, ch);
    280  1.25  christos 				PF(f, p);
    281  1.32  christos 				if (error < 0)
    282  1.32  christos 					goto out;
    283  1.25  christos 				break;
    284  1.25  christos 			}
    285  1.25  christos 			case 'o':
    286  1.25  christos 			case 'u':
    287  1.25  christos 			case 'x':
    288  1.25  christos 			case 'X': {
    289  1.25  christos 				uintmax_t p = getuintmax();
    290  1.25  christos 				char *f = mklong(start, ch);
    291  1.25  christos 				PF(f, p);
    292  1.32  christos 				if (error < 0)
    293  1.32  christos 					goto out;
    294  1.25  christos 				break;
    295  1.25  christos 			}
    296  1.25  christos 			case 'e':
    297  1.25  christos 			case 'E':
    298  1.25  christos 			case 'f':
    299  1.25  christos 			case 'g':
    300  1.25  christos 			case 'G': {
    301  1.25  christos 				double p = getdouble();
    302  1.25  christos 				PF(start, p);
    303  1.32  christos 				if (error < 0)
    304  1.32  christos 					goto out;
    305   1.4       jtc 				break;
    306  1.25  christos 			}
    307   1.6       jtc 			default:
    308  1.25  christos 				warnx("%s: invalid directive", start);
    309  1.30  christos 				return 1;
    310   1.4       jtc 			}
    311  1.25  christos 			*fmt++ = ch;
    312  1.25  christos 			*fmt = nextch;
    313  1.25  christos 			/* escape if a \c was encountered */
    314  1.25  christos 			if (rval & 0x100)
    315  1.30  christos 				return rval & ~0x100;
    316   1.6       jtc 		}
    317  1.25  christos 	} while (gargv != argv && *gargv);
    318   1.1       cgd 
    319  1.32  christos 	return rval & ~0x100;
    320  1.32  christos out:
    321  1.32  christos 	warn("print failed");
    322  1.32  christos 	return 1;
    323   1.4       jtc }
    324   1.4       jtc 
    325  1.26       dsl /* helper functions for conv_escape_str */
    326  1.26       dsl 
    327  1.26       dsl static void
    328  1.30  christos /*ARGSUSED*/
    329  1.26       dsl b_count(int ch)
    330  1.26       dsl {
    331  1.26       dsl 	b_length++;
    332  1.26       dsl }
    333  1.26       dsl 
    334  1.26       dsl /* Output one converted character for every 'x' in the 'format' */
    335  1.26       dsl 
    336  1.26       dsl static void
    337  1.26       dsl b_output(int ch)
    338  1.26       dsl {
    339  1.26       dsl 	for (;;) {
    340  1.26       dsl 		switch (*b_fmt++) {
    341  1.26       dsl 		case 0:
    342  1.26       dsl 			b_fmt--;
    343  1.26       dsl 			return;
    344  1.26       dsl 		case ' ':
    345  1.26       dsl 			putchar(' ');
    346  1.26       dsl 			break;
    347  1.26       dsl 		default:
    348  1.26       dsl 			putchar(ch);
    349  1.26       dsl 			return;
    350  1.26       dsl 		}
    351  1.26       dsl 	}
    352  1.26       dsl }
    353  1.26       dsl 
    354   1.4       jtc 
    355   1.4       jtc /*
    356   1.4       jtc  * Print SysV echo(1) style escape string
    357  1.25  christos  *	Halts processing string if a \c escape is encountered.
    358   1.4       jtc  */
    359  1.26       dsl static void
    360  1.26       dsl conv_escape_str(char *str, void (*do_putchar)(int))
    361   1.4       jtc {
    362   1.4       jtc 	int value;
    363  1.25  christos 	int ch;
    364  1.26       dsl 	char c;
    365  1.25  christos 
    366  1.30  christos 	while ((ch = *str++) != '\0') {
    367  1.25  christos 		if (ch != '\\') {
    368  1.26       dsl 			do_putchar(ch);
    369  1.25  christos 			continue;
    370  1.25  christos 		}
    371  1.25  christos 
    372  1.25  christos 		ch = *str++;
    373  1.25  christos 		if (ch == 'c') {
    374  1.25  christos 			/* \c as in SYSV echo - abort all processing.... */
    375  1.25  christos 			rval |= 0x100;
    376  1.25  christos 			break;
    377  1.25  christos 		}
    378  1.25  christos 
    379  1.25  christos 		/*
    380  1.25  christos 		 * %b string octal constants are not like those in C.
    381  1.25  christos 		 * They start with a \0, and are followed by 0, 1, 2,
    382  1.25  christos 		 * or 3 octal digits.
    383  1.25  christos 		 */
    384  1.25  christos 		if (ch == '0') {
    385  1.30  christos 			int octnum = 0, i;
    386  1.30  christos 			for (i = 0; i < 3; i++) {
    387  1.30  christos 				if (!isdigit((unsigned char)*str) || *str > '7')
    388  1.30  christos 					break;
    389  1.31       dsl 				octnum = (octnum << 3) | (*str++ - '0');
    390  1.30  christos 			}
    391  1.30  christos 			do_putchar(octnum);
    392  1.25  christos 			continue;
    393  1.25  christos 		}
    394  1.25  christos 
    395  1.25  christos 		/* \[M][^|-]C as defined by vis(3) */
    396  1.25  christos 		if (ch == 'M' && *str == '-') {
    397  1.26       dsl 			do_putchar(0200 | str[1]);
    398  1.25  christos 			str += 2;
    399  1.25  christos 			continue;
    400  1.25  christos 		}
    401  1.25  christos 		if (ch == 'M' && *str == '^') {
    402   1.4       jtc 			str++;
    403  1.25  christos 			value = 0200;
    404  1.25  christos 			ch = '^';
    405  1.25  christos 		} else
    406  1.25  christos 			value = 0;
    407  1.25  christos 		if (ch == '^') {
    408  1.25  christos 			ch = *str++;
    409  1.25  christos 			if (ch == '?')
    410  1.25  christos 				value |= 0177;
    411  1.25  christos 			else
    412  1.25  christos 				value |= ch & 037;
    413  1.26       dsl 			do_putchar(value);
    414  1.25  christos 			continue;
    415   1.1       cgd 		}
    416  1.25  christos 
    417  1.25  christos 		/* Finally test for sequences valid in the format string */
    418  1.26       dsl 		str = conv_escape(str - 1, &c);
    419  1.26       dsl 		do_putchar(c);
    420   1.4       jtc 	}
    421   1.4       jtc }
    422   1.4       jtc 
    423   1.4       jtc /*
    424   1.4       jtc  * Print "standard" escape characters
    425   1.4       jtc  */
    426  1.25  christos static char *
    427  1.25  christos conv_escape(char *str, char *conv_ch)
    428   1.4       jtc {
    429  1.36  christos 	char value;
    430  1.36  christos 	char ch;
    431  1.25  christos 	char num_buf[4], *num_end;
    432   1.4       jtc 
    433  1.25  christos 	ch = *str++;
    434   1.4       jtc 
    435  1.25  christos 	switch (ch) {
    436  1.38       kre 	case '\0':
    437  1.38       kre 		warnx("incomplete escape sequence");
    438  1.38       kre 		rval = 1;
    439  1.38       kre 		value = '\\';
    440  1.38       kre 		--str;
    441  1.38       kre 		break;
    442  1.38       kre 
    443   1.4       jtc 	case '0': case '1': case '2': case '3':
    444   1.4       jtc 	case '4': case '5': case '6': case '7':
    445  1.25  christos 		num_buf[0] = ch;
    446  1.25  christos 		ch = str[0];
    447  1.25  christos 		num_buf[1] = ch;
    448  1.36  christos 		num_buf[2] = (char)(ch != '\0' ? str[1] : '\0');
    449  1.36  christos 		num_buf[3] = '\0';
    450  1.36  christos 		value = (char)strtoul(num_buf, &num_end, 8);
    451  1.25  christos 		str += num_end  - (num_buf + 1);
    452  1.25  christos 		break;
    453   1.4       jtc 
    454   1.4       jtc 	case 'x':
    455  1.25  christos 		/* Hexadecimal character constants are not required to be
    456  1.25  christos 		   supported (by SuS v1) because there is no consistent
    457  1.25  christos 		   way to detect the end of the constant.
    458  1.25  christos 		   Supporting 2 byte constants is a compromise. */
    459  1.25  christos 		ch = str[0];
    460  1.25  christos 		num_buf[0] = ch;
    461  1.36  christos 		num_buf[1] = (char)(ch != '\0' ? str[1] : '\0');
    462  1.36  christos 		num_buf[2] = '\0';
    463  1.36  christos 		value = (char)strtoul(num_buf, &num_end, 16);
    464  1.25  christos 		str += num_end - num_buf;
    465  1.25  christos 		break;
    466  1.25  christos 
    467  1.25  christos 	case '\\':	value = '\\';	break;	/* backslash */
    468  1.25  christos 	case '\'':	value = '\'';	break;	/* single quote */
    469  1.25  christos 	case '"':	value = '"';	break;	/* double quote */
    470  1.25  christos 	case 'a':	value = '\a';	break;	/* alert */
    471  1.25  christos 	case 'b':	value = '\b';	break;	/* backspace */
    472  1.25  christos 	case 'e':	value = ESCAPE;	break;	/* escape */
    473  1.25  christos 	case 'f':	value = '\f';	break;	/* form-feed */
    474  1.25  christos 	case 'n':	value = '\n';	break;	/* newline */
    475  1.25  christos 	case 'r':	value = '\r';	break;	/* carriage-return */
    476  1.25  christos 	case 't':	value = '\t';	break;	/* tab */
    477  1.25  christos 	case 'v':	value = '\v';	break;	/* vertical-tab */
    478   1.4       jtc 
    479  1.25  christos 	default:
    480  1.26       dsl 		warnx("unknown escape sequence `\\%c'", ch);
    481  1.25  christos 		rval = 1;
    482  1.26       dsl 		value = ch;
    483   1.4       jtc 		break;
    484  1.25  christos 	}
    485   1.4       jtc 
    486  1.25  christos 	*conv_ch = value;
    487  1.25  christos 	return str;
    488  1.25  christos }
    489   1.4       jtc 
    490  1.25  christos /* expand a string so that everything is printable */
    491   1.4       jtc 
    492  1.25  christos static char *
    493  1.25  christos conv_expand(const char *str)
    494  1.25  christos {
    495  1.25  christos 	static char *conv_str;
    496  1.25  christos 	char *cp;
    497  1.36  christos 	char ch;
    498   1.4       jtc 
    499  1.25  christos 	if (conv_str)
    500  1.25  christos 		free(conv_str);
    501  1.25  christos 	/* get a buffer that is definitely large enough.... */
    502  1.25  christos 	conv_str = malloc(4 * strlen(str) + 1);
    503  1.25  christos 	if (!conv_str)
    504  1.32  christos 		return NULL;
    505  1.25  christos 	cp = conv_str;
    506   1.4       jtc 
    507  1.36  christos 	while ((ch = *(const char *)str++) != '\0') {
    508  1.25  christos 		switch (ch) {
    509  1.25  christos 		/* Use C escapes for expected control characters */
    510  1.25  christos 		case '\\':	ch = '\\';	break;	/* backslash */
    511  1.25  christos 		case '\'':	ch = '\'';	break;	/* single quote */
    512  1.25  christos 		case '"':	ch = '"';	break;	/* double quote */
    513  1.25  christos 		case '\a':	ch = 'a';	break;	/* alert */
    514  1.25  christos 		case '\b':	ch = 'b';	break;	/* backspace */
    515  1.25  christos 		case ESCAPE:	ch = 'e';	break;	/* escape */
    516  1.25  christos 		case '\f':	ch = 'f';	break;	/* form-feed */
    517  1.25  christos 		case '\n':	ch = 'n';	break;	/* newline */
    518  1.25  christos 		case '\r':	ch = 'r';	break;	/* carriage-return */
    519  1.25  christos 		case '\t':	ch = 't';	break;	/* tab */
    520  1.25  christos 		case '\v':	ch = 'v';	break;	/* vertical-tab */
    521  1.25  christos 		default:
    522  1.25  christos 			/* Copy anything printable */
    523  1.36  christos 			if (isprint((unsigned char)ch)) {
    524  1.25  christos 				*cp++ = ch;
    525  1.25  christos 				continue;
    526  1.25  christos 			}
    527  1.25  christos 			/* Use vis(3) encodings for the rest */
    528  1.25  christos 			*cp++ = '\\';
    529  1.25  christos 			if (ch & 0200) {
    530  1.25  christos 				*cp++ = 'M';
    531  1.36  christos 				ch &= (char)~0200;
    532  1.25  christos 			}
    533  1.25  christos 			if (ch == 0177) {
    534  1.25  christos 				*cp++ = '^';
    535  1.25  christos 				*cp++ = '?';
    536  1.25  christos 				continue;
    537  1.25  christos 			}
    538  1.25  christos 			if (ch < 040) {
    539  1.25  christos 				*cp++ = '^';
    540  1.25  christos 				*cp++ = ch | 0100;
    541  1.25  christos 				continue;
    542  1.25  christos 			}
    543  1.25  christos 			*cp++ = '-';
    544  1.25  christos 			*cp++ = ch;
    545  1.25  christos 			continue;
    546  1.25  christos 		}
    547  1.25  christos 		*cp++ = '\\';
    548  1.25  christos 		*cp++ = ch;
    549   1.1       cgd 	}
    550   1.4       jtc 
    551  1.25  christos 	*cp = 0;
    552  1.25  christos 	return conv_str;
    553   1.1       cgd }
    554   1.1       cgd 
    555   1.5       jtc static char *
    556  1.36  christos mklong(const char *str, char ch)
    557   1.1       cgd {
    558   1.5       jtc 	static char copy[64];
    559  1.15       cgd 	size_t len;
    560   1.1       cgd 
    561   1.1       cgd 	len = strlen(str) + 2;
    562  1.25  christos 	if (len > sizeof copy) {
    563  1.37  christos 		warnx("format %s too complex", str);
    564  1.25  christos 		len = 4;
    565  1.25  christos 	}
    566  1.15       cgd 	(void)memmove(copy, str, len - 3);
    567  1.22    kleink 	copy[len - 3] = 'j';
    568   1.1       cgd 	copy[len - 2] = ch;
    569   1.1       cgd 	copy[len - 1] = '\0';
    570  1.30  christos 	return copy;
    571   1.1       cgd }
    572   1.1       cgd 
    573  1.36  christos static char
    574  1.23       wiz getchr(void)
    575   1.1       cgd {
    576   1.1       cgd 	if (!*gargv)
    577  1.30  christos 		return 0;
    578  1.36  christos 	return **gargv++;
    579   1.1       cgd }
    580   1.1       cgd 
    581   1.5       jtc static char *
    582  1.23       wiz getstr(void)
    583   1.1       cgd {
    584  1.30  christos 	static char empty[] = "";
    585   1.1       cgd 	if (!*gargv)
    586  1.30  christos 		return empty;
    587  1.30  christos 	return *gargv++;
    588   1.1       cgd }
    589   1.1       cgd 
    590   1.5       jtc static int
    591  1.25  christos getwidth(void)
    592   1.1       cgd {
    593  1.36  christos 	unsigned long val;
    594  1.25  christos 	char *s, *ep;
    595  1.25  christos 
    596  1.25  christos 	s = *gargv;
    597   1.1       cgd 	if (!*gargv)
    598  1.25  christos 		return (0);
    599  1.25  christos 	gargv++;
    600   1.4       jtc 
    601  1.25  christos 	errno = 0;
    602  1.25  christos 	val = strtoul(s, &ep, 0);
    603  1.25  christos 	check_conversion(s, ep);
    604   1.4       jtc 
    605  1.25  christos 	/* Arbitrarily 'restrict' field widths to 1Mbyte */
    606  1.36  christos 	if (val > 1 << 20) {
    607  1.25  christos 		warnx("%s: invalid field width", s);
    608  1.25  christos 		return 0;
    609  1.25  christos 	}
    610  1.25  christos 
    611  1.36  christos 	return (int)val;
    612   1.1       cgd }
    613   1.1       cgd 
    614  1.22    kleink static intmax_t
    615  1.23       wiz getintmax(void)
    616   1.1       cgd {
    617  1.22    kleink 	intmax_t val;
    618  1.25  christos 	char *cp, *ep;
    619   1.1       cgd 
    620  1.25  christos 	cp = *gargv;
    621  1.25  christos 	if (cp == NULL)
    622  1.25  christos 		return 0;
    623  1.25  christos 	gargv++;
    624   1.4       jtc 
    625  1.25  christos 	if (*cp == '\"' || *cp == '\'')
    626  1.36  christos 		return *(cp + 1);
    627   1.4       jtc 
    628  1.11       jtc 	errno = 0;
    629  1.25  christos 	val = strtoimax(cp, &ep, 0);
    630  1.25  christos 	check_conversion(cp, ep);
    631  1.11       jtc 	return val;
    632  1.11       jtc }
    633  1.11       jtc 
    634  1.22    kleink static uintmax_t
    635  1.23       wiz getuintmax(void)
    636  1.11       jtc {
    637  1.22    kleink 	uintmax_t val;
    638  1.25  christos 	char *cp, *ep;
    639  1.11       jtc 
    640  1.25  christos 	cp = *gargv;
    641  1.25  christos 	if (cp == NULL)
    642  1.25  christos 		return 0;
    643  1.25  christos 	gargv++;
    644  1.25  christos 
    645  1.25  christos 	if (*cp == '\"' || *cp == '\'')
    646  1.36  christos 		return (uintmax_t)*(cp + 1);
    647  1.25  christos 
    648  1.25  christos 	/* strtoumax won't error -ve values */
    649  1.25  christos 	while (isspace(*(unsigned char *)cp))
    650  1.25  christos 		cp++;
    651  1.25  christos 	if (*cp == '-') {
    652  1.25  christos 		warnx("%s: expected positive numeric value", cp);
    653  1.25  christos 		rval = 1;
    654  1.25  christos 		return 0;
    655  1.25  christos 	}
    656  1.11       jtc 
    657  1.11       jtc 	errno = 0;
    658  1.25  christos 	val = strtoumax(cp, &ep, 0);
    659  1.25  christos 	check_conversion(cp, ep);
    660   1.4       jtc 	return val;
    661   1.1       cgd }
    662   1.1       cgd 
    663   1.5       jtc static double
    664  1.23       wiz getdouble(void)
    665   1.1       cgd {
    666   1.4       jtc 	double val;
    667   1.4       jtc 	char *ep;
    668   1.1       cgd 
    669   1.1       cgd 	if (!*gargv)
    670  1.25  christos 		return (0.0);
    671   1.4       jtc 
    672  1.13       jtc 	if (**gargv == '\"' || **gargv == '\'')
    673  1.13       jtc 		return (double) *((*gargv++)+1);
    674   1.1       cgd 
    675  1.11       jtc 	errno = 0;
    676  1.25  christos 	val = strtod(*gargv, &ep);
    677  1.12       jtc 	check_conversion(*gargv++, ep);
    678  1.12       jtc 	return val;
    679  1.12       jtc }
    680  1.12       jtc 
    681  1.12       jtc static void
    682  1.23       wiz check_conversion(const char *s, const char *ep)
    683  1.12       jtc {
    684   1.4       jtc 	if (*ep) {
    685  1.12       jtc 		if (ep == s)
    686  1.25  christos 			warnx("%s: expected numeric value", s);
    687  1.11       jtc 		else
    688  1.25  christos 			warnx("%s: not completely converted", s);
    689  1.11       jtc 		rval = 1;
    690  1.11       jtc 	} else if (errno == ERANGE) {
    691  1.25  christos 		warnx("%s: %s", s, strerror(ERANGE));
    692   1.4       jtc 		rval = 1;
    693   1.4       jtc 	}
    694   1.5       jtc }
    695   1.5       jtc 
    696   1.5       jtc static void
    697  1.23       wiz usage(void)
    698   1.5       jtc {
    699  1.30  christos 	(void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname());
    700   1.1       cgd }
    701