printf.c revision 216447
1216417Sdelphij/*-
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
30216310Sjilles#ifndef SHELL
311590Srgrimes#ifndef lint
3220409Sstevestatic char const copyright[] =
331590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
341590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351590Srgrimes#endif /* not lint */
361590Srgrimes#endif
371590Srgrimes
381590Srgrimes#ifndef lint
3965429Simp#if 0
4020409Sstevestatic char const sccsid[] = "@(#)printf.c	8.1 (Berkeley) 7/20/93";
4165429Simp#endif
4259435Scracauerstatic const char rcsid[] =
4359435Scracauer  "$FreeBSD: head/usr.bin/printf/printf.c 216447 2010-12-14 20:35:08Z delphij $";
441590Srgrimes#endif /* not lint */
451590Srgrimes
461590Srgrimes#include <sys/types.h>
471590Srgrimes
481590Srgrimes#include <err.h>
491590Srgrimes#include <errno.h>
50148721Sstefanf#include <inttypes.h>
511590Srgrimes#include <limits.h>
52216417Sdelphij#include <locale.h>
531590Srgrimes#include <stdio.h>
541590Srgrimes#include <stdlib.h>
551590Srgrimes#include <string.h>
5627966Ssteve#include <unistd.h>
571590Srgrimes
581590Srgrimes#ifdef SHELL
591590Srgrimes#define main printfcmd
6012730Sjoerg#include "bltin/bltin.h"
6170256Sben#include "memalloc.h"
62215520Sjilles#include "error.h"
6341582Sbde#else
6441582Sbde#define	warnx1(a, b, c)		warnx(a)
6541582Sbde#define	warnx2(a, b, c)		warnx(a, b)
6641582Sbde#define	warnx3(a, b, c)		warnx(a, b, c)
671590Srgrimes#endif
681590Srgrimes
6995409Stjr#define PF(f, func) do { \
7018613Speter	char *b = NULL; \
7198424Stjr	if (havewidth) \
7298424Stjr		if (haveprec) \
7318613Speter			(void)asprintf(&b, f, fieldwidth, precision, func); \
741590Srgrimes		else \
7518613Speter			(void)asprintf(&b, f, fieldwidth, func); \
7698424Stjr	else if (haveprec) \
7718613Speter		(void)asprintf(&b, f, precision, func); \
781590Srgrimes	else \
7918613Speter		(void)asprintf(&b, f, func); \
8018613Speter	if (b) { \
8118613Speter		(void)fputs(b, stdout); \
8218613Speter		free(b); \
8318613Speter	} \
8495409Stjr} while (0)
851590Srgrimes
8692921Simpstatic int	 asciicode(void);
87215520Sjillesstatic char	*printf_doformat(char *, int *);
88145078Sstefanfstatic int	 escape(char *, int, size_t *);
8992921Simpstatic int	 getchr(void);
90143906Sdasstatic int	 getfloating(long double *, int);
9192921Simpstatic int	 getint(int *);
92148721Sstefanfstatic int	 getnum(intmax_t *, uintmax_t *, int);
9395409Stjrstatic const char
9495409Stjr		*getstr(void);
95216418Sdelphijstatic char	*mknum(char *, char);
9692921Simpstatic void	 usage(void);
971590Srgrimes
981590Srgrimesstatic char **gargv;
991590Srgrimes
1001590Srgrimesint
101102944Sdwmalonemain(int argc, char *argv[])
1021590Srgrimes{
103145078Sstefanf	size_t len;
104216447Sdelphij	int ch, chopped, end, rval;
105145061Sstefanf	char *format, *fmt, *start;
1061590Srgrimes
107216310Sjilles#ifndef SHELL
108216424Sdelphij	(void) setlocale(LC_ALL, "");
10972304Sache#endif
110215520Sjilles#ifdef SHELL
111215520Sjilles	optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
112215520Sjilles#endif
113216447Sdelphij	while ((ch = getopt(argc, argv, "")) != -1)
114216447Sdelphij		switch (ch) {
115216447Sdelphij		case '?':
116216447Sdelphij		default:
117216447Sdelphij			usage();
118216447Sdelphij			return (1);
119216447Sdelphij		}
120216447Sdelphij	argc -= optind;
121216447Sdelphij	argv += optind;
1221590Srgrimes
1231590Srgrimes	if (argc < 1) {
1241590Srgrimes		usage();
125216439Sdelphij		return (1);
1261590Srgrimes	}
1271590Srgrimes
128215520Sjilles#ifdef SHELL
129215520Sjilles	INTOFF;
130215520Sjilles#endif
1311590Srgrimes	/*
1321590Srgrimes	 * Basic algorithm is to scan the format string for conversion
1331590Srgrimes	 * specifications -- once one is found, find out if the field
1341590Srgrimes	 * width or precision is a '*'; if it is, gather up value.  Note,
1351590Srgrimes	 * format strings are reused as necessary to use up the provided
1361590Srgrimes	 * arguments, arguments of zero/null string are provided to use
1371590Srgrimes	 * up the format string.
1381590Srgrimes	 */
139145078Sstefanf	fmt = format = *argv;
140145078Sstefanf	chopped = escape(fmt, 1, &len);		/* backslash interpretation */
141145061Sstefanf	rval = end = 0;
1421590Srgrimes	gargv = ++argv;
1431590Srgrimes	for (;;) {
144145061Sstefanf		start = fmt;
145145078Sstefanf		while (fmt < format + len) {
146145061Sstefanf			if (fmt[0] == '%') {
147145061Sstefanf				fwrite(start, 1, fmt - start, stdout);
148145061Sstefanf				if (fmt[1] == '%') {
149145061Sstefanf					/* %% prints a % */
150145061Sstefanf					putchar('%');
151145061Sstefanf					fmt += 2;
152145061Sstefanf				} else {
153215520Sjilles					fmt = printf_doformat(fmt, &rval);
154215520Sjilles					if (fmt == NULL) {
155215520Sjilles#ifdef SHELL
156215520Sjilles						INTON;
157215520Sjilles#endif
158145061Sstefanf						return (1);
159215520Sjilles					}
160145061Sstefanf					end = 0;
16195300Sjmallett				}
162145061Sstefanf				start = fmt;
163145061Sstefanf			} else
16498420Stjr				fmt++;
1651590Srgrimes		}
1661590Srgrimes
167145061Sstefanf		if (end == 1) {
168145061Sstefanf			warnx1("missing format character", NULL, NULL);
169215520Sjilles#ifdef SHELL
170215520Sjilles			INTON;
171215520Sjilles#endif
172145061Sstefanf			return (1);
173145061Sstefanf		}
174145061Sstefanf		fwrite(start, 1, fmt - start, stdout);
175215520Sjilles		if (chopped || !*gargv) {
176215520Sjilles#ifdef SHELL
177215520Sjilles			INTON;
178215520Sjilles#endif
179145061Sstefanf			return (rval);
180215520Sjilles		}
181145061Sstefanf		/* Restart at the beginning of the format string. */
182145061Sstefanf		fmt = format;
183145061Sstefanf		end = 1;
184145061Sstefanf	}
185145061Sstefanf	/* NOTREACHED */
186145061Sstefanf}
187145061Sstefanf
188145061Sstefanf
189145061Sstefanfstatic char *
190215520Sjillesprintf_doformat(char *start, int *rval)
191145061Sstefanf{
192145061Sstefanf	static const char skip1[] = "#'-+ 0";
193145061Sstefanf	static const char skip2[] = "0123456789";
194145061Sstefanf	char *fmt;
195145061Sstefanf	int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
196145061Sstefanf	char convch, nextch;
197145061Sstefanf
198145061Sstefanf	fmt = start + 1;
199145061Sstefanf	/* skip to field width */
200145061Sstefanf	fmt += strspn(fmt, skip1);
201145061Sstefanf	if (*fmt == '*') {
202145061Sstefanf		if (getint(&fieldwidth))
203145061Sstefanf			return (NULL);
204145061Sstefanf		havewidth = 1;
205145061Sstefanf		++fmt;
206145061Sstefanf	} else {
207145061Sstefanf		havewidth = 0;
208145061Sstefanf
209145061Sstefanf		/* skip to possible '.', get following precision */
210145061Sstefanf		fmt += strspn(fmt, skip2);
211145061Sstefanf	}
212145061Sstefanf	if (*fmt == '.') {
213145061Sstefanf		/* precision present? */
214145061Sstefanf		++fmt;
2151590Srgrimes		if (*fmt == '*') {
216145061Sstefanf			if (getint(&precision))
217145061Sstefanf				return (NULL);
218145061Sstefanf			haveprec = 1;
2198323Sjoerg			++fmt;
2208323Sjoerg		} else {
221145061Sstefanf			haveprec = 0;
2221590Srgrimes
223145061Sstefanf			/* skip to conversion char */
224144902Sstefanf			fmt += strspn(fmt, skip2);
2258323Sjoerg		}
226145061Sstefanf	} else
227145061Sstefanf		haveprec = 0;
228145061Sstefanf	if (!*fmt) {
229145061Sstefanf		warnx1("missing format character", NULL, NULL);
230145061Sstefanf		return (NULL);
231145061Sstefanf	}
2328323Sjoerg
233145061Sstefanf	/*
234145061Sstefanf	 * Look for a length modifier.  POSIX doesn't have these, so
235145061Sstefanf	 * we only support them for floating-point conversions, which
236145061Sstefanf	 * are extensions.  This is useful because the L modifier can
237145061Sstefanf	 * be used to gain extra range and precision, while omitting
238145061Sstefanf	 * it is more likely to produce consistent results on different
239145061Sstefanf	 * architectures.  This is not so important for integers
240145061Sstefanf	 * because overflow is the only bad thing that can happen to
241145061Sstefanf	 * them, but consider the command  printf %a 1.1
242145061Sstefanf	 */
243145061Sstefanf	if (*fmt == 'L') {
244145061Sstefanf		mod_ldbl = 1;
245145061Sstefanf		fmt++;
246145061Sstefanf		if (!strchr("aAeEfFgG", *fmt)) {
247145061Sstefanf			warnx2("bad modifier L for %%%c", *fmt, NULL);
248145061Sstefanf			return (NULL);
2491590Srgrimes		}
250145061Sstefanf	} else {
251145061Sstefanf		mod_ldbl = 0;
252145061Sstefanf	}
2531590Srgrimes
254145061Sstefanf	convch = *fmt;
255145061Sstefanf	nextch = *++fmt;
256145061Sstefanf	*fmt = '\0';
257145061Sstefanf	switch (convch) {
258145061Sstefanf	case 'b': {
259145078Sstefanf		size_t len;
260145061Sstefanf		char *p;
261145061Sstefanf		int getout;
262143906Sdas
26395409Stjr#ifdef SHELL
264145061Sstefanf		p = savestr(getstr());
26595409Stjr#else
266145061Sstefanf		p = strdup(getstr());
26795409Stjr#endif
268145061Sstefanf		if (p == NULL) {
269145061Sstefanf			warnx2("%s", strerror(ENOMEM), NULL);
270145061Sstefanf			return (NULL);
271145061Sstefanf		}
272145078Sstefanf		getout = escape(p, 0, &len);
273145061Sstefanf		*(fmt - 1) = 's';
274145061Sstefanf		PF(start, p);
275145061Sstefanf		*(fmt - 1) = 'b';
27695409Stjr#ifdef SHELL
277145061Sstefanf		ckfree(p);
27895409Stjr#else
279145061Sstefanf		free(p);
28095409Stjr#endif
281145061Sstefanf		if (getout)
282145061Sstefanf			return (fmt);
283145061Sstefanf		break;
284145061Sstefanf	}
285145061Sstefanf	case 'c': {
286145061Sstefanf		char p;
2871590Srgrimes
288145061Sstefanf		p = getchr();
289145061Sstefanf		PF(start, p);
290145061Sstefanf		break;
291145061Sstefanf	}
292145061Sstefanf	case 's': {
293145061Sstefanf		const char *p;
2941590Srgrimes
295145061Sstefanf		p = getstr();
296145061Sstefanf		PF(start, p);
297145061Sstefanf		break;
298145061Sstefanf	}
299145061Sstefanf	case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
300145061Sstefanf		char *f;
301148721Sstefanf		intmax_t val;
302148721Sstefanf		uintmax_t uval;
303145061Sstefanf		int signedconv;
3048874Srgrimes
305145061Sstefanf		signedconv = (convch == 'd' || convch == 'i');
306148721Sstefanf		if ((f = mknum(start, convch)) == NULL)
307145061Sstefanf			return (NULL);
308148721Sstefanf		if (getnum(&val, &uval, signedconv))
309145061Sstefanf			*rval = 1;
310145061Sstefanf		if (signedconv)
311145061Sstefanf			PF(f, val);
312145061Sstefanf		else
313145061Sstefanf			PF(f, uval);
314145061Sstefanf		break;
315145061Sstefanf	}
316145061Sstefanf	case 'e': case 'E':
317145061Sstefanf	case 'f': case 'F':
318145061Sstefanf	case 'g': case 'G':
319145061Sstefanf	case 'a': case 'A': {
320145061Sstefanf		long double p;
3211590Srgrimes
322145061Sstefanf		if (getfloating(&p, mod_ldbl))
323145061Sstefanf			*rval = 1;
324145061Sstefanf		if (mod_ldbl)
325145061Sstefanf			PF(start, p);
326145061Sstefanf		else
327145061Sstefanf			PF(start, (double)p);
328145061Sstefanf		break;
3291590Srgrimes	}
330145061Sstefanf	default:
331145061Sstefanf		warnx2("illegal format character %c", convch, NULL);
332145061Sstefanf		return (NULL);
333145061Sstefanf	}
334145061Sstefanf	*fmt = nextch;
335145061Sstefanf	return (fmt);
3361590Srgrimes}
3371590Srgrimes
3381590Srgrimesstatic char *
339216418Sdelphijmknum(char *str, char ch)
3401590Srgrimes{
34170256Sben	static char *copy;
34270256Sben	static size_t copy_size;
34395409Stjr	char *newcopy;
34470256Sben	size_t len, newlen;
3451590Srgrimes
3461590Srgrimes	len = strlen(str) + 2;
34770256Sben	if (len > copy_size) {
34870256Sben		newlen = ((len + 1023) >> 10) << 10;
34970256Sben#ifdef SHELL
35070256Sben		if ((newcopy = ckrealloc(copy, newlen)) == NULL)
35170256Sben#else
35270256Sben		if ((newcopy = realloc(copy, newlen)) == NULL)
35370256Sben#endif
35495409Stjr		{
35595409Stjr			warnx2("%s", strerror(ENOMEM), NULL);
35670256Sben			return (NULL);
35795409Stjr		}
35870256Sben		copy = newcopy;
35970256Sben		copy_size = newlen;
36070256Sben	}
36162928Sse
3621590Srgrimes	memmove(copy, str, len - 3);
363148721Sstefanf	copy[len - 3] = 'j';
3641590Srgrimes	copy[len - 2] = ch;
3651590Srgrimes	copy[len - 1] = '\0';
3661590Srgrimes	return (copy);
3671590Srgrimes}
3681590Srgrimes
36995300Sjmallettstatic int
370145078Sstefanfescape(char *fmt, int percent, size_t *len)
3711590Srgrimes{
372145078Sstefanf	char *save, *store;
373102944Sdwmalone	int value, c;
3741590Srgrimes
375145078Sstefanf	for (save = store = fmt; (c = *fmt); ++fmt, ++store) {
3761590Srgrimes		if (c != '\\') {
3771590Srgrimes			*store = c;
3781590Srgrimes			continue;
3791590Srgrimes		}
3801590Srgrimes		switch (*++fmt) {
3811590Srgrimes		case '\0':		/* EOS, user error */
3821590Srgrimes			*store = '\\';
3831590Srgrimes			*++store = '\0';
384145078Sstefanf			*len = store - save;
38595300Sjmallett			return (0);
3861590Srgrimes		case '\\':		/* backslash */
3871590Srgrimes		case '\'':		/* single quote */
3881590Srgrimes			*store = *fmt;
3891590Srgrimes			break;
3901590Srgrimes		case 'a':		/* bell/alert */
391145074Sstefanf			*store = '\a';
3921590Srgrimes			break;
3931590Srgrimes		case 'b':		/* backspace */
3941590Srgrimes			*store = '\b';
3951590Srgrimes			break;
39695300Sjmallett		case 'c':
39795300Sjmallett			*store = '\0';
398145078Sstefanf			*len = store - save;
39995300Sjmallett			return (1);
4001590Srgrimes		case 'f':		/* form-feed */
4011590Srgrimes			*store = '\f';
4021590Srgrimes			break;
4031590Srgrimes		case 'n':		/* newline */
4041590Srgrimes			*store = '\n';
4051590Srgrimes			break;
4061590Srgrimes		case 'r':		/* carriage-return */
4071590Srgrimes			*store = '\r';
4081590Srgrimes			break;
4091590Srgrimes		case 't':		/* horizontal tab */
4101590Srgrimes			*store = '\t';
4111590Srgrimes			break;
4121590Srgrimes		case 'v':		/* vertical tab */
413145074Sstefanf			*store = '\v';
4141590Srgrimes			break;
4151590Srgrimes					/* octal constant */
4161590Srgrimes		case '0': case '1': case '2': case '3':
4171590Srgrimes		case '4': case '5': case '6': case '7':
418181153Sdas			c = (!percent && *fmt == '0') ? 4 : 3;
419181153Sdas			for (value = 0;
4201590Srgrimes			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
4211590Srgrimes				value <<= 3;
4221590Srgrimes				value += *fmt - '0';
4231590Srgrimes			}
4241590Srgrimes			--fmt;
42598426Stjr			if (percent && value == '%') {
42698419Stjr				*store++ = '%';
42798419Stjr				*store = '%';
42898419Stjr			} else
42998419Stjr				*store = value;
4301590Srgrimes			break;
4311590Srgrimes		default:
4321590Srgrimes			*store = *fmt;
4331590Srgrimes			break;
4341590Srgrimes		}
4351590Srgrimes	}
4361590Srgrimes	*store = '\0';
437145078Sstefanf	*len = store - save;
43895300Sjmallett	return (0);
4391590Srgrimes}
4401590Srgrimes
4411590Srgrimesstatic int
442102944Sdwmalonegetchr(void)
4431590Srgrimes{
4441590Srgrimes	if (!*gargv)
4451590Srgrimes		return ('\0');
4461590Srgrimes	return ((int)**gargv++);
4471590Srgrimes}
4481590Srgrimes
44987298Sdwmalonestatic const char *
450102944Sdwmalonegetstr(void)
4511590Srgrimes{
4521590Srgrimes	if (!*gargv)
4531590Srgrimes		return ("");
4541590Srgrimes	return (*gargv++);
4551590Srgrimes}
4561590Srgrimes
4571590Srgrimesstatic int
458102944Sdwmalonegetint(int *ip)
4591590Srgrimes{
460148721Sstefanf	intmax_t val;
461148721Sstefanf	uintmax_t uval;
46295409Stjr	int rval;
4631590Srgrimes
464148721Sstefanf	if (getnum(&val, &uval, 1))
4651590Srgrimes		return (1);
46695409Stjr	rval = 0;
46795409Stjr	if (val < INT_MIN || val > INT_MAX) {
46841582Sbde		warnx3("%s: %s", *gargv, strerror(ERANGE));
46995409Stjr		rval = 1;
47095409Stjr	}
47162928Sse	*ip = (int)val;
47295409Stjr	return (rval);
4731590Srgrimes}
4741590Srgrimes
4751590Srgrimesstatic int
476148721Sstefanfgetnum(intmax_t *ip, uintmax_t *uip, int signedconv)
4771590Srgrimes{
4781590Srgrimes	char *ep;
47995409Stjr	int rval;
4801590Srgrimes
4811590Srgrimes	if (!*gargv) {
482148721Sstefanf		*ip = 0;
4831590Srgrimes		return (0);
4841590Srgrimes	}
48595300Sjmallett	if (**gargv == '"' || **gargv == '\'') {
48695409Stjr		if (signedconv)
487148721Sstefanf			*ip = asciicode();
48895409Stjr		else
489148721Sstefanf			*uip = asciicode();
4901590Srgrimes		return (0);
4911590Srgrimes	}
49295409Stjr	rval = 0;
49395300Sjmallett	errno = 0;
49495409Stjr	if (signedconv)
495148721Sstefanf		*ip = strtoimax(*gargv, &ep, 0);
49695409Stjr	else
497148721Sstefanf		*uip = strtoumax(*gargv, &ep, 0);
49895409Stjr	if (ep == *gargv) {
49995300Sjmallett		warnx2("%s: expected numeric value", *gargv, NULL);
50095409Stjr		rval = 1;
50195409Stjr	}
50295409Stjr	else if (*ep != '\0') {
50395300Sjmallett		warnx2("%s: not completely converted", *gargv, NULL);
50495409Stjr		rval = 1;
50595409Stjr	}
50695409Stjr	if (errno == ERANGE) {
50795300Sjmallett		warnx3("%s: %s", *gargv, strerror(ERANGE));
50895409Stjr		rval = 1;
50995409Stjr	}
51095300Sjmallett	++gargv;
51195409Stjr	return (rval);
5121590Srgrimes}
5131590Srgrimes
51495409Stjrstatic int
515143906Sdasgetfloating(long double *dp, int mod_ldbl)
5161590Srgrimes{
51795300Sjmallett	char *ep;
51895409Stjr	int rval;
51995300Sjmallett
520145027Sstefanf	if (!*gargv) {
521145027Sstefanf		*dp = 0.0;
52295409Stjr		return (0);
523145027Sstefanf	}
52495300Sjmallett	if (**gargv == '"' || **gargv == '\'') {
52595409Stjr		*dp = asciicode();
52695409Stjr		return (0);
52795300Sjmallett	}
528126729Scperciva	rval = 0;
52995300Sjmallett	errno = 0;
530143906Sdas	if (mod_ldbl)
531143906Sdas		*dp = strtold(*gargv, &ep);
532143906Sdas	else
533143906Sdas		*dp = strtod(*gargv, &ep);
53495409Stjr	if (ep == *gargv) {
53595300Sjmallett		warnx2("%s: expected numeric value", *gargv, NULL);
53695409Stjr		rval = 1;
53795409Stjr	} else if (*ep != '\0') {
53895300Sjmallett		warnx2("%s: not completely converted", *gargv, NULL);
53995409Stjr		rval = 1;
54095409Stjr	}
54195409Stjr	if (errno == ERANGE) {
54295300Sjmallett		warnx3("%s: %s", *gargv, strerror(ERANGE));
54395409Stjr		rval = 1;
54495409Stjr	}
54595300Sjmallett	++gargv;
54695409Stjr	return (rval);
5471590Srgrimes}
5481590Srgrimes
5491590Srgrimesstatic int
550102944Sdwmaloneasciicode(void)
5511590Srgrimes{
552102944Sdwmalone	int ch;
5531590Srgrimes
5541590Srgrimes	ch = **gargv;
5551590Srgrimes	if (ch == '\'' || ch == '"')
5561590Srgrimes		ch = (*gargv)[1];
5571590Srgrimes	++gargv;
5581590Srgrimes	return (ch);
5591590Srgrimes}
5601590Srgrimes
5611590Srgrimesstatic void
562102944Sdwmaloneusage(void)
5631590Srgrimes{
564146466Sru	(void)fprintf(stderr, "usage: printf format [arguments ...]\n");
5651590Srgrimes}
566