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
519b50d902SRodney W. Grimes #endif /* not lint */
529b50d902SRodney W. Grimes
539b50d902SRodney W. Grimes #include <sys/types.h>
549b50d902SRodney W. Grimes
55f8a6c905SPedro F. Giffuni #include <ctype.h>
569b50d902SRodney W. Grimes #include <err.h>
579b50d902SRodney W. Grimes #include <errno.h>
58d41d23e1SStefan Farfeleder #include <inttypes.h>
599b50d902SRodney W. Grimes #include <limits.h>
60c9aca921SXin LI #include <locale.h>
619b50d902SRodney W. Grimes #include <stdio.h>
629b50d902SRodney W. Grimes #include <stdlib.h>
639b50d902SRodney W. Grimes #include <string.h>
649d19feb5SSteve Price #include <unistd.h>
6598102dabSJilles Tjoelker #include <wchar.h>
669b50d902SRodney W. Grimes
679b50d902SRodney W. Grimes #ifdef SHELL
689b50d902SRodney W. Grimes #define main printfcmd
69d9f93710SJoerg Wunsch #include "bltin/bltin.h"
707cbda738SJilles Tjoelker #include "options.h"
719b50d902SRodney W. Grimes #endif
729b50d902SRodney W. Grimes
73bacab7d6STim J. Robbins #define PF(f, func) do { \
7498dd6386STim J. Robbins if (havewidth) \
7598dd6386STim J. Robbins if (haveprec) \
76342b089bSJilles Tjoelker (void)printf(f, fieldwidth, precision, func); \
779b50d902SRodney W. Grimes else \
78342b089bSJilles Tjoelker (void)printf(f, fieldwidth, func); \
7998dd6386STim J. Robbins else if (haveprec) \
80342b089bSJilles Tjoelker (void)printf(f, precision, func); \
819b50d902SRodney W. Grimes else \
82342b089bSJilles Tjoelker (void)printf(f, func); \
83bacab7d6STim J. Robbins } while (0)
849b50d902SRodney W. Grimes
85d3cb5dedSWarner Losh static int asciicode(void);
869897c45fSJilles Tjoelker static char *printf_doformat(char *, int *);
873ec96cafSStefan Farfeleder static int escape(char *, int, size_t *);
88d3cb5dedSWarner Losh static int getchr(void);
89fd757c50SDavid Schultz static int getfloating(long double *, int);
90d3cb5dedSWarner Losh static int getint(int *);
91d41d23e1SStefan Farfeleder static int getnum(intmax_t *, uintmax_t *, int);
92bacab7d6STim J. Robbins static const char
93bacab7d6STim J. Robbins *getstr(void);
949180853bSXin LI static char *mknum(char *, char);
95d3cb5dedSWarner Losh static void usage(void);
969b50d902SRodney W. Grimes
97f8a6c905SPedro F. Giffuni static const char digits[] = "0123456789";
98f8a6c905SPedro F. Giffuni
9930238f49SPedro F. Giffuni static char end_fmt[1];
10030238f49SPedro F. Giffuni
101b0620803SPedro F. Giffuni static int myargc;
102b0620803SPedro F. Giffuni static char **myargv;
1039b50d902SRodney W. Grimes static char **gargv;
104f8a6c905SPedro F. Giffuni static char **maxargv;
1059b50d902SRodney W. Grimes
1069b50d902SRodney W. Grimes int
main(int argc,char * argv[])107f4ac32deSDavid Malone main(int argc, char *argv[])
1089b50d902SRodney W. Grimes {
1093ec96cafSStefan Farfeleder size_t len;
110437bce62SPedro F. Giffuni int end, rval;
111fbd08684SStefan Farfeleder char *format, *fmt, *start;
1121866e8abSJilles Tjoelker #ifndef SHELL
1137cbda738SJilles Tjoelker int ch;
1147cbda738SJilles Tjoelker
11569be0c5eSXin LI (void) setlocale(LC_ALL, "");
116dc7d8c99SAndrey A. Chernov #endif
1177cbda738SJilles Tjoelker
1189897c45fSJilles Tjoelker #ifdef SHELL
1197cbda738SJilles Tjoelker nextopt("");
1207cbda738SJilles Tjoelker argc -= argptr - argv;
1217cbda738SJilles Tjoelker argv = argptr;
1227cbda738SJilles Tjoelker #else
123de46de4dSXin LI while ((ch = getopt(argc, argv, "")) != -1)
124de46de4dSXin LI switch (ch) {
125de46de4dSXin LI case '?':
126de46de4dSXin LI default:
127de46de4dSXin LI usage();
128de46de4dSXin LI return (1);
1299b50d902SRodney W. Grimes }
130de46de4dSXin LI argc -= optind;
131de46de4dSXin LI argv += optind;
1327cbda738SJilles Tjoelker #endif
1339b50d902SRodney W. Grimes
1349b50d902SRodney W. Grimes if (argc < 1) {
1359b50d902SRodney W. Grimes usage();
1365eccc000SXin LI return (1);
1379b50d902SRodney W. Grimes }
1389b50d902SRodney W. Grimes
1399897c45fSJilles Tjoelker #ifdef SHELL
1409897c45fSJilles Tjoelker INTOFF;
1419897c45fSJilles Tjoelker #endif
1429b50d902SRodney W. Grimes /*
1439b50d902SRodney W. Grimes * Basic algorithm is to scan the format string for conversion
1449b50d902SRodney W. Grimes * specifications -- once one is found, find out if the field
1459b50d902SRodney W. Grimes * width or precision is a '*'; if it is, gather up value. Note,
1469b50d902SRodney W. Grimes * format strings are reused as necessary to use up the provided
1479b50d902SRodney W. Grimes * arguments, arguments of zero/null string are provided to use
1489b50d902SRodney W. Grimes * up the format string.
1499b50d902SRodney W. Grimes */
1503ec96cafSStefan Farfeleder fmt = format = *argv;
151437bce62SPedro F. Giffuni escape(fmt, 1, &len); /* backslash interpretation */
152fbd08684SStefan Farfeleder rval = end = 0;
1539b50d902SRodney W. Grimes gargv = ++argv;
154b0620803SPedro F. Giffuni
1559b50d902SRodney W. Grimes for (;;) {
156f8a6c905SPedro F. Giffuni maxargv = gargv;
157b0620803SPedro F. Giffuni
158b0620803SPedro F. Giffuni myargv = gargv;
159b0620803SPedro F. Giffuni for (myargc = 0; gargv[myargc]; myargc++)
160b0620803SPedro F. Giffuni /* nop */;
161fbd08684SStefan Farfeleder start = fmt;
1623ec96cafSStefan Farfeleder while (fmt < format + len) {
163fbd08684SStefan Farfeleder if (fmt[0] == '%') {
164fbd08684SStefan Farfeleder fwrite(start, 1, fmt - start, stdout);
165fbd08684SStefan Farfeleder if (fmt[1] == '%') {
1669b50d902SRodney W. Grimes /* %% prints a % */
167fbd08684SStefan Farfeleder putchar('%');
168fbd08684SStefan Farfeleder fmt += 2;
169fbd08684SStefan Farfeleder } else {
1709897c45fSJilles Tjoelker fmt = printf_doformat(fmt, &rval);
17130238f49SPedro F. Giffuni if (fmt == NULL || fmt == end_fmt) {
1729897c45fSJilles Tjoelker #ifdef SHELL
1739897c45fSJilles Tjoelker INTON;
1749897c45fSJilles Tjoelker #endif
17530238f49SPedro F. Giffuni return (fmt == NULL ? 1 : rval);
1769897c45fSJilles Tjoelker }
177fbd08684SStefan Farfeleder end = 0;
1789b50d902SRodney W. Grimes }
179fbd08684SStefan Farfeleder start = fmt;
180fbd08684SStefan Farfeleder } else
181fbd08684SStefan Farfeleder fmt++;
182b0620803SPedro F. Giffuni if (gargv > maxargv)
183b0620803SPedro F. Giffuni maxargv = gargv;
1849b50d902SRodney W. Grimes }
185b0620803SPedro F. Giffuni gargv = maxargv;
1869b50d902SRodney W. Grimes
187fbd08684SStefan Farfeleder if (end == 1) {
1886a6760dbSJilles Tjoelker warnx("missing format character");
1899897c45fSJilles Tjoelker #ifdef SHELL
1909897c45fSJilles Tjoelker INTON;
1919897c45fSJilles Tjoelker #endif
192fbd08684SStefan Farfeleder return (1);
193fbd08684SStefan Farfeleder }
194fbd08684SStefan Farfeleder fwrite(start, 1, fmt - start, stdout);
195437bce62SPedro F. Giffuni if (!*gargv) {
1969897c45fSJilles Tjoelker #ifdef SHELL
1979897c45fSJilles Tjoelker INTON;
1989897c45fSJilles Tjoelker #endif
199fbd08684SStefan Farfeleder return (rval);
2009897c45fSJilles Tjoelker }
201fbd08684SStefan Farfeleder /* Restart at the beginning of the format string. */
202fbd08684SStefan Farfeleder fmt = format;
203fbd08684SStefan Farfeleder end = 1;
204fbd08684SStefan Farfeleder }
205fbd08684SStefan Farfeleder /* NOTREACHED */
206fbd08684SStefan Farfeleder }
207fbd08684SStefan Farfeleder
208fbd08684SStefan Farfeleder
209fbd08684SStefan Farfeleder static char *
printf_doformat(char * fmt,int * rval)210f8a6c905SPedro F. Giffuni printf_doformat(char *fmt, int *rval)
211fbd08684SStefan Farfeleder {
212fbd08684SStefan Farfeleder static const char skip1[] = "#'-+ 0";
213fbd08684SStefan Farfeleder int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
214fbd08684SStefan Farfeleder char convch, nextch;
21576f66be6SPedro F. Giffuni char start[strlen(fmt) + 1];
216f8a6c905SPedro F. Giffuni char **fargv;
217f8a6c905SPedro F. Giffuni char *dptr;
218f8a6c905SPedro F. Giffuni int l;
219fbd08684SStefan Farfeleder
220f8a6c905SPedro F. Giffuni dptr = start;
221f8a6c905SPedro F. Giffuni *dptr++ = '%';
222f8a6c905SPedro F. Giffuni *dptr = 0;
223f8a6c905SPedro F. Giffuni
224f8a6c905SPedro F. Giffuni fmt++;
225b0620803SPedro F. Giffuni
226b0620803SPedro F. Giffuni /* look for "n$" field index specifier */
227f8a6c905SPedro F. Giffuni l = strspn(fmt, digits);
228f8a6c905SPedro F. Giffuni if ((l > 0) && (fmt[l] == '$')) {
229f8a6c905SPedro F. Giffuni int idx = atoi(fmt);
230b0620803SPedro F. Giffuni if (idx <= myargc) {
231b0620803SPedro F. Giffuni gargv = &myargv[idx - 1];
232b0620803SPedro F. Giffuni } else {
233b0620803SPedro F. Giffuni gargv = &myargv[myargc];
234b0620803SPedro F. Giffuni }
235f8a6c905SPedro F. Giffuni if (gargv > maxargv)
236f8a6c905SPedro F. Giffuni maxargv = gargv;
237f8a6c905SPedro F. Giffuni fmt += l + 1;
238f8a6c905SPedro F. Giffuni
239f8a6c905SPedro F. Giffuni /* save format argument */
240f8a6c905SPedro F. Giffuni fargv = gargv;
241b0620803SPedro F. Giffuni } else {
242f8a6c905SPedro F. Giffuni fargv = NULL;
243b0620803SPedro F. Giffuni }
244b0620803SPedro F. Giffuni
2459b50d902SRodney W. Grimes /* skip to field width */
2464aa17924SPedro F. Giffuni while (*fmt && strchr(skip1, *fmt) != NULL) {
247f8a6c905SPedro F. Giffuni *dptr++ = *fmt++;
248f8a6c905SPedro F. Giffuni *dptr = 0;
249f8a6c905SPedro F. Giffuni }
250f8a6c905SPedro F. Giffuni
2519b50d902SRodney W. Grimes if (*fmt == '*') {
252f8a6c905SPedro F. Giffuni
253f8a6c905SPedro F. Giffuni fmt++;
254f8a6c905SPedro F. Giffuni l = strspn(fmt, digits);
255f8a6c905SPedro F. Giffuni if ((l > 0) && (fmt[l] == '$')) {
256f8a6c905SPedro F. Giffuni int idx = atoi(fmt);
2574aa17924SPedro F. Giffuni if (fargv == NULL) {
2584aa17924SPedro F. Giffuni warnx("incomplete use of n$");
2594aa17924SPedro F. Giffuni return (NULL);
2604aa17924SPedro F. Giffuni }
261f8a6c905SPedro F. Giffuni if (idx <= myargc) {
262f8a6c905SPedro F. Giffuni gargv = &myargv[idx - 1];
263f8a6c905SPedro F. Giffuni } else {
264f8a6c905SPedro F. Giffuni gargv = &myargv[myargc];
265f8a6c905SPedro F. Giffuni }
266f8a6c905SPedro F. Giffuni fmt += l + 1;
2674aa17924SPedro F. Giffuni } else if (fargv != NULL) {
2684aa17924SPedro F. Giffuni warnx("incomplete use of n$");
2694aa17924SPedro F. Giffuni return (NULL);
270f8a6c905SPedro F. Giffuni }
271f8a6c905SPedro F. Giffuni
2729b50d902SRodney W. Grimes if (getint(&fieldwidth))
273fbd08684SStefan Farfeleder return (NULL);
274f8a6c905SPedro F. Giffuni if (gargv > maxargv)
275f8a6c905SPedro F. Giffuni maxargv = gargv;
27698dd6386STim J. Robbins havewidth = 1;
277f8a6c905SPedro F. Giffuni
278f8a6c905SPedro F. Giffuni *dptr++ = '*';
279f8a6c905SPedro F. Giffuni *dptr = 0;
280d867cefdSJoerg Wunsch } else {
28198dd6386STim J. Robbins havewidth = 0;
2829b50d902SRodney W. Grimes
2839b50d902SRodney W. Grimes /* skip to possible '.', get following precision */
284f8a6c905SPedro F. Giffuni while (isdigit(*fmt)) {
285f8a6c905SPedro F. Giffuni *dptr++ = *fmt++;
286f8a6c905SPedro F. Giffuni *dptr = 0;
287d867cefdSJoerg Wunsch }
288f8a6c905SPedro F. Giffuni }
289f8a6c905SPedro F. Giffuni
290d867cefdSJoerg Wunsch if (*fmt == '.') {
291d867cefdSJoerg Wunsch /* precision present? */
292f8a6c905SPedro F. Giffuni fmt++;
293f8a6c905SPedro F. Giffuni *dptr++ = '.';
294f8a6c905SPedro F. Giffuni
2959b50d902SRodney W. Grimes if (*fmt == '*') {
296f8a6c905SPedro F. Giffuni
297f8a6c905SPedro F. Giffuni fmt++;
298f8a6c905SPedro F. Giffuni l = strspn(fmt, digits);
299f8a6c905SPedro F. Giffuni if ((l > 0) && (fmt[l] == '$')) {
300f8a6c905SPedro F. Giffuni int idx = atoi(fmt);
3014aa17924SPedro F. Giffuni if (fargv == NULL) {
3024aa17924SPedro F. Giffuni warnx("incomplete use of n$");
3034aa17924SPedro F. Giffuni return (NULL);
3044aa17924SPedro F. Giffuni }
305f8a6c905SPedro F. Giffuni if (idx <= myargc) {
306f8a6c905SPedro F. Giffuni gargv = &myargv[idx - 1];
307f8a6c905SPedro F. Giffuni } else {
308f8a6c905SPedro F. Giffuni gargv = &myargv[myargc];
309f8a6c905SPedro F. Giffuni }
310f8a6c905SPedro F. Giffuni fmt += l + 1;
3114aa17924SPedro F. Giffuni } else if (fargv != NULL) {
3124aa17924SPedro F. Giffuni warnx("incomplete use of n$");
3134aa17924SPedro F. Giffuni return (NULL);
314f8a6c905SPedro F. Giffuni }
315f8a6c905SPedro F. Giffuni
3169b50d902SRodney W. Grimes if (getint(&precision))
317fbd08684SStefan Farfeleder return (NULL);
318f8a6c905SPedro F. Giffuni if (gargv > maxargv)
319f8a6c905SPedro F. Giffuni maxargv = gargv;
32098dd6386STim J. Robbins haveprec = 1;
321f8a6c905SPedro F. Giffuni *dptr++ = '*';
322f8a6c905SPedro F. Giffuni *dptr = 0;
323d867cefdSJoerg Wunsch } else {
32498dd6386STim J. Robbins haveprec = 0;
3259b50d902SRodney W. Grimes
3269b50d902SRodney W. Grimes /* skip to conversion char */
327f8a6c905SPedro F. Giffuni while (isdigit(*fmt)) {
328f8a6c905SPedro F. Giffuni *dptr++ = *fmt++;
329f8a6c905SPedro F. Giffuni *dptr = 0;
330f8a6c905SPedro F. Giffuni }
331d867cefdSJoerg Wunsch }
332d867cefdSJoerg Wunsch } else
33398dd6386STim J. Robbins haveprec = 0;
3349b50d902SRodney W. Grimes if (!*fmt) {
3356a6760dbSJilles Tjoelker warnx("missing format character");
336fbd08684SStefan Farfeleder return (NULL);
3379b50d902SRodney W. Grimes }
338f8a6c905SPedro F. Giffuni *dptr++ = *fmt;
339f8a6c905SPedro F. Giffuni *dptr = 0;
3409b50d902SRodney W. Grimes
341fd757c50SDavid Schultz /*
342fd757c50SDavid Schultz * Look for a length modifier. POSIX doesn't have these, so
343fd757c50SDavid Schultz * we only support them for floating-point conversions, which
344fd757c50SDavid Schultz * are extensions. This is useful because the L modifier can
345fd757c50SDavid Schultz * be used to gain extra range and precision, while omitting
346fd757c50SDavid Schultz * it is more likely to produce consistent results on different
347fd757c50SDavid Schultz * architectures. This is not so important for integers
348fd757c50SDavid Schultz * because overflow is the only bad thing that can happen to
349fd757c50SDavid Schultz * them, but consider the command printf %a 1.1
350fd757c50SDavid Schultz */
351fd757c50SDavid Schultz if (*fmt == 'L') {
352fd757c50SDavid Schultz mod_ldbl = 1;
353fd757c50SDavid Schultz fmt++;
354fd757c50SDavid Schultz if (!strchr("aAeEfFgG", *fmt)) {
3556a6760dbSJilles Tjoelker warnx("bad modifier L for %%%c", *fmt);
356fbd08684SStefan Farfeleder return (NULL);
357fd757c50SDavid Schultz }
358fd757c50SDavid Schultz } else {
359fd757c50SDavid Schultz mod_ldbl = 0;
360fd757c50SDavid Schultz }
361fd757c50SDavid Schultz
362f8a6c905SPedro F. Giffuni /* save the current arg offset, and set to the format arg */
363f8a6c905SPedro F. Giffuni if (fargv != NULL) {
364f8a6c905SPedro F. Giffuni gargv = fargv;
365f8a6c905SPedro F. Giffuni }
366f8a6c905SPedro F. Giffuni
3679b50d902SRodney W. Grimes convch = *fmt;
3689b50d902SRodney W. Grimes nextch = *++fmt;
369f8a6c905SPedro F. Giffuni
3709b50d902SRodney W. Grimes *fmt = '\0';
3719b50d902SRodney W. Grimes switch (convch) {
372ab5a295bSJuli Mallett case 'b': {
3733ec96cafSStefan Farfeleder size_t len;
374ab5a295bSJuli Mallett char *p;
375ab5a295bSJuli Mallett int getout;
376ab5a295bSJuli Mallett
377702da352SPedro F. Giffuni /* Convert "b" to "s" for output. */
378702da352SPedro F. Giffuni start[strlen(start) - 1] = 's';
379702da352SPedro F. Giffuni if ((p = strdup(getstr())) == NULL) {
3806a6760dbSJilles Tjoelker warnx("%s", strerror(ENOMEM));
381fbd08684SStefan Farfeleder return (NULL);
382bacab7d6STim J. Robbins }
3833ec96cafSStefan Farfeleder getout = escape(p, 0, &len);
384702da352SPedro F. Giffuni PF(start, p);
385702da352SPedro F. Giffuni /* Restore format for next loop. */
386702da352SPedro F. Giffuni
387ab5a295bSJuli Mallett free(p);
388ab5a295bSJuli Mallett if (getout)
389*051c7fddSJilles Tjoelker return (end_fmt);
390ab5a295bSJuli Mallett break;
391ab5a295bSJuli Mallett }
3929b50d902SRodney W. Grimes case 'c': {
3939b50d902SRodney W. Grimes char p;
3949b50d902SRodney W. Grimes
3959b50d902SRodney W. Grimes p = getchr();
396342b089bSJilles Tjoelker if (p != '\0')
3979b50d902SRodney W. Grimes PF(start, p);
3989b50d902SRodney W. Grimes break;
3999b50d902SRodney W. Grimes }
4009b50d902SRodney W. Grimes case 's': {
40145af1a4cSDavid Malone const char *p;
4029b50d902SRodney W. Grimes
4039b50d902SRodney W. Grimes p = getstr();
4049b50d902SRodney W. Grimes PF(start, p);
4059b50d902SRodney W. Grimes break;
4069b50d902SRodney W. Grimes }
4079b50d902SRodney W. Grimes case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
4089b50d902SRodney W. Grimes char *f;
409d41d23e1SStefan Farfeleder intmax_t val;
410d41d23e1SStefan Farfeleder uintmax_t uval;
411bacab7d6STim J. Robbins int signedconv;
4129b50d902SRodney W. Grimes
413bacab7d6STim J. Robbins signedconv = (convch == 'd' || convch == 'i');
414d41d23e1SStefan Farfeleder if ((f = mknum(start, convch)) == NULL)
415fbd08684SStefan Farfeleder return (NULL);
416d41d23e1SStefan Farfeleder if (getnum(&val, &uval, signedconv))
417fbd08684SStefan Farfeleder *rval = 1;
418bacab7d6STim J. Robbins if (signedconv)
419bacab7d6STim J. Robbins PF(f, val);
420bacab7d6STim J. Robbins else
421bacab7d6STim J. Robbins PF(f, uval);
4229b50d902SRodney W. Grimes break;
4239b50d902SRodney W. Grimes }
42403b2eaacSDavid Schultz case 'e': case 'E':
42503b2eaacSDavid Schultz case 'f': case 'F':
42603b2eaacSDavid Schultz case 'g': case 'G':
42703b2eaacSDavid Schultz case 'a': case 'A': {
428fd757c50SDavid Schultz long double p;
4299b50d902SRodney W. Grimes
430fd757c50SDavid Schultz if (getfloating(&p, mod_ldbl))
431fbd08684SStefan Farfeleder *rval = 1;
432fd757c50SDavid Schultz if (mod_ldbl)
4339b50d902SRodney W. Grimes PF(start, p);
434fd757c50SDavid Schultz else
435fd757c50SDavid Schultz PF(start, (double)p);
4369b50d902SRodney W. Grimes break;
4379b50d902SRodney W. Grimes }
4389b50d902SRodney W. Grimes default:
4396a6760dbSJilles Tjoelker warnx("illegal format character %c", convch);
440fbd08684SStefan Farfeleder return (NULL);
4419b50d902SRodney W. Grimes }
4429b50d902SRodney W. Grimes *fmt = nextch;
443f8a6c905SPedro F. Giffuni /* return the gargv to the next element */
444fbd08684SStefan Farfeleder return (fmt);
4459b50d902SRodney W. Grimes }
4469b50d902SRodney W. Grimes
4479b50d902SRodney W. Grimes static char *
mknum(char * str,char ch)4489180853bSXin LI mknum(char *str, char ch)
4499b50d902SRodney W. Grimes {
4503c6e4a5cSBen Smithurst static char *copy;
4513c6e4a5cSBen Smithurst static size_t copy_size;
4523c6e4a5cSBen Smithurst char *newcopy;
453bacab7d6STim J. Robbins size_t len, newlen;
4549b50d902SRodney W. Grimes
4559b50d902SRodney W. Grimes len = strlen(str) + 2;
4563c6e4a5cSBen Smithurst if (len > copy_size) {
4573c6e4a5cSBen Smithurst newlen = ((len + 1023) >> 10) << 10;
458ea1a630aSPedro F. Giffuni if ((newcopy = realloc(copy, newlen)) == NULL) {
4596a6760dbSJilles Tjoelker warnx("%s", strerror(ENOMEM));
4603c6e4a5cSBen Smithurst return (NULL);
461bacab7d6STim J. Robbins }
4623c6e4a5cSBen Smithurst copy = newcopy;
4633c6e4a5cSBen Smithurst copy_size = newlen;
4643c6e4a5cSBen Smithurst }
46562a721e7SStefan Eßer
4669b50d902SRodney W. Grimes memmove(copy, str, len - 3);
467d41d23e1SStefan Farfeleder copy[len - 3] = 'j';
4689b50d902SRodney W. Grimes copy[len - 2] = ch;
4699b50d902SRodney W. Grimes copy[len - 1] = '\0';
4709b50d902SRodney W. Grimes return (copy);
4719b50d902SRodney W. Grimes }
4729b50d902SRodney W. Grimes
473ab5a295bSJuli Mallett static int
escape(char * fmt,int percent,size_t * len)4743ec96cafSStefan Farfeleder escape(char *fmt, int percent, size_t *len)
4759b50d902SRodney W. Grimes {
476ab71f271SPedro F. Giffuni char *save, *store, c;
477ab71f271SPedro F. Giffuni int value;
4789b50d902SRodney W. Grimes
479ab71f271SPedro F. Giffuni for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
4809b50d902SRodney W. Grimes if (c != '\\') {
4819b50d902SRodney W. Grimes *store = c;
4829b50d902SRodney W. Grimes continue;
4839b50d902SRodney W. Grimes }
4849b50d902SRodney W. Grimes switch (*++fmt) {
4859b50d902SRodney W. Grimes case '\0': /* EOS, user error */
4869b50d902SRodney W. Grimes *store = '\\';
4879b50d902SRodney W. Grimes *++store = '\0';
4883ec96cafSStefan Farfeleder *len = store - save;
489ab5a295bSJuli Mallett return (0);
4909b50d902SRodney W. Grimes case '\\': /* backslash */
4919b50d902SRodney W. Grimes case '\'': /* single quote */
4929b50d902SRodney W. Grimes *store = *fmt;
4939b50d902SRodney W. Grimes break;
4949b50d902SRodney W. Grimes case 'a': /* bell/alert */
495f3f148d2SStefan Farfeleder *store = '\a';
4969b50d902SRodney W. Grimes break;
4979b50d902SRodney W. Grimes case 'b': /* backspace */
4989b50d902SRodney W. Grimes *store = '\b';
4999b50d902SRodney W. Grimes break;
500ab5a295bSJuli Mallett case 'c':
501437bce62SPedro F. Giffuni if (!percent) {
502ab5a295bSJuli Mallett *store = '\0';
5033ec96cafSStefan Farfeleder *len = store - save;
504ab5a295bSJuli Mallett return (1);
505437bce62SPedro F. Giffuni }
506437bce62SPedro F. Giffuni *store = 'c';
507437bce62SPedro F. Giffuni break;
5089b50d902SRodney W. Grimes case 'f': /* form-feed */
5099b50d902SRodney W. Grimes *store = '\f';
5109b50d902SRodney W. Grimes break;
5119b50d902SRodney W. Grimes case 'n': /* newline */
5129b50d902SRodney W. Grimes *store = '\n';
5139b50d902SRodney W. Grimes break;
5149b50d902SRodney W. Grimes case 'r': /* carriage-return */
5159b50d902SRodney W. Grimes *store = '\r';
5169b50d902SRodney W. Grimes break;
5179b50d902SRodney W. Grimes case 't': /* horizontal tab */
5189b50d902SRodney W. Grimes *store = '\t';
5199b50d902SRodney W. Grimes break;
5209b50d902SRodney W. Grimes case 'v': /* vertical tab */
521f3f148d2SStefan Farfeleder *store = '\v';
5229b50d902SRodney W. Grimes break;
5239b50d902SRodney W. Grimes /* octal constant */
5249b50d902SRodney W. Grimes case '0': case '1': case '2': case '3':
5259b50d902SRodney W. Grimes case '4': case '5': case '6': case '7':
5269d65050eSDavid Schultz c = (!percent && *fmt == '0') ? 4 : 3;
5279d65050eSDavid Schultz for (value = 0;
5289b50d902SRodney W. Grimes c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
5299b50d902SRodney W. Grimes value <<= 3;
5309b50d902SRodney W. Grimes value += *fmt - '0';
5319b50d902SRodney W. Grimes }
5329b50d902SRodney W. Grimes --fmt;
53312e8db40STim J. Robbins if (percent && value == '%') {
53437fd4590STim J. Robbins *store++ = '%';
53537fd4590STim J. Robbins *store = '%';
53637fd4590STim J. Robbins } else
537ab71f271SPedro F. Giffuni *store = (char)value;
5389b50d902SRodney W. Grimes break;
5399b50d902SRodney W. Grimes default:
5409b50d902SRodney W. Grimes *store = *fmt;
5419b50d902SRodney W. Grimes break;
5429b50d902SRodney W. Grimes }
5439b50d902SRodney W. Grimes }
5449b50d902SRodney W. Grimes *store = '\0';
5453ec96cafSStefan Farfeleder *len = store - save;
546ab5a295bSJuli Mallett return (0);
5479b50d902SRodney W. Grimes }
5489b50d902SRodney W. Grimes
5499b50d902SRodney W. Grimes static int
getchr(void)550f4ac32deSDavid Malone getchr(void)
5519b50d902SRodney W. Grimes {
5529b50d902SRodney W. Grimes if (!*gargv)
5539b50d902SRodney W. Grimes return ('\0');
5549b50d902SRodney W. Grimes return ((int)**gargv++);
5559b50d902SRodney W. Grimes }
5569b50d902SRodney W. Grimes
55745af1a4cSDavid Malone static const char *
getstr(void)558f4ac32deSDavid Malone getstr(void)
5599b50d902SRodney W. Grimes {
5609b50d902SRodney W. Grimes if (!*gargv)
5619b50d902SRodney W. Grimes return ("");
5629b50d902SRodney W. Grimes return (*gargv++);
5639b50d902SRodney W. Grimes }
5649b50d902SRodney W. Grimes
5659b50d902SRodney W. Grimes static int
getint(int * ip)566f4ac32deSDavid Malone getint(int *ip)
5679b50d902SRodney W. Grimes {
568d41d23e1SStefan Farfeleder intmax_t val;
569d41d23e1SStefan Farfeleder uintmax_t uval;
570bacab7d6STim J. Robbins int rval;
5719b50d902SRodney W. Grimes
572d41d23e1SStefan Farfeleder if (getnum(&val, &uval, 1))
5739b50d902SRodney W. Grimes return (1);
574bacab7d6STim J. Robbins rval = 0;
575bacab7d6STim J. Robbins if (val < INT_MIN || val > INT_MAX) {
5766a6760dbSJilles Tjoelker warnx("%s: %s", *gargv, strerror(ERANGE));
577bacab7d6STim J. Robbins rval = 1;
578bacab7d6STim J. Robbins }
57962a721e7SStefan Eßer *ip = (int)val;
580bacab7d6STim J. Robbins return (rval);
5819b50d902SRodney W. Grimes }
5829b50d902SRodney W. Grimes
5839b50d902SRodney W. Grimes static int
getnum(intmax_t * ip,uintmax_t * uip,int signedconv)584d41d23e1SStefan Farfeleder getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
5859b50d902SRodney W. Grimes {
5869b50d902SRodney W. Grimes char *ep;
587bacab7d6STim J. Robbins int rval;
5889b50d902SRodney W. Grimes
5899b50d902SRodney W. Grimes if (!*gargv) {
5904e4d9802SJilles Tjoelker *ip = *uip = 0;
5919b50d902SRodney W. Grimes return (0);
5929b50d902SRodney W. Grimes }
593ab5a295bSJuli Mallett if (**gargv == '"' || **gargv == '\'') {
594bacab7d6STim J. Robbins if (signedconv)
595d41d23e1SStefan Farfeleder *ip = asciicode();
596bacab7d6STim J. Robbins else
597d41d23e1SStefan Farfeleder *uip = asciicode();
5989b50d902SRodney W. Grimes return (0);
5999b50d902SRodney W. Grimes }
600bacab7d6STim J. Robbins rval = 0;
601ab5a295bSJuli Mallett errno = 0;
602bacab7d6STim J. Robbins if (signedconv)
603d41d23e1SStefan Farfeleder *ip = strtoimax(*gargv, &ep, 0);
604bacab7d6STim J. Robbins else
605d41d23e1SStefan Farfeleder *uip = strtoumax(*gargv, &ep, 0);
606bacab7d6STim J. Robbins if (ep == *gargv) {
6076a6760dbSJilles Tjoelker warnx("%s: expected numeric value", *gargv);
608bacab7d6STim J. Robbins rval = 1;
609bacab7d6STim J. Robbins }
610bacab7d6STim J. Robbins else if (*ep != '\0') {
6116a6760dbSJilles Tjoelker warnx("%s: not completely converted", *gargv);
612bacab7d6STim J. Robbins rval = 1;
613bacab7d6STim J. Robbins }
614bacab7d6STim J. Robbins if (errno == ERANGE) {
6156a6760dbSJilles Tjoelker warnx("%s: %s", *gargv, strerror(ERANGE));
616bacab7d6STim J. Robbins rval = 1;
617bacab7d6STim J. Robbins }
618ab5a295bSJuli Mallett ++gargv;
619bacab7d6STim J. Robbins return (rval);
6209b50d902SRodney W. Grimes }
6219b50d902SRodney W. Grimes
622bacab7d6STim J. Robbins static int
getfloating(long double * dp,int mod_ldbl)623fd757c50SDavid Schultz getfloating(long double *dp, int mod_ldbl)
6249b50d902SRodney W. Grimes {
625ab5a295bSJuli Mallett char *ep;
626bacab7d6STim J. Robbins int rval;
627ab5a295bSJuli Mallett
6285ec2b8dcSStefan Farfeleder if (!*gargv) {
6295ec2b8dcSStefan Farfeleder *dp = 0.0;
630bacab7d6STim J. Robbins return (0);
6315ec2b8dcSStefan Farfeleder }
632ab5a295bSJuli Mallett if (**gargv == '"' || **gargv == '\'') {
633bacab7d6STim J. Robbins *dp = asciicode();
634bacab7d6STim J. Robbins return (0);
635ab5a295bSJuli Mallett }
6368c423a99SColin Percival rval = 0;
637ab5a295bSJuli Mallett errno = 0;
638fd757c50SDavid Schultz if (mod_ldbl)
639fd757c50SDavid Schultz *dp = strtold(*gargv, &ep);
640fd757c50SDavid Schultz else
641bacab7d6STim J. Robbins *dp = strtod(*gargv, &ep);
642bacab7d6STim J. Robbins if (ep == *gargv) {
6436a6760dbSJilles Tjoelker warnx("%s: expected numeric value", *gargv);
644bacab7d6STim J. Robbins rval = 1;
645bacab7d6STim J. Robbins } else if (*ep != '\0') {
6466a6760dbSJilles Tjoelker warnx("%s: not completely converted", *gargv);
647bacab7d6STim J. Robbins rval = 1;
648bacab7d6STim J. Robbins }
649bacab7d6STim J. Robbins if (errno == ERANGE) {
6506a6760dbSJilles Tjoelker warnx("%s: %s", *gargv, strerror(ERANGE));
651bacab7d6STim J. Robbins rval = 1;
652bacab7d6STim J. Robbins }
653ab5a295bSJuli Mallett ++gargv;
654bacab7d6STim J. Robbins return (rval);
6559b50d902SRodney W. Grimes }
6569b50d902SRodney W. Grimes
6579b50d902SRodney W. Grimes static int
asciicode(void)658f4ac32deSDavid Malone asciicode(void)
6599b50d902SRodney W. Grimes {
660f4ac32deSDavid Malone int ch;
66198102dabSJilles Tjoelker wchar_t wch;
66298102dabSJilles Tjoelker mbstate_t mbs;
6639b50d902SRodney W. Grimes
66498102dabSJilles Tjoelker ch = (unsigned char)**gargv;
66598102dabSJilles Tjoelker if (ch == '\'' || ch == '"') {
66698102dabSJilles Tjoelker memset(&mbs, 0, sizeof(mbs));
66798102dabSJilles Tjoelker switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
66898102dabSJilles Tjoelker case (size_t)-2:
66998102dabSJilles Tjoelker case (size_t)-1:
67098102dabSJilles Tjoelker wch = (unsigned char)gargv[0][1];
67198102dabSJilles Tjoelker break;
67298102dabSJilles Tjoelker case 0:
67398102dabSJilles Tjoelker wch = 0;
67498102dabSJilles Tjoelker break;
67598102dabSJilles Tjoelker }
67698102dabSJilles Tjoelker ch = wch;
67798102dabSJilles Tjoelker }
6789b50d902SRodney W. Grimes ++gargv;
6799b50d902SRodney W. Grimes return (ch);
6809b50d902SRodney W. Grimes }
6819b50d902SRodney W. Grimes
6829b50d902SRodney W. Grimes static void
usage(void)683f4ac32deSDavid Malone usage(void)
6849b50d902SRodney W. Grimes {
685f682f10cSRuslan Ermilov (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
6869b50d902SRodney W. Grimes }
687