1c9aca921SXin LI /*-
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
4702da352SPedro F. Giffuni * Copyright 2018 Staysail Systems, Inc. <[email protected]>
5f8a6c905SPedro F. Giffuni * Copyright 2014 Garrett D'Amore <[email protected]>
6b0620803SPedro F. Giffuni * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
79b50d902SRodney W. Grimes * Copyright (c) 1989, 1993
89b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved.
99b50d902SRodney W. Grimes *
109b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
119b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions
129b50d902SRodney W. Grimes * are met:
139b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
159b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
169b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
179b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
199b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software
209b50d902SRodney W. Grimes * without specific prior written permission.
219b50d902SRodney W. Grimes *
229b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329b50d902SRodney W. Grimes * SUCH DAMAGE.
339b50d902SRodney W. Grimes */
3477fae5c1SJilles Tjoelker /*
3577fae5c1SJilles Tjoelker * Important: This file is used both as a standalone program /usr/bin/printf
3677fae5c1SJilles Tjoelker * and as a builtin for /bin/sh (#define SHELL).
3777fae5c1SJilles Tjoelker */
389b50d902SRodney W. Grimes
391866e8abSJilles Tjoelker #ifndef SHELL
409b50d902SRodney W. Grimes #ifndef lint
413ec30b79SSteve Price static char const copyright[] =
429b50d902SRodney W. Grimes "@(#) Copyright (c) 1989, 1993\n\
439b50d902SRodney W. Grimes The Regents of the University of California. All rights reserved.\n";
449b50d902SRodney W. Grimes #endif /* not lint */
459b50d902SRodney W. Grimes #endif
469b50d902SRodney W. Grimes
479b50d902SRodney W. Grimes #ifndef lint
48c9e05349SWarner Losh #if 0
493ec30b79SSteve Price static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
50c9e05349SWarner Losh #endif
511ea7321bSMartin Cracauer static const char rcsid[] =
521ea7321bSMartin Cracauer "$FreeBSD$";
539b50d902SRodney W. Grimes #endif /* not lint */
549b50d902SRodney W. Grimes
559b50d902SRodney W. Grimes #include <sys/types.h>
569b50d902SRodney W. Grimes
57f8a6c905SPedro F. Giffuni #include <ctype.h>
589b50d902SRodney W. Grimes #include <err.h>
599b50d902SRodney W. Grimes #include <errno.h>
60d41d23e1SStefan Farfeleder #include <inttypes.h>
619b50d902SRodney W. Grimes #include <limits.h>
62c9aca921SXin LI #include <locale.h>
639b50d902SRodney W. Grimes #include <stdio.h>
649b50d902SRodney W. Grimes #include <stdlib.h>
659b50d902SRodney W. Grimes #include <string.h>
669d19feb5SSteve Price #include <unistd.h>
6798102dabSJilles Tjoelker #include <wchar.h>
689b50d902SRodney W. Grimes
699b50d902SRodney W. Grimes #ifdef SHELL
709b50d902SRodney W. Grimes #define main printfcmd
71d9f93710SJoerg Wunsch #include "bltin/bltin.h"
727cbda738SJilles Tjoelker #include "options.h"
739b50d902SRodney W. Grimes #endif
749b50d902SRodney W. Grimes
75bacab7d6STim J. Robbins #define PF(f, func) do { \
7698dd6386STim J. Robbins if (havewidth) \
7798dd6386STim J. Robbins if (haveprec) \
78342b089bSJilles Tjoelker (void)printf(f, fieldwidth, precision, func); \
799b50d902SRodney W. Grimes else \
80342b089bSJilles Tjoelker (void)printf(f, fieldwidth, func); \
8198dd6386STim J. Robbins else if (haveprec) \
82342b089bSJilles Tjoelker (void)printf(f, precision, func); \
839b50d902SRodney W. Grimes else \
84342b089bSJilles Tjoelker (void)printf(f, func); \
85bacab7d6STim J. Robbins } while (0)
869b50d902SRodney W. Grimes
87d3cb5dedSWarner Losh static int asciicode(void);
889897c45fSJilles Tjoelker static char *printf_doformat(char *, int *);
893ec96cafSStefan Farfeleder static int escape(char *, int, size_t *);
90d3cb5dedSWarner Losh static int getchr(void);
91fd757c50SDavid Schultz static int getfloating(long double *, int);
92d3cb5dedSWarner Losh static int getint(int *);
93d41d23e1SStefan Farfeleder static int getnum(intmax_t *, uintmax_t *, int);
94bacab7d6STim J. Robbins static const char
95bacab7d6STim J. Robbins *getstr(void);
969180853bSXin LI static char *mknum(char *, char);
97d3cb5dedSWarner Losh static void usage(void);
989b50d902SRodney W. Grimes
99f8a6c905SPedro F. Giffuni static const char digits[] = "0123456789";
100f8a6c905SPedro F. Giffuni
10130238f49SPedro F. Giffuni static char end_fmt[1];
10230238f49SPedro F. Giffuni
103b0620803SPedro F. Giffuni static int myargc;
104b0620803SPedro F. Giffuni static char **myargv;
1059b50d902SRodney W. Grimes static char **gargv;
106f8a6c905SPedro F. Giffuni static char **maxargv;
1079b50d902SRodney W. Grimes
1089b50d902SRodney W. Grimes int
main(int argc,char * argv[])109f4ac32deSDavid Malone main(int argc, char *argv[])
1109b50d902SRodney W. Grimes {
1113ec96cafSStefan Farfeleder size_t len;
112437bce62SPedro F. Giffuni int end, rval;
113fbd08684SStefan Farfeleder char *format, *fmt, *start;
1141866e8abSJilles Tjoelker #ifndef SHELL
1157cbda738SJilles Tjoelker int ch;
1167cbda738SJilles Tjoelker
11769be0c5eSXin LI (void) setlocale(LC_ALL, "");
118dc7d8c99SAndrey A. Chernov #endif
1197cbda738SJilles Tjoelker
1209897c45fSJilles Tjoelker #ifdef SHELL
1217cbda738SJilles Tjoelker nextopt("");
1227cbda738SJilles Tjoelker argc -= argptr - argv;
1237cbda738SJilles Tjoelker argv = argptr;
1247cbda738SJilles Tjoelker #else
125de46de4dSXin LI while ((ch = getopt(argc, argv, "")) != -1)
126de46de4dSXin LI switch (ch) {
127de46de4dSXin LI case '?':
128de46de4dSXin LI default:
129de46de4dSXin LI usage();
130de46de4dSXin LI return (1);
1319b50d902SRodney W. Grimes }
132de46de4dSXin LI argc -= optind;
133de46de4dSXin LI argv += optind;
1347cbda738SJilles Tjoelker #endif
1359b50d902SRodney W. Grimes
1369b50d902SRodney W. Grimes if (argc < 1) {
1379b50d902SRodney W. Grimes usage();
1385eccc000SXin LI return (1);
1399b50d902SRodney W. Grimes }
1409b50d902SRodney W. Grimes
1419897c45fSJilles Tjoelker #ifdef SHELL
1429897c45fSJilles Tjoelker INTOFF;
1439897c45fSJilles Tjoelker #endif
1449b50d902SRodney W. Grimes /*
1459b50d902SRodney W. Grimes * Basic algorithm is to scan the format string for conversion
1469b50d902SRodney W. Grimes * specifications -- once one is found, find out if the field
1479b50d902SRodney W. Grimes * width or precision is a '*'; if it is, gather up value. Note,
1489b50d902SRodney W. Grimes * format strings are reused as necessary to use up the provided
1499b50d902SRodney W. Grimes * arguments, arguments of zero/null string are provided to use
1509b50d902SRodney W. Grimes * up the format string.
1519b50d902SRodney W. Grimes */
1523ec96cafSStefan Farfeleder fmt = format = *argv;
153437bce62SPedro F. Giffuni escape(fmt, 1, &len); /* backslash interpretation */
154fbd08684SStefan Farfeleder rval = end = 0;
1559b50d902SRodney W. Grimes gargv = ++argv;
156b0620803SPedro F. Giffuni
1579b50d902SRodney W. Grimes for (;;) {
158f8a6c905SPedro F. Giffuni maxargv = gargv;
159b0620803SPedro F. Giffuni
160b0620803SPedro F. Giffuni myargv = gargv;
161b0620803SPedro F. Giffuni for (myargc = 0; gargv[myargc]; myargc++)
162b0620803SPedro F. Giffuni /* nop */;
163fbd08684SStefan Farfeleder start = fmt;
1643ec96cafSStefan Farfeleder while (fmt < format + len) {
165fbd08684SStefan Farfeleder if (fmt[0] == '%') {
166fbd08684SStefan Farfeleder fwrite(start, 1, fmt - start, stdout);
167fbd08684SStefan Farfeleder if (fmt[1] == '%') {
1689b50d902SRodney W. Grimes /* %% prints a % */
169fbd08684SStefan Farfeleder putchar('%');
170fbd08684SStefan Farfeleder fmt += 2;
171fbd08684SStefan Farfeleder } else {
1729897c45fSJilles Tjoelker fmt = printf_doformat(fmt, &rval);
17330238f49SPedro F. Giffuni if (fmt == NULL || fmt == end_fmt) {
1749897c45fSJilles Tjoelker #ifdef SHELL
1759897c45fSJilles Tjoelker INTON;
1769897c45fSJilles Tjoelker #endif
17730238f49SPedro F. Giffuni return (fmt == NULL ? 1 : rval);
1789897c45fSJilles Tjoelker }
179fbd08684SStefan Farfeleder end = 0;
1809b50d902SRodney W. Grimes }
181fbd08684SStefan Farfeleder start = fmt;
182fbd08684SStefan Farfeleder } else
183fbd08684SStefan Farfeleder fmt++;
184b0620803SPedro F. Giffuni if (gargv > maxargv)
185b0620803SPedro F. Giffuni maxargv = gargv;
1869b50d902SRodney W. Grimes }
187b0620803SPedro F. Giffuni gargv = maxargv;
1889b50d902SRodney W. Grimes
189fbd08684SStefan Farfeleder if (end == 1) {
1906a6760dbSJilles Tjoelker warnx("missing format character");
1919897c45fSJilles Tjoelker #ifdef SHELL
1929897c45fSJilles Tjoelker INTON;
1939897c45fSJilles Tjoelker #endif
194fbd08684SStefan Farfeleder return (1);
195fbd08684SStefan Farfeleder }
196fbd08684SStefan Farfeleder fwrite(start, 1, fmt - start, stdout);
197437bce62SPedro F. Giffuni if (!*gargv) {
1989897c45fSJilles Tjoelker #ifdef SHELL
1999897c45fSJilles Tjoelker INTON;
2009897c45fSJilles Tjoelker #endif
201fbd08684SStefan Farfeleder return (rval);
2029897c45fSJilles Tjoelker }
203fbd08684SStefan Farfeleder /* Restart at the beginning of the format string. */
204fbd08684SStefan Farfeleder fmt = format;
205fbd08684SStefan Farfeleder end = 1;
206fbd08684SStefan Farfeleder }
207fbd08684SStefan Farfeleder /* NOTREACHED */
208fbd08684SStefan Farfeleder }
209fbd08684SStefan Farfeleder
210fbd08684SStefan Farfeleder
211fbd08684SStefan Farfeleder static char *
printf_doformat(char * fmt,int * rval)212f8a6c905SPedro F. Giffuni printf_doformat(char *fmt, int *rval)
213fbd08684SStefan Farfeleder {
214fbd08684SStefan Farfeleder static const char skip1[] = "#'-+ 0";
215fbd08684SStefan Farfeleder int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
216fbd08684SStefan Farfeleder char convch, nextch;
21776f66be6SPedro F. Giffuni char start[strlen(fmt) + 1];
218f8a6c905SPedro F. Giffuni char **fargv;
219f8a6c905SPedro F. Giffuni char *dptr;
220f8a6c905SPedro F. Giffuni int l;
221fbd08684SStefan Farfeleder
222f8a6c905SPedro F. Giffuni dptr = start;
223f8a6c905SPedro F. Giffuni *dptr++ = '%';
224f8a6c905SPedro F. Giffuni *dptr = 0;
225f8a6c905SPedro F. Giffuni
226f8a6c905SPedro F. Giffuni fmt++;
227b0620803SPedro F. Giffuni
228b0620803SPedro F. Giffuni /* look for "n$" field index specifier */
229f8a6c905SPedro F. Giffuni l = strspn(fmt, digits);
230f8a6c905SPedro F. Giffuni if ((l > 0) && (fmt[l] == '$')) {
231f8a6c905SPedro F. Giffuni int idx = atoi(fmt);
232b0620803SPedro F. Giffuni if (idx <= myargc) {
233b0620803SPedro F. Giffuni gargv = &myargv[idx - 1];
234b0620803SPedro F. Giffuni } else {
235b0620803SPedro F. Giffuni gargv = &myargv[myargc];
236b0620803SPedro F. Giffuni }
237f8a6c905SPedro F. Giffuni if (gargv > maxargv)
238f8a6c905SPedro F. Giffuni maxargv = gargv;
239f8a6c905SPedro F. Giffuni fmt += l + 1;
240f8a6c905SPedro F. Giffuni
241f8a6c905SPedro F. Giffuni /* save format argument */
242f8a6c905SPedro F. Giffuni fargv = gargv;
243b0620803SPedro F. Giffuni } else {
244f8a6c905SPedro F. Giffuni fargv = NULL;
245b0620803SPedro F. Giffuni }
246b0620803SPedro F. Giffuni
2479b50d902SRodney W. Grimes /* skip to field width */
2484aa17924SPedro F. Giffuni while (*fmt && strchr(skip1, *fmt) != NULL) {
249f8a6c905SPedro F. Giffuni *dptr++ = *fmt++;
250f8a6c905SPedro F. Giffuni *dptr = 0;
251f8a6c905SPedro F. Giffuni }
252f8a6c905SPedro F. Giffuni
2539b50d902SRodney W. Grimes if (*fmt == '*') {
254f8a6c905SPedro F. Giffuni
255f8a6c905SPedro F. Giffuni fmt++;
256f8a6c905SPedro F. Giffuni l = strspn(fmt, digits);
257f8a6c905SPedro F. Giffuni if ((l > 0) && (fmt[l] == '$')) {
258f8a6c905SPedro F. Giffuni int idx = atoi(fmt);
2594aa17924SPedro F. Giffuni if (fargv == NULL) {
2604aa17924SPedro F. Giffuni warnx("incomplete use of n$");
2614aa17924SPedro F. Giffuni return (NULL);
2624aa17924SPedro F. Giffuni }
263f8a6c905SPedro F. Giffuni if (idx <= myargc) {
264f8a6c905SPedro F. Giffuni gargv = &myargv[idx - 1];
265f8a6c905SPedro F. Giffuni } else {
266f8a6c905SPedro F. Giffuni gargv = &myargv[myargc];
267f8a6c905SPedro F. Giffuni }
268f8a6c905SPedro F. Giffuni fmt += l + 1;
2694aa17924SPedro F. Giffuni } else if (fargv != NULL) {
2704aa17924SPedro F. Giffuni warnx("incomplete use of n$");
2714aa17924SPedro F. Giffuni return (NULL);
272f8a6c905SPedro F. Giffuni }
273f8a6c905SPedro F. Giffuni
2749b50d902SRodney W. Grimes if (getint(&fieldwidth))
275fbd08684SStefan Farfeleder return (NULL);
276f8a6c905SPedro F. Giffuni if (gargv > maxargv)
277f8a6c905SPedro F. Giffuni maxargv = gargv;
27898dd6386STim J. Robbins havewidth = 1;
279f8a6c905SPedro F. Giffuni
280f8a6c905SPedro F. Giffuni *dptr++ = '*';
281f8a6c905SPedro F. Giffuni *dptr = 0;
282d867cefdSJoerg Wunsch } else {
28398dd6386STim J. Robbins havewidth = 0;
2849b50d902SRodney W. Grimes
2859b50d902SRodney W. Grimes /* skip to possible '.', get following precision */
286f8a6c905SPedro F. Giffuni while (isdigit(*fmt)) {
287f8a6c905SPedro F. Giffuni *dptr++ = *fmt++;
288f8a6c905SPedro F. Giffuni *dptr = 0;
289d867cefdSJoerg Wunsch }
290f8a6c905SPedro F. Giffuni }
291f8a6c905SPedro F. Giffuni
292d867cefdSJoerg Wunsch if (*fmt == '.') {
293d867cefdSJoerg Wunsch /* precision present? */
294f8a6c905SPedro F. Giffuni fmt++;
295f8a6c905SPedro F. Giffuni *dptr++ = '.';
296f8a6c905SPedro F. Giffuni
2979b50d902SRodney W. Grimes if (*fmt == '*') {
298f8a6c905SPedro F. Giffuni
299f8a6c905SPedro F. Giffuni fmt++;
300f8a6c905SPedro F. Giffuni l = strspn(fmt, digits);
301f8a6c905SPedro F. Giffuni if ((l > 0) && (fmt[l] == '$')) {
302f8a6c905SPedro F. Giffuni int idx = atoi(fmt);
3034aa17924SPedro F. Giffuni if (fargv == NULL) {
3044aa17924SPedro F. Giffuni warnx("incomplete use of n$");
3054aa17924SPedro F. Giffuni return (NULL);
3064aa17924SPedro F. Giffuni }
307f8a6c905SPedro F. Giffuni if (idx <= myargc) {
308f8a6c905SPedro F. Giffuni gargv = &myargv[idx - 1];
309f8a6c905SPedro F. Giffuni } else {
310f8a6c905SPedro F. Giffuni gargv = &myargv[myargc];
311f8a6c905SPedro F. Giffuni }
312f8a6c905SPedro F. Giffuni fmt += l + 1;
3134aa17924SPedro F. Giffuni } else if (fargv != NULL) {
3144aa17924SPedro F. Giffuni warnx("incomplete use of n$");
3154aa17924SPedro F. Giffuni return (NULL);
316f8a6c905SPedro F. Giffuni }
317f8a6c905SPedro F. Giffuni
3189b50d902SRodney W. Grimes if (getint(&precision))
319fbd08684SStefan Farfeleder return (NULL);
320f8a6c905SPedro F. Giffuni if (gargv > maxargv)
321f8a6c905SPedro F. Giffuni maxargv = gargv;
32298dd6386STim J. Robbins haveprec = 1;
323f8a6c905SPedro F. Giffuni *dptr++ = '*';
324f8a6c905SPedro F. Giffuni *dptr = 0;
325d867cefdSJoerg Wunsch } else {
32698dd6386STim J. Robbins haveprec = 0;
3279b50d902SRodney W. Grimes
3289b50d902SRodney W. Grimes /* skip to conversion char */
329f8a6c905SPedro F. Giffuni while (isdigit(*fmt)) {
330f8a6c905SPedro F. Giffuni *dptr++ = *fmt++;
331f8a6c905SPedro F. Giffuni *dptr = 0;
332f8a6c905SPedro F. Giffuni }
333d867cefdSJoerg Wunsch }
334d867cefdSJoerg Wunsch } else
33598dd6386STim J. Robbins haveprec = 0;
3369b50d902SRodney W. Grimes if (!*fmt) {
3376a6760dbSJilles Tjoelker warnx("missing format character");
338fbd08684SStefan Farfeleder return (NULL);
3399b50d902SRodney W. Grimes }
340f8a6c905SPedro F. Giffuni *dptr++ = *fmt;
341f8a6c905SPedro F. Giffuni *dptr = 0;
3429b50d902SRodney W. Grimes
343fd757c50SDavid Schultz /*
344fd757c50SDavid Schultz * Look for a length modifier. POSIX doesn't have these, so
345fd757c50SDavid Schultz * we only support them for floating-point conversions, which
346fd757c50SDavid Schultz * are extensions. This is useful because the L modifier can
347fd757c50SDavid Schultz * be used to gain extra range and precision, while omitting
348fd757c50SDavid Schultz * it is more likely to produce consistent results on different
349fd757c50SDavid Schultz * architectures. This is not so important for integers
350fd757c50SDavid Schultz * because overflow is the only bad thing that can happen to
351fd757c50SDavid Schultz * them, but consider the command printf %a 1.1
352fd757c50SDavid Schultz */
353fd757c50SDavid Schultz if (*fmt == 'L') {
354fd757c50SDavid Schultz mod_ldbl = 1;
355fd757c50SDavid Schultz fmt++;
356fd757c50SDavid Schultz if (!strchr("aAeEfFgG", *fmt)) {
3576a6760dbSJilles Tjoelker warnx("bad modifier L for %%%c", *fmt);
358fbd08684SStefan Farfeleder return (NULL);
359fd757c50SDavid Schultz }
360fd757c50SDavid Schultz } else {
361fd757c50SDavid Schultz mod_ldbl = 0;
362fd757c50SDavid Schultz }
363fd757c50SDavid Schultz
364f8a6c905SPedro F. Giffuni /* save the current arg offset, and set to the format arg */
365f8a6c905SPedro F. Giffuni if (fargv != NULL) {
366f8a6c905SPedro F. Giffuni gargv = fargv;
367f8a6c905SPedro F. Giffuni }
368f8a6c905SPedro F. Giffuni
3699b50d902SRodney W. Grimes convch = *fmt;
3709b50d902SRodney W. Grimes nextch = *++fmt;
371f8a6c905SPedro F. Giffuni
3729b50d902SRodney W. Grimes *fmt = '\0';
3739b50d902SRodney W. Grimes switch (convch) {
374ab5a295bSJuli Mallett case 'b': {
3753ec96cafSStefan Farfeleder size_t len;
376ab5a295bSJuli Mallett char *p;
377ab5a295bSJuli Mallett int getout;
378ab5a295bSJuli Mallett
379702da352SPedro F. Giffuni /* Convert "b" to "s" for output. */
380702da352SPedro F. Giffuni start[strlen(start) - 1] = 's';
381702da352SPedro F. Giffuni if ((p = strdup(getstr())) == NULL) {
3826a6760dbSJilles Tjoelker warnx("%s", strerror(ENOMEM));
383fbd08684SStefan Farfeleder return (NULL);
384bacab7d6STim J. Robbins }
3853ec96cafSStefan Farfeleder getout = escape(p, 0, &len);
386702da352SPedro F. Giffuni PF(start, p);
387702da352SPedro F. Giffuni /* Restore format for next loop. */
388702da352SPedro F. Giffuni
389ab5a295bSJuli Mallett free(p);
390ab5a295bSJuli Mallett if (getout)
391*051c7fddSJilles Tjoelker return (end_fmt);
392ab5a295bSJuli Mallett break;
393ab5a295bSJuli Mallett }
3949b50d902SRodney W. Grimes case 'c': {
3959b50d902SRodney W. Grimes char p;
3969b50d902SRodney W. Grimes
3979b50d902SRodney W. Grimes p = getchr();
398342b089bSJilles Tjoelker if (p != '\0')
3999b50d902SRodney W. Grimes PF(start, p);
4009b50d902SRodney W. Grimes break;
4019b50d902SRodney W. Grimes }
4029b50d902SRodney W. Grimes case 's': {
40345af1a4cSDavid Malone const char *p;
4049b50d902SRodney W. Grimes
4059b50d902SRodney W. Grimes p = getstr();
4069b50d902SRodney W. Grimes PF(start, p);
4079b50d902SRodney W. Grimes break;
4089b50d902SRodney W. Grimes }
4099b50d902SRodney W. Grimes case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
4109b50d902SRodney W. Grimes char *f;
411d41d23e1SStefan Farfeleder intmax_t val;
412d41d23e1SStefan Farfeleder uintmax_t uval;
413bacab7d6STim J. Robbins int signedconv;
4149b50d902SRodney W. Grimes
415bacab7d6STim J. Robbins signedconv = (convch == 'd' || convch == 'i');
416d41d23e1SStefan Farfeleder if ((f = mknum(start, convch)) == NULL)
417fbd08684SStefan Farfeleder return (NULL);
418d41d23e1SStefan Farfeleder if (getnum(&val, &uval, signedconv))
419fbd08684SStefan Farfeleder *rval = 1;
420bacab7d6STim J. Robbins if (signedconv)
421bacab7d6STim J. Robbins PF(f, val);
422bacab7d6STim J. Robbins else
423bacab7d6STim J. Robbins PF(f, uval);
4249b50d902SRodney W. Grimes break;
4259b50d902SRodney W. Grimes }
42603b2eaacSDavid Schultz case 'e': case 'E':
42703b2eaacSDavid Schultz case 'f': case 'F':
42803b2eaacSDavid Schultz case 'g': case 'G':
42903b2eaacSDavid Schultz case 'a': case 'A': {
430fd757c50SDavid Schultz long double p;
4319b50d902SRodney W. Grimes
432fd757c50SDavid Schultz if (getfloating(&p, mod_ldbl))
433fbd08684SStefan Farfeleder *rval = 1;
434fd757c50SDavid Schultz if (mod_ldbl)
4359b50d902SRodney W. Grimes PF(start, p);
436fd757c50SDavid Schultz else
437fd757c50SDavid Schultz PF(start, (double)p);
4389b50d902SRodney W. Grimes break;
4399b50d902SRodney W. Grimes }
4409b50d902SRodney W. Grimes default:
4416a6760dbSJilles Tjoelker warnx("illegal format character %c", convch);
442fbd08684SStefan Farfeleder return (NULL);
4439b50d902SRodney W. Grimes }
4449b50d902SRodney W. Grimes *fmt = nextch;
445f8a6c905SPedro F. Giffuni /* return the gargv to the next element */
446fbd08684SStefan Farfeleder return (fmt);
4479b50d902SRodney W. Grimes }
4489b50d902SRodney W. Grimes
4499b50d902SRodney W. Grimes static char *
mknum(char * str,char ch)4509180853bSXin LI mknum(char *str, char ch)
4519b50d902SRodney W. Grimes {
4523c6e4a5cSBen Smithurst static char *copy;
4533c6e4a5cSBen Smithurst static size_t copy_size;
4543c6e4a5cSBen Smithurst char *newcopy;
455bacab7d6STim J. Robbins size_t len, newlen;
4569b50d902SRodney W. Grimes
4579b50d902SRodney W. Grimes len = strlen(str) + 2;
4583c6e4a5cSBen Smithurst if (len > copy_size) {
4593c6e4a5cSBen Smithurst newlen = ((len + 1023) >> 10) << 10;
460ea1a630aSPedro F. Giffuni if ((newcopy = realloc(copy, newlen)) == NULL) {
4616a6760dbSJilles Tjoelker warnx("%s", strerror(ENOMEM));
4623c6e4a5cSBen Smithurst return (NULL);
463bacab7d6STim J. Robbins }
4643c6e4a5cSBen Smithurst copy = newcopy;
4653c6e4a5cSBen Smithurst copy_size = newlen;
4663c6e4a5cSBen Smithurst }
46762a721e7SStefan Eßer
4689b50d902SRodney W. Grimes memmove(copy, str, len - 3);
469d41d23e1SStefan Farfeleder copy[len - 3] = 'j';
4709b50d902SRodney W. Grimes copy[len - 2] = ch;
4719b50d902SRodney W. Grimes copy[len - 1] = '\0';
4729b50d902SRodney W. Grimes return (copy);
4739b50d902SRodney W. Grimes }
4749b50d902SRodney W. Grimes
475ab5a295bSJuli Mallett static int
escape(char * fmt,int percent,size_t * len)4763ec96cafSStefan Farfeleder escape(char *fmt, int percent, size_t *len)
4779b50d902SRodney W. Grimes {
478ab71f271SPedro F. Giffuni char *save, *store, c;
479ab71f271SPedro F. Giffuni int value;
4809b50d902SRodney W. Grimes
481ab71f271SPedro F. Giffuni for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
4829b50d902SRodney W. Grimes if (c != '\\') {
4839b50d902SRodney W. Grimes *store = c;
4849b50d902SRodney W. Grimes continue;
4859b50d902SRodney W. Grimes }
4869b50d902SRodney W. Grimes switch (*++fmt) {
4879b50d902SRodney W. Grimes case '\0': /* EOS, user error */
4889b50d902SRodney W. Grimes *store = '\\';
4899b50d902SRodney W. Grimes *++store = '\0';
4903ec96cafSStefan Farfeleder *len = store - save;
491ab5a295bSJuli Mallett return (0);
4929b50d902SRodney W. Grimes case '\\': /* backslash */
4939b50d902SRodney W. Grimes case '\'': /* single quote */
4949b50d902SRodney W. Grimes *store = *fmt;
4959b50d902SRodney W. Grimes break;
4969b50d902SRodney W. Grimes case 'a': /* bell/alert */
497f3f148d2SStefan Farfeleder *store = '\a';
4989b50d902SRodney W. Grimes break;
4999b50d902SRodney W. Grimes case 'b': /* backspace */
5009b50d902SRodney W. Grimes *store = '\b';
5019b50d902SRodney W. Grimes break;
502ab5a295bSJuli Mallett case 'c':
503437bce62SPedro F. Giffuni if (!percent) {
504ab5a295bSJuli Mallett *store = '\0';
5053ec96cafSStefan Farfeleder *len = store - save;
506ab5a295bSJuli Mallett return (1);
507437bce62SPedro F. Giffuni }
508437bce62SPedro F. Giffuni *store = 'c';
509437bce62SPedro F. Giffuni break;
5109b50d902SRodney W. Grimes case 'f': /* form-feed */
5119b50d902SRodney W. Grimes *store = '\f';
5129b50d902SRodney W. Grimes break;
5139b50d902SRodney W. Grimes case 'n': /* newline */
5149b50d902SRodney W. Grimes *store = '\n';
5159b50d902SRodney W. Grimes break;
5169b50d902SRodney W. Grimes case 'r': /* carriage-return */
5179b50d902SRodney W. Grimes *store = '\r';
5189b50d902SRodney W. Grimes break;
5199b50d902SRodney W. Grimes case 't': /* horizontal tab */
5209b50d902SRodney W. Grimes *store = '\t';
5219b50d902SRodney W. Grimes break;
5229b50d902SRodney W. Grimes case 'v': /* vertical tab */
523f3f148d2SStefan Farfeleder *store = '\v';
5249b50d902SRodney W. Grimes break;
5259b50d902SRodney W. Grimes /* octal constant */
5269b50d902SRodney W. Grimes case '0': case '1': case '2': case '3':
5279b50d902SRodney W. Grimes case '4': case '5': case '6': case '7':
5289d65050eSDavid Schultz c = (!percent && *fmt == '0') ? 4 : 3;
5299d65050eSDavid Schultz for (value = 0;
5309b50d902SRodney W. Grimes c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
5319b50d902SRodney W. Grimes value <<= 3;
5329b50d902SRodney W. Grimes value += *fmt - '0';
5339b50d902SRodney W. Grimes }
5349b50d902SRodney W. Grimes --fmt;
53512e8db40STim J. Robbins if (percent && value == '%') {
53637fd4590STim J. Robbins *store++ = '%';
53737fd4590STim J. Robbins *store = '%';
53837fd4590STim J. Robbins } else
539ab71f271SPedro F. Giffuni *store = (char)value;
5409b50d902SRodney W. Grimes break;
5419b50d902SRodney W. Grimes default:
5429b50d902SRodney W. Grimes *store = *fmt;
5439b50d902SRodney W. Grimes break;
5449b50d902SRodney W. Grimes }
5459b50d902SRodney W. Grimes }
5469b50d902SRodney W. Grimes *store = '\0';
5473ec96cafSStefan Farfeleder *len = store - save;
548ab5a295bSJuli Mallett return (0);
5499b50d902SRodney W. Grimes }
5509b50d902SRodney W. Grimes
5519b50d902SRodney W. Grimes static int
getchr(void)552f4ac32deSDavid Malone getchr(void)
5539b50d902SRodney W. Grimes {
5549b50d902SRodney W. Grimes if (!*gargv)
5559b50d902SRodney W. Grimes return ('\0');
5569b50d902SRodney W. Grimes return ((int)**gargv++);
5579b50d902SRodney W. Grimes }
5589b50d902SRodney W. Grimes
55945af1a4cSDavid Malone static const char *
getstr(void)560f4ac32deSDavid Malone getstr(void)
5619b50d902SRodney W. Grimes {
5629b50d902SRodney W. Grimes if (!*gargv)
5639b50d902SRodney W. Grimes return ("");
5649b50d902SRodney W. Grimes return (*gargv++);
5659b50d902SRodney W. Grimes }
5669b50d902SRodney W. Grimes
5679b50d902SRodney W. Grimes static int
getint(int * ip)568f4ac32deSDavid Malone getint(int *ip)
5699b50d902SRodney W. Grimes {
570d41d23e1SStefan Farfeleder intmax_t val;
571d41d23e1SStefan Farfeleder uintmax_t uval;
572bacab7d6STim J. Robbins int rval;
5739b50d902SRodney W. Grimes
574d41d23e1SStefan Farfeleder if (getnum(&val, &uval, 1))
5759b50d902SRodney W. Grimes return (1);
576bacab7d6STim J. Robbins rval = 0;
577bacab7d6STim J. Robbins if (val < INT_MIN || val > INT_MAX) {
5786a6760dbSJilles Tjoelker warnx("%s: %s", *gargv, strerror(ERANGE));
579bacab7d6STim J. Robbins rval = 1;
580bacab7d6STim J. Robbins }
58162a721e7SStefan Eßer *ip = (int)val;
582bacab7d6STim J. Robbins return (rval);
5839b50d902SRodney W. Grimes }
5849b50d902SRodney W. Grimes
5859b50d902SRodney W. Grimes static int
getnum(intmax_t * ip,uintmax_t * uip,int signedconv)586d41d23e1SStefan Farfeleder getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
5879b50d902SRodney W. Grimes {
5889b50d902SRodney W. Grimes char *ep;
589bacab7d6STim J. Robbins int rval;
5909b50d902SRodney W. Grimes
5919b50d902SRodney W. Grimes if (!*gargv) {
5924e4d9802SJilles Tjoelker *ip = *uip = 0;
5939b50d902SRodney W. Grimes return (0);
5949b50d902SRodney W. Grimes }
595ab5a295bSJuli Mallett if (**gargv == '"' || **gargv == '\'') {
596bacab7d6STim J. Robbins if (signedconv)
597d41d23e1SStefan Farfeleder *ip = asciicode();
598bacab7d6STim J. Robbins else
599d41d23e1SStefan Farfeleder *uip = asciicode();
6009b50d902SRodney W. Grimes return (0);
6019b50d902SRodney W. Grimes }
602bacab7d6STim J. Robbins rval = 0;
603ab5a295bSJuli Mallett errno = 0;
604bacab7d6STim J. Robbins if (signedconv)
605d41d23e1SStefan Farfeleder *ip = strtoimax(*gargv, &ep, 0);
606bacab7d6STim J. Robbins else
607d41d23e1SStefan Farfeleder *uip = strtoumax(*gargv, &ep, 0);
608bacab7d6STim J. Robbins if (ep == *gargv) {
6096a6760dbSJilles Tjoelker warnx("%s: expected numeric value", *gargv);
610bacab7d6STim J. Robbins rval = 1;
611bacab7d6STim J. Robbins }
612bacab7d6STim J. Robbins else if (*ep != '\0') {
6136a6760dbSJilles Tjoelker warnx("%s: not completely converted", *gargv);
614bacab7d6STim J. Robbins rval = 1;
615bacab7d6STim J. Robbins }
616bacab7d6STim J. Robbins if (errno == ERANGE) {
6176a6760dbSJilles Tjoelker warnx("%s: %s", *gargv, strerror(ERANGE));
618bacab7d6STim J. Robbins rval = 1;
619bacab7d6STim J. Robbins }
620ab5a295bSJuli Mallett ++gargv;
621bacab7d6STim J. Robbins return (rval);
6229b50d902SRodney W. Grimes }
6239b50d902SRodney W. Grimes
624bacab7d6STim J. Robbins static int
getfloating(long double * dp,int mod_ldbl)625fd757c50SDavid Schultz getfloating(long double *dp, int mod_ldbl)
6269b50d902SRodney W. Grimes {
627ab5a295bSJuli Mallett char *ep;
628bacab7d6STim J. Robbins int rval;
629ab5a295bSJuli Mallett
6305ec2b8dcSStefan Farfeleder if (!*gargv) {
6315ec2b8dcSStefan Farfeleder *dp = 0.0;
632bacab7d6STim J. Robbins return (0);
6335ec2b8dcSStefan Farfeleder }
634ab5a295bSJuli Mallett if (**gargv == '"' || **gargv == '\'') {
635bacab7d6STim J. Robbins *dp = asciicode();
636bacab7d6STim J. Robbins return (0);
637ab5a295bSJuli Mallett }
6388c423a99SColin Percival rval = 0;
639ab5a295bSJuli Mallett errno = 0;
640fd757c50SDavid Schultz if (mod_ldbl)
641fd757c50SDavid Schultz *dp = strtold(*gargv, &ep);
642fd757c50SDavid Schultz else
643bacab7d6STim J. Robbins *dp = strtod(*gargv, &ep);
644bacab7d6STim J. Robbins if (ep == *gargv) {
6456a6760dbSJilles Tjoelker warnx("%s: expected numeric value", *gargv);
646bacab7d6STim J. Robbins rval = 1;
647bacab7d6STim J. Robbins } else if (*ep != '\0') {
6486a6760dbSJilles Tjoelker warnx("%s: not completely converted", *gargv);
649bacab7d6STim J. Robbins rval = 1;
650bacab7d6STim J. Robbins }
651bacab7d6STim J. Robbins if (errno == ERANGE) {
6526a6760dbSJilles Tjoelker warnx("%s: %s", *gargv, strerror(ERANGE));
653bacab7d6STim J. Robbins rval = 1;
654bacab7d6STim J. Robbins }
655ab5a295bSJuli Mallett ++gargv;
656bacab7d6STim J. Robbins return (rval);
6579b50d902SRodney W. Grimes }
6589b50d902SRodney W. Grimes
6599b50d902SRodney W. Grimes static int
asciicode(void)660f4ac32deSDavid Malone asciicode(void)
6619b50d902SRodney W. Grimes {
662f4ac32deSDavid Malone int ch;
66398102dabSJilles Tjoelker wchar_t wch;
66498102dabSJilles Tjoelker mbstate_t mbs;
6659b50d902SRodney W. Grimes
66698102dabSJilles Tjoelker ch = (unsigned char)**gargv;
66798102dabSJilles Tjoelker if (ch == '\'' || ch == '"') {
66898102dabSJilles Tjoelker memset(&mbs, 0, sizeof(mbs));
66998102dabSJilles Tjoelker switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
67098102dabSJilles Tjoelker case (size_t)-2:
67198102dabSJilles Tjoelker case (size_t)-1:
67298102dabSJilles Tjoelker wch = (unsigned char)gargv[0][1];
67398102dabSJilles Tjoelker break;
67498102dabSJilles Tjoelker case 0:
67598102dabSJilles Tjoelker wch = 0;
67698102dabSJilles Tjoelker break;
67798102dabSJilles Tjoelker }
67898102dabSJilles Tjoelker ch = wch;
67998102dabSJilles Tjoelker }
6809b50d902SRodney W. Grimes ++gargv;
6819b50d902SRodney W. Grimes return (ch);
6829b50d902SRodney W. Grimes }
6839b50d902SRodney W. Grimes
6849b50d902SRodney W. Grimes static void
usage(void)685f4ac32deSDavid Malone usage(void)
6869b50d902SRodney W. Grimes {
687f682f10cSRuslan Ermilov (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
6889b50d902SRodney W. Grimes }
689