Home | History | Annotate | Line # | Download | only in printf
printf.c revision 1.25
      1  1.25  christos /*	$NetBSD: printf.c,v 1.25 2002/11/24 22:35:45 christos 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.1       cgd  * 3. All advertising materials mentioning features or use of this software
     16   1.1       cgd  *    must display the following acknowledgement:
     17   1.1       cgd  *	This product includes software developed by the University of
     18   1.1       cgd  *	California, Berkeley and its contributors.
     19   1.1       cgd  * 4. Neither the name of the University nor the names of its contributors
     20   1.1       cgd  *    may be used to endorse or promote products derived from this software
     21   1.1       cgd  *    without specific prior written permission.
     22   1.1       cgd  *
     23   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33   1.1       cgd  * SUCH DAMAGE.
     34   1.1       cgd  */
     35   1.1       cgd 
     36  1.16  christos #include <sys/cdefs.h>
     37   1.1       cgd #ifndef lint
     38  1.17       mrg #if !defined(BUILTIN) && !defined(SHELL)
     39  1.17       mrg __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
     40  1.17       mrg 	The Regents of the University of California.  All rights reserved.\n");
     41  1.17       mrg #endif
     42   1.5       jtc #endif
     43   1.1       cgd 
     44   1.1       cgd #ifndef lint
     45  1.16  christos #if 0
     46  1.17       mrg static char sccsid[] = "@(#)printf.c	8.2 (Berkeley) 3/22/95";
     47  1.16  christos #else
     48  1.25  christos __RCSID("$NetBSD: printf.c,v 1.25 2002/11/24 22:35:45 christos Exp $");
     49  1.16  christos #endif
     50   1.1       cgd #endif /* not lint */
     51   1.1       cgd 
     52  1.17       mrg #include <sys/types.h>
     53  1.17       mrg 
     54   1.4       jtc #include <ctype.h>
     55  1.19     perry #include <err.h>
     56  1.19     perry #include <errno.h>
     57  1.22    kleink #include <inttypes.h>
     58  1.19     perry #include <limits.h>
     59  1.19     perry #include <locale.h>
     60  1.23       wiz #include <stdarg.h>
     61   1.1       cgd #include <stdio.h>
     62   1.4       jtc #include <stdlib.h>
     63   1.2   mycroft #include <string.h>
     64  1.19     perry #include <unistd.h>
     65   1.4       jtc 
     66  1.25  christos #ifdef __GNUC__
     67  1.25  christos #define ESCAPE '\e'
     68  1.25  christos #else
     69  1.25  christos #define ESCAPE 033
     70  1.25  christos #endif
     71   1.5       jtc 
     72  1.25  christos static char	*conv_escape_str(char *);
     73  1.25  christos static char	*conv_escape(char *, char *);
     74  1.25  christos static char	*conv_expand(const char *);
     75  1.23       wiz static int	 getchr(void);
     76  1.23       wiz static double	 getdouble(void);
     77  1.25  christos static int	 getwidth(void);
     78  1.23       wiz static intmax_t	 getintmax(void);
     79  1.25  christos static uintmax_t getuintmax(void);
     80  1.23       wiz static char	*getstr(void);
     81  1.25  christos static char	*mklong(const char *, int);
     82  1.23       wiz static void      check_conversion(const char *, const char *);
     83  1.23       wiz static void	 usage(void);
     84  1.25  christos 
     85   1.5       jtc static int	rval;
     86   1.5       jtc static char  **gargv;
     87   1.4       jtc 
     88  1.25  christos #ifdef BUILTIN		/* csh builtin */
     89  1.25  christos #define main progprintf
     90  1.16  christos #endif
     91  1.16  christos 
     92  1.25  christos #ifdef SHELL		/* sh (aka ash) builtin */
     93   1.5       jtc #define main printfcmd
     94   1.5       jtc #include "../../bin/sh/bltin/bltin.h"
     95   1.5       jtc #endif /* SHELL */
     96   1.5       jtc 
     97   1.1       cgd #define PF(f, func) { \
     98  1.25  christos 	if (fieldwidth != -1) { \
     99  1.25  christos 		if (precision != -1) \
    100   1.1       cgd 			(void)printf(f, fieldwidth, precision, func); \
    101   1.1       cgd 		else \
    102   1.1       cgd 			(void)printf(f, fieldwidth, func); \
    103  1.25  christos 	} else if (precision != -1) \
    104   1.1       cgd 		(void)printf(f, precision, func); \
    105   1.1       cgd 	else \
    106   1.1       cgd 		(void)printf(f, func); \
    107   1.1       cgd }
    108   1.1       cgd 
    109  1.25  christos int main(int, char **);
    110  1.25  christos int main(int argc, char *argv[])
    111   1.1       cgd {
    112  1.18     lukem 	char *fmt, *start;
    113  1.18     lukem 	int fieldwidth, precision;
    114  1.25  christos 	char nextch;
    115   1.4       jtc 	char *format;
    116   1.5       jtc 	int ch;
    117   1.4       jtc 
    118   1.5       jtc #if !defined(SHELL) && !defined(BUILTIN)
    119  1.15       cgd 	(void)setlocale (LC_ALL, "");
    120   1.5       jtc #endif
    121   1.1       cgd 
    122   1.5       jtc 	while ((ch = getopt(argc, argv, "")) != -1) {
    123   1.5       jtc 		switch (ch) {
    124   1.5       jtc 		case '?':
    125   1.5       jtc 		default:
    126   1.5       jtc 			usage();
    127   1.5       jtc 			return (1);
    128   1.5       jtc 		}
    129   1.1       cgd 	}
    130   1.5       jtc 	argc -= optind;
    131   1.5       jtc 	argv += optind;
    132   1.1       cgd 
    133   1.5       jtc 	if (argc < 1) {
    134   1.5       jtc 		usage();
    135   1.5       jtc 		return (1);
    136   1.5       jtc 	}
    137   1.5       jtc 
    138   1.5       jtc 	format = *argv;
    139   1.5       jtc 	gargv = ++argv;
    140   1.4       jtc 
    141   1.7       jtc #define SKIP1	"#-+ 0"
    142   1.7       jtc #define SKIP2	"*0123456789"
    143   1.4       jtc 	do {
    144   1.6       jtc 		/*
    145   1.6       jtc 		 * Basic algorithm is to scan the format string for conversion
    146   1.6       jtc 		 * specifications -- once one is found, find out if the field
    147   1.6       jtc 		 * width or precision is a '*'; if it is, gather up value.
    148   1.6       jtc 		 * Note, format strings are reused as necessary to use up the
    149   1.6       jtc 		 * provided arguments, arguments of zero/null string are
    150   1.6       jtc 		 * provided to use up the format string.
    151   1.6       jtc 		 */
    152   1.6       jtc 
    153   1.6       jtc 		/* find next format specification */
    154  1.25  christos 		for (fmt = format; (ch = *fmt++) ;) {
    155  1.25  christos 			if (ch == '\\') {
    156  1.25  christos 				char c_ch;
    157  1.25  christos 				fmt = conv_escape(fmt, &c_ch);
    158  1.25  christos 				putchar(c_ch);
    159  1.25  christos 				continue;
    160  1.25  christos 			}
    161  1.25  christos 			if (ch != '%' || (*fmt == '%' && ++fmt)) {
    162  1.25  christos 				(void)putchar(ch);
    163  1.25  christos 				continue;
    164  1.25  christos 			}
    165  1.25  christos 
    166  1.25  christos 			/* Ok - we've found a format specification,
    167  1.25  christos 			   Save its address for a later printf(). */
    168  1.25  christos 			start = fmt - 1;
    169  1.25  christos 
    170  1.25  christos 			/* skip to field width */
    171  1.25  christos 			fmt += strspn(fmt, SKIP1);
    172  1.25  christos 			fieldwidth = *fmt == '*' ? getwidth() : -1;
    173  1.25  christos 
    174  1.25  christos 			/* skip to possible '.', get following precision */
    175  1.25  christos 			fmt += strspn(fmt, SKIP2);
    176  1.25  christos 			if (*fmt == '.')
    177  1.25  christos 				++fmt;
    178  1.25  christos 			precision = *fmt == '*' ? getwidth() : -1;
    179  1.25  christos 
    180  1.25  christos 			fmt += strspn(fmt, SKIP2);
    181  1.25  christos 
    182  1.25  christos 			ch = *fmt;
    183  1.25  christos 			if (!ch) {
    184  1.25  christos 				warnx("missing format character");
    185  1.25  christos 				return (1);
    186  1.25  christos 			}
    187  1.25  christos 			/* null terminate format string to we can use it
    188  1.25  christos 			   as an argument to printf. */
    189  1.25  christos 			nextch = fmt[1];
    190  1.25  christos 			fmt[1] = 0;
    191  1.25  christos 			switch (ch) {
    192  1.25  christos 
    193  1.25  christos 			case 'B': {
    194  1.25  christos 				const char *p = conv_expand(getstr());
    195  1.25  christos 				*fmt = 's';
    196  1.25  christos 				PF(start, p);
    197   1.6       jtc 				break;
    198  1.25  christos 			}
    199  1.25  christos 			case 'b': {
    200  1.25  christos 				char *p = conv_escape_str(getstr());
    201  1.25  christos 				*fmt = 's';
    202  1.25  christos 				PF(start, p);
    203  1.25  christos 				break;
    204  1.25  christos 			}
    205  1.25  christos 			case 'c': {
    206  1.25  christos 				char p = getchr();
    207  1.25  christos 				PF(start, p);
    208  1.25  christos 				break;
    209  1.25  christos 			}
    210  1.25  christos 			case 's': {
    211  1.25  christos 				char *p = getstr();
    212  1.25  christos 				PF(start, p);
    213  1.25  christos 				break;
    214  1.25  christos 			}
    215  1.25  christos 			case 'd':
    216  1.25  christos 			case 'i': {
    217  1.25  christos 				intmax_t p = getintmax();
    218  1.25  christos 				char *f = mklong(start, ch);
    219  1.25  christos 				PF(f, p);
    220  1.25  christos 				break;
    221  1.25  christos 			}
    222  1.25  christos 			case 'o':
    223  1.25  christos 			case 'u':
    224  1.25  christos 			case 'x':
    225  1.25  christos 			case 'X': {
    226  1.25  christos 				uintmax_t p = getuintmax();
    227  1.25  christos 				char *f = mklong(start, ch);
    228  1.25  christos 				PF(f, p);
    229  1.25  christos 				break;
    230  1.25  christos 			}
    231  1.25  christos 			case 'e':
    232  1.25  christos 			case 'E':
    233  1.25  christos 			case 'f':
    234  1.25  christos 			case 'g':
    235  1.25  christos 			case 'G': {
    236  1.25  christos 				double p = getdouble();
    237  1.25  christos 				PF(start, p);
    238   1.4       jtc 				break;
    239  1.25  christos 			}
    240   1.6       jtc 			default:
    241  1.25  christos 				warnx("%s: invalid directive", start);
    242  1.25  christos 				return (1);
    243   1.4       jtc 			}
    244  1.25  christos 			*fmt++ = ch;
    245  1.25  christos 			*fmt = nextch;
    246  1.25  christos 			/* escape if a \c was encountered */
    247  1.25  christos 			if (rval & 0x100)
    248  1.25  christos 				return (rval & ~0x100);
    249   1.6       jtc 		}
    250  1.25  christos 	} while (gargv != argv && *gargv);
    251   1.1       cgd 
    252   1.6       jtc 	return (rval);
    253   1.4       jtc }
    254   1.4       jtc 
    255   1.4       jtc 
    256   1.4       jtc /*
    257   1.4       jtc  * Print SysV echo(1) style escape string
    258  1.25  christos  *	Halts processing string if a \c escape is encountered.
    259   1.4       jtc  */
    260  1.25  christos static char *
    261  1.25  christos conv_escape_str(char *str)
    262   1.4       jtc {
    263   1.4       jtc 	int value;
    264  1.25  christos 	int ch;
    265  1.25  christos 	static char *conv_str;
    266  1.25  christos 	char *cp;
    267   1.4       jtc 
    268  1.25  christos 	/* convert string into a temporary buffer... */
    269  1.25  christos 	if (conv_str)
    270  1.25  christos 		free(conv_str);
    271  1.25  christos 	conv_str = malloc(strlen(str) + 4);
    272  1.25  christos 	if (!conv_str)
    273  1.25  christos 		return "<no memory>";
    274  1.25  christos 	cp = conv_str;
    275  1.25  christos 
    276  1.25  christos 	while ((ch = *str++)) {
    277  1.25  christos 		if (ch != '\\') {
    278  1.25  christos 			*cp++ = ch;
    279  1.25  christos 			continue;
    280  1.25  christos 		}
    281  1.25  christos 
    282  1.25  christos 		ch = *str++;
    283  1.25  christos 		if (ch == 'c') {
    284  1.25  christos 			/* \c as in SYSV echo - abort all processing.... */
    285  1.25  christos 			rval |= 0x100;
    286  1.25  christos 			break;
    287  1.25  christos 		}
    288  1.25  christos 
    289  1.25  christos 		/*
    290  1.25  christos 		 * %b string octal constants are not like those in C.
    291  1.25  christos 		 * They start with a \0, and are followed by 0, 1, 2,
    292  1.25  christos 		 * or 3 octal digits.
    293  1.25  christos 		 */
    294  1.25  christos 		if (ch == '0') {
    295  1.25  christos 			char octnum[4], *oct_end;
    296  1.25  christos 			octnum[0] = str[0];
    297  1.25  christos 			octnum[1] = str[1];
    298  1.25  christos 			octnum[2] = str[2];
    299  1.25  christos 			octnum[3] = 0;
    300  1.25  christos 			*cp++ = strtoul(octnum, &oct_end, 8);
    301  1.25  christos 			str += oct_end - octnum;
    302  1.25  christos 			continue;
    303  1.25  christos 		}
    304  1.25  christos 
    305  1.25  christos 		/* \[M][^|-]C as defined by vis(3) */
    306  1.25  christos 		if (ch == 'M' && *str == '-') {
    307  1.25  christos 			*cp++ = 0200 | str[1];
    308  1.25  christos 			str += 2;
    309  1.25  christos 			continue;
    310  1.25  christos 		}
    311  1.25  christos 		if (ch == 'M' && *str == '^') {
    312   1.4       jtc 			str++;
    313  1.25  christos 			value = 0200;
    314  1.25  christos 			ch = '^';
    315  1.25  christos 		} else
    316  1.25  christos 			value = 0;
    317  1.25  christos 		if (ch == '^') {
    318  1.25  christos 			ch = *str++;
    319  1.25  christos 			if (ch == '?')
    320  1.25  christos 				value |= 0177;
    321  1.25  christos 			else
    322  1.25  christos 				value |= ch & 037;
    323  1.25  christos 			*cp++ = value;
    324  1.25  christos 			continue;
    325   1.1       cgd 		}
    326  1.25  christos 
    327  1.25  christos 		/* Finally test for sequences valid in the format string */
    328  1.25  christos 		str = conv_escape(str - 1, cp);
    329  1.25  christos 		cp++;
    330   1.4       jtc 	}
    331  1.25  christos 	*cp = 0;
    332   1.7       jtc 
    333  1.25  christos 	return conv_str;
    334   1.4       jtc }
    335   1.4       jtc 
    336   1.4       jtc /*
    337   1.4       jtc  * Print "standard" escape characters
    338   1.4       jtc  */
    339  1.25  christos static char *
    340  1.25  christos conv_escape(char *str, char *conv_ch)
    341   1.4       jtc {
    342   1.9       jtc 	int value;
    343  1.25  christos 	int ch;
    344  1.25  christos 	char num_buf[4], *num_end;
    345   1.4       jtc 
    346  1.25  christos 	ch = *str++;
    347   1.4       jtc 
    348  1.25  christos 	switch (ch) {
    349   1.4       jtc 	case '0': case '1': case '2': case '3':
    350   1.4       jtc 	case '4': case '5': case '6': case '7':
    351  1.25  christos 		num_buf[0] = ch;
    352  1.25  christos 		ch = str[0];
    353  1.25  christos 		num_buf[1] = ch;
    354  1.25  christos 		num_buf[2] = ch ? str[1] : 0;
    355  1.25  christos 		num_buf[3] = 0;
    356  1.25  christos 		value = strtoul(num_buf, &num_end, 8);
    357  1.25  christos 		str += num_end  - (num_buf + 1);
    358  1.25  christos 		break;
    359   1.4       jtc 
    360   1.4       jtc 	case 'x':
    361  1.25  christos 		/* Hexadecimal character constants are not required to be
    362  1.25  christos 		   supported (by SuS v1) because there is no consistent
    363  1.25  christos 		   way to detect the end of the constant.
    364  1.25  christos 		   Supporting 2 byte constants is a compromise. */
    365  1.25  christos 		ch = str[0];
    366  1.25  christos 		num_buf[0] = ch;
    367  1.25  christos 		num_buf[1] = ch ? str[1] : 0;
    368  1.25  christos 		num_buf[2] = 0;
    369  1.25  christos 		value = strtoul(num_buf, &num_end, 16);
    370  1.25  christos 		str += num_end - num_buf;
    371  1.25  christos 		break;
    372  1.25  christos 
    373  1.25  christos 	case '\\':	value = '\\';	break;	/* backslash */
    374  1.25  christos 	case '\'':	value = '\'';	break;	/* single quote */
    375  1.25  christos 	case '"':	value = '"';	break;	/* double quote */
    376  1.25  christos 	case 'a':	value = '\a';	break;	/* alert */
    377  1.25  christos 	case 'b':	value = '\b';	break;	/* backspace */
    378  1.25  christos 	case 'e':	value = ESCAPE;	break;	/* escape */
    379  1.25  christos 	case 'f':	value = '\f';	break;	/* form-feed */
    380  1.25  christos 	case 'n':	value = '\n';	break;	/* newline */
    381  1.25  christos 	case 'r':	value = '\r';	break;	/* carriage-return */
    382  1.25  christos 	case 't':	value = '\t';	break;	/* tab */
    383  1.25  christos 	case 'v':	value = '\v';	break;	/* vertical-tab */
    384   1.4       jtc 
    385  1.25  christos 	default:
    386  1.25  christos 		warnx("unknown escape sequence `\\%c'", *str);
    387  1.25  christos 		rval = 1;
    388  1.25  christos 		value = *str;
    389   1.4       jtc 		break;
    390  1.25  christos 	}
    391   1.4       jtc 
    392  1.25  christos 	*conv_ch = value;
    393  1.25  christos 	return str;
    394  1.25  christos }
    395   1.4       jtc 
    396  1.25  christos /* expand a string so that everything is printable */
    397   1.4       jtc 
    398  1.25  christos static char *
    399  1.25  christos conv_expand(const char *str)
    400  1.25  christos {
    401  1.25  christos 	static char *conv_str;
    402  1.25  christos 	char *cp;
    403  1.25  christos 	int ch;
    404   1.4       jtc 
    405  1.25  christos 	if (conv_str)
    406  1.25  christos 		free(conv_str);
    407  1.25  christos 	/* get a buffer that is definitely large enough.... */
    408  1.25  christos 	conv_str = malloc(4 * strlen(str) + 1);
    409  1.25  christos 	if (!conv_str)
    410  1.25  christos 		return "<no memory>";
    411  1.25  christos 	cp = conv_str;
    412   1.4       jtc 
    413  1.25  christos 	while ((ch = *(unsigned char *)str++)) {
    414  1.25  christos 		switch (ch) {
    415  1.25  christos 		/* Use C escapes for expected control characters */
    416  1.25  christos 		case '\\':	ch = '\\';	break;	/* backslash */
    417  1.25  christos 		case '\'':	ch = '\'';	break;	/* single quote */
    418  1.25  christos 		case '"':	ch = '"';	break;	/* double quote */
    419  1.25  christos 		case '\a':	ch = 'a';	break;	/* alert */
    420  1.25  christos 		case '\b':	ch = 'b';	break;	/* backspace */
    421  1.25  christos 		case ESCAPE:	ch = 'e';	break;	/* escape */
    422  1.25  christos 		case '\f':	ch = 'f';	break;	/* form-feed */
    423  1.25  christos 		case '\n':	ch = 'n';	break;	/* newline */
    424  1.25  christos 		case '\r':	ch = 'r';	break;	/* carriage-return */
    425  1.25  christos 		case '\t':	ch = 't';	break;	/* tab */
    426  1.25  christos 		case '\v':	ch = 'v';	break;	/* vertical-tab */
    427  1.25  christos 		default:
    428  1.25  christos 			/* Copy anything printable */
    429  1.25  christos 			if (isprint(ch)) {
    430  1.25  christos 				*cp++ = ch;
    431  1.25  christos 				continue;
    432  1.25  christos 			}
    433  1.25  christos 			/* Use vis(3) encodings for the rest */
    434  1.25  christos 			*cp++ = '\\';
    435  1.25  christos 			if (ch & 0200) {
    436  1.25  christos 				*cp++ = 'M';
    437  1.25  christos 				ch &= ~0200;
    438  1.25  christos 			}
    439  1.25  christos 			if (ch == 0177) {
    440  1.25  christos 				*cp++ = '^';
    441  1.25  christos 				*cp++ = '?';
    442  1.25  christos 				continue;
    443  1.25  christos 			}
    444  1.25  christos 			if (ch < 040) {
    445  1.25  christos 				*cp++ = '^';
    446  1.25  christos 				*cp++ = ch | 0100;
    447  1.25  christos 				continue;
    448  1.25  christos 			}
    449  1.25  christos 			*cp++ = '-';
    450  1.25  christos 			*cp++ = ch;
    451  1.25  christos 			continue;
    452  1.25  christos 		}
    453  1.25  christos 		*cp++ = '\\';
    454  1.25  christos 		*cp++ = ch;
    455   1.1       cgd 	}
    456   1.4       jtc 
    457  1.25  christos 	*cp = 0;
    458  1.25  christos 	return conv_str;
    459   1.1       cgd }
    460   1.1       cgd 
    461   1.5       jtc static char *
    462  1.23       wiz mklong(const char *str, int ch)
    463   1.1       cgd {
    464   1.5       jtc 	static char copy[64];
    465  1.15       cgd 	size_t len;
    466   1.1       cgd 
    467   1.1       cgd 	len = strlen(str) + 2;
    468  1.25  christos 	if (len > sizeof copy) {
    469  1.25  christos 		warnx("format %s too complex\n", str);
    470  1.25  christos 		len = 4;
    471  1.25  christos 	}
    472  1.15       cgd 	(void)memmove(copy, str, len - 3);
    473  1.22    kleink 	copy[len - 3] = 'j';
    474   1.1       cgd 	copy[len - 2] = ch;
    475   1.1       cgd 	copy[len - 1] = '\0';
    476   1.5       jtc 	return (copy);
    477   1.1       cgd }
    478   1.1       cgd 
    479   1.5       jtc static int
    480  1.23       wiz getchr(void)
    481   1.1       cgd {
    482   1.1       cgd 	if (!*gargv)
    483  1.17       mrg 		return ('\0');
    484  1.17       mrg 	return ((int)**gargv++);
    485   1.1       cgd }
    486   1.1       cgd 
    487   1.5       jtc static char *
    488  1.23       wiz getstr(void)
    489   1.1       cgd {
    490   1.1       cgd 	if (!*gargv)
    491  1.17       mrg 		return ("");
    492  1.17       mrg 	return (*gargv++);
    493   1.1       cgd }
    494   1.1       cgd 
    495   1.5       jtc static int
    496  1.25  christos getwidth(void)
    497   1.1       cgd {
    498  1.25  christos 	long val;
    499  1.25  christos 	char *s, *ep;
    500  1.25  christos 
    501  1.25  christos 	s = *gargv;
    502   1.1       cgd 	if (!*gargv)
    503  1.25  christos 		return (0);
    504  1.25  christos 	gargv++;
    505   1.4       jtc 
    506  1.25  christos 	errno = 0;
    507  1.25  christos 	val = strtoul(s, &ep, 0);
    508  1.25  christos 	check_conversion(s, ep);
    509   1.4       jtc 
    510  1.25  christos 	/* Arbitrarily 'restrict' field widths to 1Mbyte */
    511  1.25  christos 	if (val < 0 || val > 1 << 20) {
    512  1.25  christos 		warnx("%s: invalid field width", s);
    513  1.25  christos 		return 0;
    514  1.25  christos 	}
    515  1.25  christos 
    516  1.25  christos 	return val;
    517   1.1       cgd }
    518   1.1       cgd 
    519  1.22    kleink static intmax_t
    520  1.23       wiz getintmax(void)
    521   1.1       cgd {
    522  1.22    kleink 	intmax_t val;
    523  1.25  christos 	char *cp, *ep;
    524   1.1       cgd 
    525  1.25  christos 	cp = *gargv;
    526  1.25  christos 	if (cp == NULL)
    527  1.25  christos 		return 0;
    528  1.25  christos 	gargv++;
    529   1.4       jtc 
    530  1.25  christos 	if (*cp == '\"' || *cp == '\'')
    531  1.25  christos 		return *(cp+1);
    532   1.4       jtc 
    533  1.11       jtc 	errno = 0;
    534  1.25  christos 	val = strtoimax(cp, &ep, 0);
    535  1.25  christos 	check_conversion(cp, ep);
    536  1.11       jtc 	return val;
    537  1.11       jtc }
    538  1.11       jtc 
    539  1.22    kleink static uintmax_t
    540  1.23       wiz getuintmax(void)
    541  1.11       jtc {
    542  1.22    kleink 	uintmax_t val;
    543  1.25  christos 	char *cp, *ep;
    544  1.11       jtc 
    545  1.25  christos 	cp = *gargv;
    546  1.25  christos 	if (cp == NULL)
    547  1.25  christos 		return 0;
    548  1.25  christos 	gargv++;
    549  1.25  christos 
    550  1.25  christos 	if (*cp == '\"' || *cp == '\'')
    551  1.25  christos 		return *(cp+1);
    552  1.25  christos 
    553  1.25  christos 	/* strtoumax won't error -ve values */
    554  1.25  christos 	while (isspace(*(unsigned char *)cp))
    555  1.25  christos 		cp++;
    556  1.25  christos 	if (*cp == '-') {
    557  1.25  christos 		warnx("%s: expected positive numeric value", cp);
    558  1.25  christos 		rval = 1;
    559  1.25  christos 		return 0;
    560  1.25  christos 	}
    561  1.11       jtc 
    562  1.11       jtc 	errno = 0;
    563  1.25  christos 	val = strtoumax(cp, &ep, 0);
    564  1.25  christos 	check_conversion(cp, ep);
    565   1.4       jtc 	return val;
    566   1.1       cgd }
    567   1.1       cgd 
    568   1.5       jtc static double
    569  1.23       wiz getdouble(void)
    570   1.1       cgd {
    571   1.4       jtc 	double val;
    572   1.4       jtc 	char *ep;
    573   1.1       cgd 
    574   1.1       cgd 	if (!*gargv)
    575  1.25  christos 		return (0.0);
    576   1.4       jtc 
    577  1.13       jtc 	if (**gargv == '\"' || **gargv == '\'')
    578  1.13       jtc 		return (double) *((*gargv++)+1);
    579   1.1       cgd 
    580  1.11       jtc 	errno = 0;
    581  1.25  christos 	val = strtod(*gargv, &ep);
    582  1.12       jtc 	check_conversion(*gargv++, ep);
    583  1.12       jtc 	return val;
    584  1.12       jtc }
    585  1.12       jtc 
    586  1.12       jtc static void
    587  1.23       wiz check_conversion(const char *s, const char *ep)
    588  1.12       jtc {
    589   1.4       jtc 	if (*ep) {
    590  1.12       jtc 		if (ep == s)
    591  1.25  christos 			warnx("%s: expected numeric value", s);
    592  1.11       jtc 		else
    593  1.25  christos 			warnx("%s: not completely converted", s);
    594  1.11       jtc 		rval = 1;
    595  1.11       jtc 	} else if (errno == ERANGE) {
    596  1.25  christos 		warnx("%s: %s", s, strerror(ERANGE));
    597   1.4       jtc 		rval = 1;
    598   1.4       jtc 	}
    599   1.5       jtc }
    600   1.5       jtc 
    601   1.5       jtc static void
    602  1.23       wiz usage(void)
    603   1.5       jtc {
    604   1.5       jtc 	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
    605   1.1       cgd }
    606