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 */
29219153Sjilles/*
30219153Sjilles * Important: This file is used both as a standalone program /usr/bin/printf
31219153Sjilles * and as a builtin for /bin/sh (#define SHELL).
32219153Sjilles */
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$";
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>
56216417Sdelphij#include <locale.h>
571590Srgrimes#include <stdio.h>
581590Srgrimes#include <stdlib.h>
591590Srgrimes#include <string.h>
6027966Ssteve#include <unistd.h>
61222418Sjilles#include <wchar.h>
621590Srgrimes
631590Srgrimes#ifdef SHELL
64265160Spfg#define	main printfcmd
6512730Sjoerg#include "bltin/bltin.h"
66215520Sjilles#include "error.h"
67240541Sjilles#include "options.h"
681590Srgrimes#endif
691590Srgrimes
70265160Spfg#define	PF(f, func) do {						\
71230027Spfg	char *b = NULL;							\
72230027Spfg	if (havewidth)							\
73230027Spfg		if (haveprec)						\
7418613Speter			(void)asprintf(&b, f, fieldwidth, precision, func); \
75230027Spfg		else							\
76230027Spfg			(void)asprintf(&b, f, fieldwidth, func);	\
77230027Spfg	else if (haveprec)						\
78230027Spfg		(void)asprintf(&b, f, precision, func);			\
79230027Spfg	else								\
80230027Spfg		(void)asprintf(&b, f, func);				\
81230027Spfg	if (b) {							\
82230027Spfg		(void)fputs(b, stdout);					\
83230027Spfg		free(b);						\
84230027Spfg	}								\
8595409Stjr} while (0)
861590Srgrimes
8792921Simpstatic int	 asciicode(void);
88215520Sjillesstatic char	*printf_doformat(char *, int *);
89145078Sstefanfstatic int	 escape(char *, int, size_t *);
9092921Simpstatic int	 getchr(void);
91143906Sdasstatic int	 getfloating(long double *, int);
9292921Simpstatic int	 getint(int *);
93148721Sstefanfstatic int	 getnum(intmax_t *, uintmax_t *, int);
9495409Stjrstatic const char
9595409Stjr		*getstr(void);
96216418Sdelphijstatic char	*mknum(char *, char);
9792921Simpstatic void	 usage(void);
981590Srgrimes
991590Srgrimesstatic char **gargv;
1001590Srgrimes
1011590Srgrimesint
102102944Sdwmalonemain(int argc, char *argv[])
1031590Srgrimes{
104145078Sstefanf	size_t len;
105240541Sjilles	int chopped, end, rval;
106145061Sstefanf	char *format, *fmt, *start;
107240541Sjilles#ifndef SHELL
108240541Sjilles	int ch;
1091590Srgrimes
110216424Sdelphij	(void) setlocale(LC_ALL, "");
11172304Sache#endif
112240541Sjilles
113215520Sjilles#ifdef SHELL
114240541Sjilles	nextopt("");
115240541Sjilles	argc -= argptr - argv;
116240541Sjilles	argv = argptr;
117240541Sjilles#else
118216447Sdelphij	while ((ch = getopt(argc, argv, "")) != -1)
119216447Sdelphij		switch (ch) {
120216447Sdelphij		case '?':
121216447Sdelphij		default:
122216447Sdelphij			usage();
123216447Sdelphij			return (1);
124216447Sdelphij		}
125216447Sdelphij	argc -= optind;
126216447Sdelphij	argv += optind;
127240541Sjilles#endif
1281590Srgrimes
1291590Srgrimes	if (argc < 1) {
1301590Srgrimes		usage();
131216439Sdelphij		return (1);
1321590Srgrimes	}
1331590Srgrimes
134215520Sjilles#ifdef SHELL
135215520Sjilles	INTOFF;
136215520Sjilles#endif
1371590Srgrimes	/*
1381590Srgrimes	 * Basic algorithm is to scan the format string for conversion
1391590Srgrimes	 * specifications -- once one is found, find out if the field
1401590Srgrimes	 * width or precision is a '*'; if it is, gather up value.  Note,
1411590Srgrimes	 * format strings are reused as necessary to use up the provided
1421590Srgrimes	 * arguments, arguments of zero/null string are provided to use
1431590Srgrimes	 * up the format string.
1441590Srgrimes	 */
145145078Sstefanf	fmt = format = *argv;
146145078Sstefanf	chopped = escape(fmt, 1, &len);		/* backslash interpretation */
147145061Sstefanf	rval = end = 0;
1481590Srgrimes	gargv = ++argv;
1491590Srgrimes	for (;;) {
150145061Sstefanf		start = fmt;
151145078Sstefanf		while (fmt < format + len) {
152145061Sstefanf			if (fmt[0] == '%') {
153145061Sstefanf				fwrite(start, 1, fmt - start, stdout);
154145061Sstefanf				if (fmt[1] == '%') {
155145061Sstefanf					/* %% prints a % */
156145061Sstefanf					putchar('%');
157145061Sstefanf					fmt += 2;
158145061Sstefanf				} else {
159215520Sjilles					fmt = printf_doformat(fmt, &rval);
160215520Sjilles					if (fmt == NULL) {
161215520Sjilles#ifdef SHELL
162215520Sjilles						INTON;
163215520Sjilles#endif
164145061Sstefanf						return (1);
165215520Sjilles					}
166145061Sstefanf					end = 0;
16795300Sjmallett				}
168145061Sstefanf				start = fmt;
169145061Sstefanf			} else
17098420Stjr				fmt++;
1711590Srgrimes		}
1721590Srgrimes
173145061Sstefanf		if (end == 1) {
174216606Sjilles			warnx("missing format character");
175215520Sjilles#ifdef SHELL
176215520Sjilles			INTON;
177215520Sjilles#endif
178145061Sstefanf			return (1);
179145061Sstefanf		}
180145061Sstefanf		fwrite(start, 1, fmt - start, stdout);
181215520Sjilles		if (chopped || !*gargv) {
182215520Sjilles#ifdef SHELL
183215520Sjilles			INTON;
184215520Sjilles#endif
185145061Sstefanf			return (rval);
186215520Sjilles		}
187145061Sstefanf		/* Restart at the beginning of the format string. */
188145061Sstefanf		fmt = format;
189145061Sstefanf		end = 1;
190145061Sstefanf	}
191145061Sstefanf	/* NOTREACHED */
192145061Sstefanf}
193145061Sstefanf
194145061Sstefanf
195145061Sstefanfstatic char *
196215520Sjillesprintf_doformat(char *start, int *rval)
197145061Sstefanf{
198145061Sstefanf	static const char skip1[] = "#'-+ 0";
199145061Sstefanf	static const char skip2[] = "0123456789";
200145061Sstefanf	char *fmt;
201145061Sstefanf	int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
202145061Sstefanf	char convch, nextch;
203145061Sstefanf
204145061Sstefanf	fmt = start + 1;
205145061Sstefanf	/* skip to field width */
206145061Sstefanf	fmt += strspn(fmt, skip1);
207145061Sstefanf	if (*fmt == '*') {
208145061Sstefanf		if (getint(&fieldwidth))
209145061Sstefanf			return (NULL);
210145061Sstefanf		havewidth = 1;
211145061Sstefanf		++fmt;
212145061Sstefanf	} else {
213145061Sstefanf		havewidth = 0;
214145061Sstefanf
215145061Sstefanf		/* skip to possible '.', get following precision */
216145061Sstefanf		fmt += strspn(fmt, skip2);
217145061Sstefanf	}
218145061Sstefanf	if (*fmt == '.') {
219145061Sstefanf		/* precision present? */
220145061Sstefanf		++fmt;
2211590Srgrimes		if (*fmt == '*') {
222145061Sstefanf			if (getint(&precision))
223145061Sstefanf				return (NULL);
224145061Sstefanf			haveprec = 1;
2258323Sjoerg			++fmt;
2268323Sjoerg		} else {
227145061Sstefanf			haveprec = 0;
2281590Srgrimes
229145061Sstefanf			/* skip to conversion char */
230144902Sstefanf			fmt += strspn(fmt, skip2);
2318323Sjoerg		}
232145061Sstefanf	} else
233145061Sstefanf		haveprec = 0;
234145061Sstefanf	if (!*fmt) {
235216606Sjilles		warnx("missing format character");
236145061Sstefanf		return (NULL);
237145061Sstefanf	}
2388323Sjoerg
239145061Sstefanf	/*
240145061Sstefanf	 * Look for a length modifier.  POSIX doesn't have these, so
241145061Sstefanf	 * we only support them for floating-point conversions, which
242145061Sstefanf	 * are extensions.  This is useful because the L modifier can
243145061Sstefanf	 * be used to gain extra range and precision, while omitting
244145061Sstefanf	 * it is more likely to produce consistent results on different
245145061Sstefanf	 * architectures.  This is not so important for integers
246145061Sstefanf	 * because overflow is the only bad thing that can happen to
247145061Sstefanf	 * them, but consider the command  printf %a 1.1
248145061Sstefanf	 */
249145061Sstefanf	if (*fmt == 'L') {
250145061Sstefanf		mod_ldbl = 1;
251145061Sstefanf		fmt++;
252145061Sstefanf		if (!strchr("aAeEfFgG", *fmt)) {
253216606Sjilles			warnx("bad modifier L for %%%c", *fmt);
254145061Sstefanf			return (NULL);
2551590Srgrimes		}
256145061Sstefanf	} else {
257145061Sstefanf		mod_ldbl = 0;
258145061Sstefanf	}
2591590Srgrimes
260145061Sstefanf	convch = *fmt;
261145061Sstefanf	nextch = *++fmt;
262145061Sstefanf	*fmt = '\0';
263145061Sstefanf	switch (convch) {
264145061Sstefanf	case 'b': {
265145078Sstefanf		size_t len;
266145061Sstefanf		char *p;
267145061Sstefanf		int getout;
268143906Sdas
269145061Sstefanf		p = strdup(getstr());
270145061Sstefanf		if (p == NULL) {
271216606Sjilles			warnx("%s", strerror(ENOMEM));
272145061Sstefanf			return (NULL);
273145061Sstefanf		}
274145078Sstefanf		getout = escape(p, 0, &len);
275145061Sstefanf		*(fmt - 1) = 's';
276145061Sstefanf		PF(start, p);
277145061Sstefanf		*(fmt - 1) = 'b';
278145061Sstefanf		free(p);
279145061Sstefanf		if (getout)
280145061Sstefanf			return (fmt);
281145061Sstefanf		break;
282145061Sstefanf	}
283145061Sstefanf	case 'c': {
284145061Sstefanf		char p;
2851590Srgrimes
286145061Sstefanf		p = getchr();
287145061Sstefanf		PF(start, p);
288145061Sstefanf		break;
289145061Sstefanf	}
290145061Sstefanf	case 's': {
291145061Sstefanf		const char *p;
2921590Srgrimes
293145061Sstefanf		p = getstr();
294145061Sstefanf		PF(start, p);
295145061Sstefanf		break;
296145061Sstefanf	}
297145061Sstefanf	case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
298145061Sstefanf		char *f;
299148721Sstefanf		intmax_t val;
300148721Sstefanf		uintmax_t uval;
301145061Sstefanf		int signedconv;
3028874Srgrimes
303145061Sstefanf		signedconv = (convch == 'd' || convch == 'i');
304148721Sstefanf		if ((f = mknum(start, convch)) == NULL)
305145061Sstefanf			return (NULL);
306148721Sstefanf		if (getnum(&val, &uval, signedconv))
307145061Sstefanf			*rval = 1;
308145061Sstefanf		if (signedconv)
309145061Sstefanf			PF(f, val);
310145061Sstefanf		else
311145061Sstefanf			PF(f, uval);
312145061Sstefanf		break;
313145061Sstefanf	}
314145061Sstefanf	case 'e': case 'E':
315145061Sstefanf	case 'f': case 'F':
316145061Sstefanf	case 'g': case 'G':
317145061Sstefanf	case 'a': case 'A': {
318145061Sstefanf		long double p;
3191590Srgrimes
320145061Sstefanf		if (getfloating(&p, mod_ldbl))
321145061Sstefanf			*rval = 1;
322145061Sstefanf		if (mod_ldbl)
323145061Sstefanf			PF(start, p);
324145061Sstefanf		else
325145061Sstefanf			PF(start, (double)p);
326145061Sstefanf		break;
3271590Srgrimes	}
328145061Sstefanf	default:
329216606Sjilles		warnx("illegal format character %c", convch);
330145061Sstefanf		return (NULL);
331145061Sstefanf	}
332145061Sstefanf	*fmt = nextch;
333145061Sstefanf	return (fmt);
3341590Srgrimes}
3351590Srgrimes
3361590Srgrimesstatic char *
337216418Sdelphijmknum(char *str, char ch)
3381590Srgrimes{
33970256Sben	static char *copy;
34070256Sben	static size_t copy_size;
34195409Stjr	char *newcopy;
34270256Sben	size_t len, newlen;
3431590Srgrimes
3441590Srgrimes	len = strlen(str) + 2;
34570256Sben	if (len > copy_size) {
34670256Sben		newlen = ((len + 1023) >> 10) << 10;
34770256Sben		if ((newcopy = realloc(copy, newlen)) == NULL)
34895409Stjr		{
349216606Sjilles			warnx("%s", strerror(ENOMEM));
35070256Sben			return (NULL);
35195409Stjr		}
35270256Sben		copy = newcopy;
35370256Sben		copy_size = newlen;
35470256Sben	}
35562928Sse
3561590Srgrimes	memmove(copy, str, len - 3);
357148721Sstefanf	copy[len - 3] = 'j';
3581590Srgrimes	copy[len - 2] = ch;
3591590Srgrimes	copy[len - 1] = '\0';
3601590Srgrimes	return (copy);
3611590Srgrimes}
3621590Srgrimes
36395300Sjmallettstatic int
364145078Sstefanfescape(char *fmt, int percent, size_t *len)
3651590Srgrimes{
366230027Spfg	char *save, *store, c;
367230027Spfg	int value;
3681590Srgrimes
369230027Spfg	for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
3701590Srgrimes		if (c != '\\') {
3711590Srgrimes			*store = c;
3721590Srgrimes			continue;
3731590Srgrimes		}
3741590Srgrimes		switch (*++fmt) {
3751590Srgrimes		case '\0':		/* EOS, user error */
3761590Srgrimes			*store = '\\';
3771590Srgrimes			*++store = '\0';
378145078Sstefanf			*len = store - save;
37995300Sjmallett			return (0);
3801590Srgrimes		case '\\':		/* backslash */
3811590Srgrimes		case '\'':		/* single quote */
3821590Srgrimes			*store = *fmt;
3831590Srgrimes			break;
3841590Srgrimes		case 'a':		/* bell/alert */
385145074Sstefanf			*store = '\a';
3861590Srgrimes			break;
3871590Srgrimes		case 'b':		/* backspace */
3881590Srgrimes			*store = '\b';
3891590Srgrimes			break;
39095300Sjmallett		case 'c':
39195300Sjmallett			*store = '\0';
392145078Sstefanf			*len = store - save;
39395300Sjmallett			return (1);
3941590Srgrimes		case 'f':		/* form-feed */
3951590Srgrimes			*store = '\f';
3961590Srgrimes			break;
3971590Srgrimes		case 'n':		/* newline */
3981590Srgrimes			*store = '\n';
3991590Srgrimes			break;
4001590Srgrimes		case 'r':		/* carriage-return */
4011590Srgrimes			*store = '\r';
4021590Srgrimes			break;
4031590Srgrimes		case 't':		/* horizontal tab */
4041590Srgrimes			*store = '\t';
4051590Srgrimes			break;
4061590Srgrimes		case 'v':		/* vertical tab */
407145074Sstefanf			*store = '\v';
4081590Srgrimes			break;
4091590Srgrimes					/* octal constant */
4101590Srgrimes		case '0': case '1': case '2': case '3':
4111590Srgrimes		case '4': case '5': case '6': case '7':
412181153Sdas			c = (!percent && *fmt == '0') ? 4 : 3;
413181153Sdas			for (value = 0;
4141590Srgrimes			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
4151590Srgrimes				value <<= 3;
4161590Srgrimes				value += *fmt - '0';
4171590Srgrimes			}
4181590Srgrimes			--fmt;
41998426Stjr			if (percent && value == '%') {
42098419Stjr				*store++ = '%';
42198419Stjr				*store = '%';
42298419Stjr			} else
423230027Spfg				*store = (char)value;
4241590Srgrimes			break;
4251590Srgrimes		default:
4261590Srgrimes			*store = *fmt;
4271590Srgrimes			break;
4281590Srgrimes		}
4291590Srgrimes	}
4301590Srgrimes	*store = '\0';
431145078Sstefanf	*len = store - save;
43295300Sjmallett	return (0);
4331590Srgrimes}
4341590Srgrimes
4351590Srgrimesstatic int
436102944Sdwmalonegetchr(void)
4371590Srgrimes{
4381590Srgrimes	if (!*gargv)
4391590Srgrimes		return ('\0');
4401590Srgrimes	return ((int)**gargv++);
4411590Srgrimes}
4421590Srgrimes
44387298Sdwmalonestatic const char *
444102944Sdwmalonegetstr(void)
4451590Srgrimes{
4461590Srgrimes	if (!*gargv)
4471590Srgrimes		return ("");
4481590Srgrimes	return (*gargv++);
4491590Srgrimes}
4501590Srgrimes
4511590Srgrimesstatic int
452102944Sdwmalonegetint(int *ip)
4531590Srgrimes{
454148721Sstefanf	intmax_t val;
455148721Sstefanf	uintmax_t uval;
45695409Stjr	int rval;
4571590Srgrimes
458148721Sstefanf	if (getnum(&val, &uval, 1))
4591590Srgrimes		return (1);
46095409Stjr	rval = 0;
46195409Stjr	if (val < INT_MIN || val > INT_MAX) {
462216606Sjilles		warnx("%s: %s", *gargv, strerror(ERANGE));
46395409Stjr		rval = 1;
46495409Stjr	}
46562928Sse	*ip = (int)val;
46695409Stjr	return (rval);
4671590Srgrimes}
4681590Srgrimes
4691590Srgrimesstatic int
470148721Sstefanfgetnum(intmax_t *ip, uintmax_t *uip, int signedconv)
4711590Srgrimes{
4721590Srgrimes	char *ep;
47395409Stjr	int rval;
4741590Srgrimes
4751590Srgrimes	if (!*gargv) {
476244407Seadler		*ip = *uip = 0;
4771590Srgrimes		return (0);
4781590Srgrimes	}
47995300Sjmallett	if (**gargv == '"' || **gargv == '\'') {
48095409Stjr		if (signedconv)
481148721Sstefanf			*ip = asciicode();
48295409Stjr		else
483148721Sstefanf			*uip = asciicode();
4841590Srgrimes		return (0);
4851590Srgrimes	}
48695409Stjr	rval = 0;
48795300Sjmallett	errno = 0;
48895409Stjr	if (signedconv)
489148721Sstefanf		*ip = strtoimax(*gargv, &ep, 0);
49095409Stjr	else
491148721Sstefanf		*uip = strtoumax(*gargv, &ep, 0);
49295409Stjr	if (ep == *gargv) {
493216606Sjilles		warnx("%s: expected numeric value", *gargv);
49495409Stjr		rval = 1;
49595409Stjr	}
49695409Stjr	else if (*ep != '\0') {
497216606Sjilles		warnx("%s: not completely converted", *gargv);
49895409Stjr		rval = 1;
49995409Stjr	}
50095409Stjr	if (errno == ERANGE) {
501216606Sjilles		warnx("%s: %s", *gargv, strerror(ERANGE));
50295409Stjr		rval = 1;
50395409Stjr	}
50495300Sjmallett	++gargv;
50595409Stjr	return (rval);
5061590Srgrimes}
5071590Srgrimes
50895409Stjrstatic int
509143906Sdasgetfloating(long double *dp, int mod_ldbl)
5101590Srgrimes{
51195300Sjmallett	char *ep;
51295409Stjr	int rval;
51395300Sjmallett
514145027Sstefanf	if (!*gargv) {
515145027Sstefanf		*dp = 0.0;
51695409Stjr		return (0);
517145027Sstefanf	}
51895300Sjmallett	if (**gargv == '"' || **gargv == '\'') {
51995409Stjr		*dp = asciicode();
52095409Stjr		return (0);
52195300Sjmallett	}
522126729Scperciva	rval = 0;
52395300Sjmallett	errno = 0;
524143906Sdas	if (mod_ldbl)
525143906Sdas		*dp = strtold(*gargv, &ep);
526143906Sdas	else
527143906Sdas		*dp = strtod(*gargv, &ep);
52895409Stjr	if (ep == *gargv) {
529216606Sjilles		warnx("%s: expected numeric value", *gargv);
53095409Stjr		rval = 1;
53195409Stjr	} else if (*ep != '\0') {
532216606Sjilles		warnx("%s: not completely converted", *gargv);
53395409Stjr		rval = 1;
53495409Stjr	}
53595409Stjr	if (errno == ERANGE) {
536216606Sjilles		warnx("%s: %s", *gargv, strerror(ERANGE));
53795409Stjr		rval = 1;
53895409Stjr	}
53995300Sjmallett	++gargv;
54095409Stjr	return (rval);
5411590Srgrimes}
5421590Srgrimes
5431590Srgrimesstatic int
544102944Sdwmaloneasciicode(void)
5451590Srgrimes{
546102944Sdwmalone	int ch;
547222418Sjilles	wchar_t wch;
548222418Sjilles	mbstate_t mbs;
5491590Srgrimes
550222418Sjilles	ch = (unsigned char)**gargv;
551222418Sjilles	if (ch == '\'' || ch == '"') {
552222418Sjilles		memset(&mbs, 0, sizeof(mbs));
553222418Sjilles		switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
554222418Sjilles		case (size_t)-2:
555222418Sjilles		case (size_t)-1:
556222418Sjilles			wch = (unsigned char)gargv[0][1];
557222418Sjilles			break;
558222418Sjilles		case 0:
559222418Sjilles			wch = 0;
560222418Sjilles			break;
561222418Sjilles		}
562222418Sjilles		ch = wch;
563222418Sjilles	}
5641590Srgrimes	++gargv;
5651590Srgrimes	return (ch);
5661590Srgrimes}
5671590Srgrimes
5681590Srgrimesstatic void
569102944Sdwmaloneusage(void)
5701590Srgrimes{
571146466Sru	(void)fprintf(stderr, "usage: printf format [arguments ...]\n");
5721590Srgrimes}
573