printf.c revision 216310
11590Srgrimes/*
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
34216310Sjilles#ifndef SHELL
351590Srgrimes#ifndef lint
3620409Sstevestatic char const copyright[] =
371590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
381590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
391590Srgrimes#endif /* not lint */
401590Srgrimes#endif
411590Srgrimes
421590Srgrimes#ifndef lint
4365429Simp#if 0
4420409Sstevestatic char const sccsid[] = "@(#)printf.c	8.1 (Berkeley) 7/20/93";
4565429Simp#endif
4659435Scracauerstatic const char rcsid[] =
4759435Scracauer  "$FreeBSD: head/usr.bin/printf/printf.c 216310 2010-12-08 22:13:27Z jilles $";
481590Srgrimes#endif /* not lint */
491590Srgrimes
501590Srgrimes#include <sys/types.h>
511590Srgrimes
521590Srgrimes#include <err.h>
531590Srgrimes#include <errno.h>
54148721Sstefanf#include <inttypes.h>
551590Srgrimes#include <limits.h>
561590Srgrimes#include <stdio.h>
571590Srgrimes#include <stdlib.h>
581590Srgrimes#include <string.h>
5927966Ssteve#include <unistd.h>
601590Srgrimes
611590Srgrimes#ifdef SHELL
621590Srgrimes#define main printfcmd
6312730Sjoerg#include "bltin/bltin.h"
6470256Sben#include "memalloc.h"
65215520Sjilles#include "error.h"
6641582Sbde#else
6741582Sbde#define	warnx1(a, b, c)		warnx(a)
6841582Sbde#define	warnx2(a, b, c)		warnx(a, b)
6941582Sbde#define	warnx3(a, b, c)		warnx(a, b, c)
701590Srgrimes#endif
711590Srgrimes
7272304Sache#include <locale.h>
7372304Sache
7495409Stjr#define PF(f, func) do { \
7518613Speter	char *b = NULL; \
7698424Stjr	if (havewidth) \
7798424Stjr		if (haveprec) \
7818613Speter			(void)asprintf(&b, f, fieldwidth, precision, func); \
791590Srgrimes		else \
8018613Speter			(void)asprintf(&b, f, fieldwidth, func); \
8198424Stjr	else if (haveprec) \
8218613Speter		(void)asprintf(&b, f, precision, func); \
831590Srgrimes	else \
8418613Speter		(void)asprintf(&b, f, func); \
8518613Speter	if (b) { \
8618613Speter		(void)fputs(b, stdout); \
8718613Speter		free(b); \
8818613Speter	} \
8995409Stjr} while (0)
901590Srgrimes
9192921Simpstatic int	 asciicode(void);
92215520Sjillesstatic char	*printf_doformat(char *, int *);
93145078Sstefanfstatic int	 escape(char *, int, size_t *);
9492921Simpstatic int	 getchr(void);
95143906Sdasstatic int	 getfloating(long double *, int);
9692921Simpstatic int	 getint(int *);
97148721Sstefanfstatic int	 getnum(intmax_t *, uintmax_t *, int);
9895409Stjrstatic const char
9995409Stjr		*getstr(void);
100148721Sstefanfstatic char	*mknum(char *, int);
10192921Simpstatic void	 usage(void);
1021590Srgrimes
1031590Srgrimesstatic char **gargv;
1041590Srgrimes
1051590Srgrimesint
106102944Sdwmalonemain(int argc, char *argv[])
1071590Srgrimes{
108145078Sstefanf	size_t len;
109145061Sstefanf	int ch, chopped, end, rval;
110145061Sstefanf	char *format, *fmt, *start;
1111590Srgrimes
112216310Sjilles#ifndef SHELL
11372304Sache	(void) setlocale(LC_NUMERIC, "");
11472304Sache#endif
115215520Sjilles#ifdef SHELL
116215520Sjilles	optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
117215520Sjilles#endif
11824360Simp	while ((ch = getopt(argc, argv, "")) != -1)
1191590Srgrimes		switch (ch) {
1201590Srgrimes		case '?':
1211590Srgrimes		default:
1221590Srgrimes			usage();
1231590Srgrimes			return (1);
1241590Srgrimes		}
1251590Srgrimes	argc -= optind;
1261590Srgrimes	argv += optind;
1271590Srgrimes
1281590Srgrimes	if (argc < 1) {
1291590Srgrimes		usage();
1301590Srgrimes		return (1);
1311590Srgrimes	}
1321590Srgrimes
133215520Sjilles#ifdef SHELL
134215520Sjilles	INTOFF;
135215520Sjilles#endif
1361590Srgrimes	/*
1371590Srgrimes	 * Basic algorithm is to scan the format string for conversion
1381590Srgrimes	 * specifications -- once one is found, find out if the field
1391590Srgrimes	 * width or precision is a '*'; if it is, gather up value.  Note,
1401590Srgrimes	 * format strings are reused as necessary to use up the provided
1411590Srgrimes	 * arguments, arguments of zero/null string are provided to use
1421590Srgrimes	 * up the format string.
1431590Srgrimes	 */
144145078Sstefanf	fmt = format = *argv;
145145078Sstefanf	chopped = escape(fmt, 1, &len);		/* backslash interpretation */
146145061Sstefanf	rval = end = 0;
1471590Srgrimes	gargv = ++argv;
1481590Srgrimes	for (;;) {
149145061Sstefanf		start = fmt;
150145078Sstefanf		while (fmt < format + len) {
151145061Sstefanf			if (fmt[0] == '%') {
152145061Sstefanf				fwrite(start, 1, fmt - start, stdout);
153145061Sstefanf				if (fmt[1] == '%') {
154145061Sstefanf					/* %% prints a % */
155145061Sstefanf					putchar('%');
156145061Sstefanf					fmt += 2;
157145061Sstefanf				} else {
158215520Sjilles					fmt = printf_doformat(fmt, &rval);
159215520Sjilles					if (fmt == NULL) {
160215520Sjilles#ifdef SHELL
161215520Sjilles						INTON;
162215520Sjilles#endif
163145061Sstefanf						return (1);
164215520Sjilles					}
165145061Sstefanf					end = 0;
16695300Sjmallett				}
167145061Sstefanf				start = fmt;
168145061Sstefanf			} else
16998420Stjr				fmt++;
1701590Srgrimes		}
1711590Srgrimes
172145061Sstefanf		if (end == 1) {
173145061Sstefanf			warnx1("missing format character", NULL, NULL);
174215520Sjilles#ifdef SHELL
175215520Sjilles			INTON;
176215520Sjilles#endif
177145061Sstefanf			return (1);
178145061Sstefanf		}
179145061Sstefanf		fwrite(start, 1, fmt - start, stdout);
180215520Sjilles		if (chopped || !*gargv) {
181215520Sjilles#ifdef SHELL
182215520Sjilles			INTON;
183215520Sjilles#endif
184145061Sstefanf			return (rval);
185215520Sjilles		}
186145061Sstefanf		/* Restart at the beginning of the format string. */
187145061Sstefanf		fmt = format;
188145061Sstefanf		end = 1;
189145061Sstefanf	}
190145061Sstefanf	/* NOTREACHED */
191145061Sstefanf}
192145061Sstefanf
193145061Sstefanf
194145061Sstefanfstatic char *
195215520Sjillesprintf_doformat(char *start, int *rval)
196145061Sstefanf{
197145061Sstefanf	static const char skip1[] = "#'-+ 0";
198145061Sstefanf	static const char skip2[] = "0123456789";
199145061Sstefanf	char *fmt;
200145061Sstefanf	int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
201145061Sstefanf	char convch, nextch;
202145061Sstefanf
203145061Sstefanf	fmt = start + 1;
204145061Sstefanf	/* skip to field width */
205145061Sstefanf	fmt += strspn(fmt, skip1);
206145061Sstefanf	if (*fmt == '*') {
207145061Sstefanf		if (getint(&fieldwidth))
208145061Sstefanf			return (NULL);
209145061Sstefanf		havewidth = 1;
210145061Sstefanf		++fmt;
211145061Sstefanf	} else {
212145061Sstefanf		havewidth = 0;
213145061Sstefanf
214145061Sstefanf		/* skip to possible '.', get following precision */
215145061Sstefanf		fmt += strspn(fmt, skip2);
216145061Sstefanf	}
217145061Sstefanf	if (*fmt == '.') {
218145061Sstefanf		/* precision present? */
219145061Sstefanf		++fmt;
2201590Srgrimes		if (*fmt == '*') {
221145061Sstefanf			if (getint(&precision))
222145061Sstefanf				return (NULL);
223145061Sstefanf			haveprec = 1;
2248323Sjoerg			++fmt;
2258323Sjoerg		} else {
226145061Sstefanf			haveprec = 0;
2271590Srgrimes
228145061Sstefanf			/* skip to conversion char */
229144902Sstefanf			fmt += strspn(fmt, skip2);
2308323Sjoerg		}
231145061Sstefanf	} else
232145061Sstefanf		haveprec = 0;
233145061Sstefanf	if (!*fmt) {
234145061Sstefanf		warnx1("missing format character", NULL, NULL);
235145061Sstefanf		return (NULL);
236145061Sstefanf	}
2378323Sjoerg
238145061Sstefanf	/*
239145061Sstefanf	 * Look for a length modifier.  POSIX doesn't have these, so
240145061Sstefanf	 * we only support them for floating-point conversions, which
241145061Sstefanf	 * are extensions.  This is useful because the L modifier can
242145061Sstefanf	 * be used to gain extra range and precision, while omitting
243145061Sstefanf	 * it is more likely to produce consistent results on different
244145061Sstefanf	 * architectures.  This is not so important for integers
245145061Sstefanf	 * because overflow is the only bad thing that can happen to
246145061Sstefanf	 * them, but consider the command  printf %a 1.1
247145061Sstefanf	 */
248145061Sstefanf	if (*fmt == 'L') {
249145061Sstefanf		mod_ldbl = 1;
250145061Sstefanf		fmt++;
251145061Sstefanf		if (!strchr("aAeEfFgG", *fmt)) {
252145061Sstefanf			warnx2("bad modifier L for %%%c", *fmt, NULL);
253145061Sstefanf			return (NULL);
2541590Srgrimes		}
255145061Sstefanf	} else {
256145061Sstefanf		mod_ldbl = 0;
257145061Sstefanf	}
2581590Srgrimes
259145061Sstefanf	convch = *fmt;
260145061Sstefanf	nextch = *++fmt;
261145061Sstefanf	*fmt = '\0';
262145061Sstefanf	switch (convch) {
263145061Sstefanf	case 'b': {
264145078Sstefanf		size_t len;
265145061Sstefanf		char *p;
266145061Sstefanf		int getout;
267143906Sdas
26895409Stjr#ifdef SHELL
269145061Sstefanf		p = savestr(getstr());
27095409Stjr#else
271145061Sstefanf		p = strdup(getstr());
27295409Stjr#endif
273145061Sstefanf		if (p == NULL) {
274145061Sstefanf			warnx2("%s", strerror(ENOMEM), NULL);
275145061Sstefanf			return (NULL);
276145061Sstefanf		}
277145078Sstefanf		getout = escape(p, 0, &len);
278145061Sstefanf		*(fmt - 1) = 's';
279145061Sstefanf		PF(start, p);
280145061Sstefanf		*(fmt - 1) = 'b';
28195409Stjr#ifdef SHELL
282145061Sstefanf		ckfree(p);
28395409Stjr#else
284145061Sstefanf		free(p);
28595409Stjr#endif
286145061Sstefanf		if (getout)
287145061Sstefanf			return (fmt);
288145061Sstefanf		break;
289145061Sstefanf	}
290145061Sstefanf	case 'c': {
291145061Sstefanf		char p;
2921590Srgrimes
293145061Sstefanf		p = getchr();
294145061Sstefanf		PF(start, p);
295145061Sstefanf		break;
296145061Sstefanf	}
297145061Sstefanf	case 's': {
298145061Sstefanf		const char *p;
2991590Srgrimes
300145061Sstefanf		p = getstr();
301145061Sstefanf		PF(start, p);
302145061Sstefanf		break;
303145061Sstefanf	}
304145061Sstefanf	case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
305145061Sstefanf		char *f;
306148721Sstefanf		intmax_t val;
307148721Sstefanf		uintmax_t uval;
308145061Sstefanf		int signedconv;
3098874Srgrimes
310145061Sstefanf		signedconv = (convch == 'd' || convch == 'i');
311148721Sstefanf		if ((f = mknum(start, convch)) == NULL)
312145061Sstefanf			return (NULL);
313148721Sstefanf		if (getnum(&val, &uval, signedconv))
314145061Sstefanf			*rval = 1;
315145061Sstefanf		if (signedconv)
316145061Sstefanf			PF(f, val);
317145061Sstefanf		else
318145061Sstefanf			PF(f, uval);
319145061Sstefanf		break;
320145061Sstefanf	}
321145061Sstefanf	case 'e': case 'E':
322145061Sstefanf	case 'f': case 'F':
323145061Sstefanf	case 'g': case 'G':
324145061Sstefanf	case 'a': case 'A': {
325145061Sstefanf		long double p;
3261590Srgrimes
327145061Sstefanf		if (getfloating(&p, mod_ldbl))
328145061Sstefanf			*rval = 1;
329145061Sstefanf		if (mod_ldbl)
330145061Sstefanf			PF(start, p);
331145061Sstefanf		else
332145061Sstefanf			PF(start, (double)p);
333145061Sstefanf		break;
3341590Srgrimes	}
335145061Sstefanf	default:
336145061Sstefanf		warnx2("illegal format character %c", convch, NULL);
337145061Sstefanf		return (NULL);
338145061Sstefanf	}
339145061Sstefanf	*fmt = nextch;
340145061Sstefanf	return (fmt);
3411590Srgrimes}
3421590Srgrimes
3431590Srgrimesstatic char *
344148721Sstefanfmknum(char *str, int ch)
3451590Srgrimes{
34670256Sben	static char *copy;
34770256Sben	static size_t copy_size;
34895409Stjr	char *newcopy;
34970256Sben	size_t len, newlen;
3501590Srgrimes
3511590Srgrimes	len = strlen(str) + 2;
35270256Sben	if (len > copy_size) {
35370256Sben		newlen = ((len + 1023) >> 10) << 10;
35470256Sben#ifdef SHELL
35570256Sben		if ((newcopy = ckrealloc(copy, newlen)) == NULL)
35670256Sben#else
35770256Sben		if ((newcopy = realloc(copy, newlen)) == NULL)
35870256Sben#endif
35995409Stjr		{
36095409Stjr			warnx2("%s", strerror(ENOMEM), NULL);
36170256Sben			return (NULL);
36295409Stjr		}
36370256Sben		copy = newcopy;
36470256Sben		copy_size = newlen;
36570256Sben	}
36662928Sse
3671590Srgrimes	memmove(copy, str, len - 3);
368148721Sstefanf	copy[len - 3] = 'j';
3691590Srgrimes	copy[len - 2] = ch;
3701590Srgrimes	copy[len - 1] = '\0';
3711590Srgrimes	return (copy);
3721590Srgrimes}
3731590Srgrimes
37495300Sjmallettstatic int
375145078Sstefanfescape(char *fmt, int percent, size_t *len)
3761590Srgrimes{
377145078Sstefanf	char *save, *store;
378102944Sdwmalone	int value, c;
3791590Srgrimes
380145078Sstefanf	for (save = store = fmt; (c = *fmt); ++fmt, ++store) {
3811590Srgrimes		if (c != '\\') {
3821590Srgrimes			*store = c;
3831590Srgrimes			continue;
3841590Srgrimes		}
3851590Srgrimes		switch (*++fmt) {
3861590Srgrimes		case '\0':		/* EOS, user error */
3871590Srgrimes			*store = '\\';
3881590Srgrimes			*++store = '\0';
389145078Sstefanf			*len = store - save;
39095300Sjmallett			return (0);
3911590Srgrimes		case '\\':		/* backslash */
3921590Srgrimes		case '\'':		/* single quote */
3931590Srgrimes			*store = *fmt;
3941590Srgrimes			break;
3951590Srgrimes		case 'a':		/* bell/alert */
396145074Sstefanf			*store = '\a';
3971590Srgrimes			break;
3981590Srgrimes		case 'b':		/* backspace */
3991590Srgrimes			*store = '\b';
4001590Srgrimes			break;
40195300Sjmallett		case 'c':
40295300Sjmallett			*store = '\0';
403145078Sstefanf			*len = store - save;
40495300Sjmallett			return (1);
4051590Srgrimes		case 'f':		/* form-feed */
4061590Srgrimes			*store = '\f';
4071590Srgrimes			break;
4081590Srgrimes		case 'n':		/* newline */
4091590Srgrimes			*store = '\n';
4101590Srgrimes			break;
4111590Srgrimes		case 'r':		/* carriage-return */
4121590Srgrimes			*store = '\r';
4131590Srgrimes			break;
4141590Srgrimes		case 't':		/* horizontal tab */
4151590Srgrimes			*store = '\t';
4161590Srgrimes			break;
4171590Srgrimes		case 'v':		/* vertical tab */
418145074Sstefanf			*store = '\v';
4191590Srgrimes			break;
4201590Srgrimes					/* octal constant */
4211590Srgrimes		case '0': case '1': case '2': case '3':
4221590Srgrimes		case '4': case '5': case '6': case '7':
423181153Sdas			c = (!percent && *fmt == '0') ? 4 : 3;
424181153Sdas			for (value = 0;
4251590Srgrimes			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
4261590Srgrimes				value <<= 3;
4271590Srgrimes				value += *fmt - '0';
4281590Srgrimes			}
4291590Srgrimes			--fmt;
43098426Stjr			if (percent && value == '%') {
43198419Stjr				*store++ = '%';
43298419Stjr				*store = '%';
43398419Stjr			} else
43498419Stjr				*store = value;
4351590Srgrimes			break;
4361590Srgrimes		default:
4371590Srgrimes			*store = *fmt;
4381590Srgrimes			break;
4391590Srgrimes		}
4401590Srgrimes	}
4411590Srgrimes	*store = '\0';
442145078Sstefanf	*len = store - save;
44395300Sjmallett	return (0);
4441590Srgrimes}
4451590Srgrimes
4461590Srgrimesstatic int
447102944Sdwmalonegetchr(void)
4481590Srgrimes{
4491590Srgrimes	if (!*gargv)
4501590Srgrimes		return ('\0');
4511590Srgrimes	return ((int)**gargv++);
4521590Srgrimes}
4531590Srgrimes
45487298Sdwmalonestatic const char *
455102944Sdwmalonegetstr(void)
4561590Srgrimes{
4571590Srgrimes	if (!*gargv)
4581590Srgrimes		return ("");
4591590Srgrimes	return (*gargv++);
4601590Srgrimes}
4611590Srgrimes
4621590Srgrimesstatic int
463102944Sdwmalonegetint(int *ip)
4641590Srgrimes{
465148721Sstefanf	intmax_t val;
466148721Sstefanf	uintmax_t uval;
46795409Stjr	int rval;
4681590Srgrimes
469148721Sstefanf	if (getnum(&val, &uval, 1))
4701590Srgrimes		return (1);
47195409Stjr	rval = 0;
47295409Stjr	if (val < INT_MIN || val > INT_MAX) {
47341582Sbde		warnx3("%s: %s", *gargv, strerror(ERANGE));
47495409Stjr		rval = 1;
47595409Stjr	}
47662928Sse	*ip = (int)val;
47795409Stjr	return (rval);
4781590Srgrimes}
4791590Srgrimes
4801590Srgrimesstatic int
481148721Sstefanfgetnum(intmax_t *ip, uintmax_t *uip, int signedconv)
4821590Srgrimes{
4831590Srgrimes	char *ep;
48495409Stjr	int rval;
4851590Srgrimes
4861590Srgrimes	if (!*gargv) {
487148721Sstefanf		*ip = 0;
4881590Srgrimes		return (0);
4891590Srgrimes	}
49095300Sjmallett	if (**gargv == '"' || **gargv == '\'') {
49195409Stjr		if (signedconv)
492148721Sstefanf			*ip = asciicode();
49395409Stjr		else
494148721Sstefanf			*uip = asciicode();
4951590Srgrimes		return (0);
4961590Srgrimes	}
49795409Stjr	rval = 0;
49895300Sjmallett	errno = 0;
49995409Stjr	if (signedconv)
500148721Sstefanf		*ip = strtoimax(*gargv, &ep, 0);
50195409Stjr	else
502148721Sstefanf		*uip = strtoumax(*gargv, &ep, 0);
50395409Stjr	if (ep == *gargv) {
50495300Sjmallett		warnx2("%s: expected numeric value", *gargv, NULL);
50595409Stjr		rval = 1;
50695409Stjr	}
50795409Stjr	else if (*ep != '\0') {
50895300Sjmallett		warnx2("%s: not completely converted", *gargv, NULL);
50995409Stjr		rval = 1;
51095409Stjr	}
51195409Stjr	if (errno == ERANGE) {
51295300Sjmallett		warnx3("%s: %s", *gargv, strerror(ERANGE));
51395409Stjr		rval = 1;
51495409Stjr	}
51595300Sjmallett	++gargv;
51695409Stjr	return (rval);
5171590Srgrimes}
5181590Srgrimes
51995409Stjrstatic int
520143906Sdasgetfloating(long double *dp, int mod_ldbl)
5211590Srgrimes{
52295300Sjmallett	char *ep;
52395409Stjr	int rval;
52495300Sjmallett
525145027Sstefanf	if (!*gargv) {
526145027Sstefanf		*dp = 0.0;
52795409Stjr		return (0);
528145027Sstefanf	}
52995300Sjmallett	if (**gargv == '"' || **gargv == '\'') {
53095409Stjr		*dp = asciicode();
53195409Stjr		return (0);
53295300Sjmallett	}
533126729Scperciva	rval = 0;
53495300Sjmallett	errno = 0;
535143906Sdas	if (mod_ldbl)
536143906Sdas		*dp = strtold(*gargv, &ep);
537143906Sdas	else
538143906Sdas		*dp = strtod(*gargv, &ep);
53995409Stjr	if (ep == *gargv) {
54095300Sjmallett		warnx2("%s: expected numeric value", *gargv, NULL);
54195409Stjr		rval = 1;
54295409Stjr	} else if (*ep != '\0') {
54395300Sjmallett		warnx2("%s: not completely converted", *gargv, NULL);
54495409Stjr		rval = 1;
54595409Stjr	}
54695409Stjr	if (errno == ERANGE) {
54795300Sjmallett		warnx3("%s: %s", *gargv, strerror(ERANGE));
54895409Stjr		rval = 1;
54995409Stjr	}
55095300Sjmallett	++gargv;
55195409Stjr	return (rval);
5521590Srgrimes}
5531590Srgrimes
5541590Srgrimesstatic int
555102944Sdwmaloneasciicode(void)
5561590Srgrimes{
557102944Sdwmalone	int ch;
5581590Srgrimes
5591590Srgrimes	ch = **gargv;
5601590Srgrimes	if (ch == '\'' || ch == '"')
5611590Srgrimes		ch = (*gargv)[1];
5621590Srgrimes	++gargv;
5631590Srgrimes	return (ch);
5641590Srgrimes}
5651590Srgrimes
5661590Srgrimesstatic void
567102944Sdwmaloneusage(void)
5681590Srgrimes{
569146466Sru	(void)fprintf(stderr, "usage: printf format [arguments ...]\n");
5701590Srgrimes}
571