1*0a6a1f1dSLionel Sambuc /* $NetBSD: printf.c,v 1.37 2015/06/16 22:54:10 christos Exp $ */
22c96f054SLionel Sambuc
32c96f054SLionel Sambuc /*
42c96f054SLionel Sambuc * Copyright (c) 1989, 1993
52c96f054SLionel Sambuc * The Regents of the University of California. All rights reserved.
62c96f054SLionel Sambuc *
72c96f054SLionel Sambuc * Redistribution and use in source and binary forms, with or without
82c96f054SLionel Sambuc * modification, are permitted provided that the following conditions
92c96f054SLionel Sambuc * are met:
102c96f054SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
112c96f054SLionel Sambuc * notice, this list of conditions and the following disclaimer.
122c96f054SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
132c96f054SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
142c96f054SLionel Sambuc * documentation and/or other materials provided with the distribution.
152c96f054SLionel Sambuc * 3. Neither the name of the University nor the names of its contributors
162c96f054SLionel Sambuc * may be used to endorse or promote products derived from this software
172c96f054SLionel Sambuc * without specific prior written permission.
182c96f054SLionel Sambuc *
192c96f054SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
202c96f054SLionel Sambuc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
212c96f054SLionel Sambuc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
222c96f054SLionel Sambuc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
232c96f054SLionel Sambuc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
242c96f054SLionel Sambuc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
252c96f054SLionel Sambuc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
262c96f054SLionel Sambuc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
272c96f054SLionel Sambuc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
282c96f054SLionel Sambuc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
292c96f054SLionel Sambuc * SUCH DAMAGE.
302c96f054SLionel Sambuc */
312c96f054SLionel Sambuc
322c96f054SLionel Sambuc #include <sys/cdefs.h>
332c96f054SLionel Sambuc #ifndef lint
342c96f054SLionel Sambuc #if !defined(BUILTIN) && !defined(SHELL)
352c96f054SLionel Sambuc __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
362c96f054SLionel Sambuc The Regents of the University of California. All rights reserved.");
372c96f054SLionel Sambuc #endif
382c96f054SLionel Sambuc #endif
392c96f054SLionel Sambuc
402c96f054SLionel Sambuc #ifndef lint
412c96f054SLionel Sambuc #if 0
422c96f054SLionel Sambuc static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95";
432c96f054SLionel Sambuc #else
44*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: printf.c,v 1.37 2015/06/16 22:54:10 christos Exp $");
452c96f054SLionel Sambuc #endif
462c96f054SLionel Sambuc #endif /* not lint */
472c96f054SLionel Sambuc
482c96f054SLionel Sambuc #include <sys/types.h>
492c96f054SLionel Sambuc
502c96f054SLionel Sambuc #include <ctype.h>
512c96f054SLionel Sambuc #include <err.h>
522c96f054SLionel Sambuc #include <errno.h>
532c96f054SLionel Sambuc #include <inttypes.h>
542c96f054SLionel Sambuc #include <limits.h>
552c96f054SLionel Sambuc #include <locale.h>
562c96f054SLionel Sambuc #include <stdarg.h>
572c96f054SLionel Sambuc #include <stdio.h>
582c96f054SLionel Sambuc #include <stdlib.h>
592c96f054SLionel Sambuc #include <string.h>
602c96f054SLionel Sambuc #include <unistd.h>
612c96f054SLionel Sambuc
622c96f054SLionel Sambuc #ifdef __GNUC__
632c96f054SLionel Sambuc #define ESCAPE '\e'
642c96f054SLionel Sambuc #else
652c96f054SLionel Sambuc #define ESCAPE 033
662c96f054SLionel Sambuc #endif
672c96f054SLionel Sambuc
682c96f054SLionel Sambuc static void conv_escape_str(char *, void (*)(int));
692c96f054SLionel Sambuc static char *conv_escape(char *, char *);
702c96f054SLionel Sambuc static char *conv_expand(const char *);
7184d9c625SLionel Sambuc static char getchr(void);
722c96f054SLionel Sambuc static double getdouble(void);
732c96f054SLionel Sambuc static int getwidth(void);
742c96f054SLionel Sambuc static intmax_t getintmax(void);
752c96f054SLionel Sambuc static uintmax_t getuintmax(void);
762c96f054SLionel Sambuc static char *getstr(void);
7784d9c625SLionel Sambuc static char *mklong(const char *, char);
782c96f054SLionel Sambuc static void check_conversion(const char *, const char *);
792c96f054SLionel Sambuc static void usage(void);
802c96f054SLionel Sambuc
812c96f054SLionel Sambuc static void b_count(int);
822c96f054SLionel Sambuc static void b_output(int);
832c96f054SLionel Sambuc static size_t b_length;
842c96f054SLionel Sambuc static char *b_fmt;
852c96f054SLionel Sambuc
862c96f054SLionel Sambuc static int rval;
872c96f054SLionel Sambuc static char **gargv;
882c96f054SLionel Sambuc
892c96f054SLionel Sambuc #ifdef BUILTIN /* csh builtin */
902c96f054SLionel Sambuc #define main progprintf
912c96f054SLionel Sambuc #endif
922c96f054SLionel Sambuc
932c96f054SLionel Sambuc #ifdef SHELL /* sh (aka ash) builtin */
942c96f054SLionel Sambuc #define main printfcmd
952c96f054SLionel Sambuc #include "../../bin/sh/bltin/bltin.h"
962c96f054SLionel Sambuc #endif /* SHELL */
972c96f054SLionel Sambuc
982c96f054SLionel Sambuc #define PF(f, func) { \
992c96f054SLionel Sambuc if (fieldwidth != -1) { \
1002c96f054SLionel Sambuc if (precision != -1) \
1012c96f054SLionel Sambuc error = printf(f, fieldwidth, precision, func); \
1022c96f054SLionel Sambuc else \
1032c96f054SLionel Sambuc error = printf(f, fieldwidth, func); \
1042c96f054SLionel Sambuc } else if (precision != -1) \
1052c96f054SLionel Sambuc error = printf(f, precision, func); \
1062c96f054SLionel Sambuc else \
1072c96f054SLionel Sambuc error = printf(f, func); \
1082c96f054SLionel Sambuc }
1092c96f054SLionel Sambuc
1102c96f054SLionel Sambuc #define APF(cpp, f, func) { \
1112c96f054SLionel Sambuc if (fieldwidth != -1) { \
1122c96f054SLionel Sambuc if (precision != -1) \
1132c96f054SLionel Sambuc error = asprintf(cpp, f, fieldwidth, precision, func); \
1142c96f054SLionel Sambuc else \
1152c96f054SLionel Sambuc error = asprintf(cpp, f, fieldwidth, func); \
1162c96f054SLionel Sambuc } else if (precision != -1) \
1172c96f054SLionel Sambuc error = asprintf(cpp, f, precision, func); \
1182c96f054SLionel Sambuc else \
1192c96f054SLionel Sambuc error = asprintf(cpp, f, func); \
1202c96f054SLionel Sambuc }
1212c96f054SLionel Sambuc
1222c96f054SLionel Sambuc #ifdef main
1232c96f054SLionel Sambuc int main(int, char *[]);
1242c96f054SLionel Sambuc #endif
main(int argc,char * argv[])1252c96f054SLionel Sambuc int main(int argc, char *argv[])
1262c96f054SLionel Sambuc {
1272c96f054SLionel Sambuc char *fmt, *start;
1282c96f054SLionel Sambuc int fieldwidth, precision;
1292c96f054SLionel Sambuc char nextch;
1302c96f054SLionel Sambuc char *format;
13184d9c625SLionel Sambuc char ch;
13284d9c625SLionel Sambuc int error, o;
1332c96f054SLionel Sambuc
1342c96f054SLionel Sambuc #if !defined(SHELL) && !defined(BUILTIN)
1352c96f054SLionel Sambuc (void)setlocale (LC_ALL, "");
1362c96f054SLionel Sambuc #endif
1372c96f054SLionel Sambuc
13884d9c625SLionel Sambuc while ((o = getopt(argc, argv, "")) != -1) {
13984d9c625SLionel Sambuc switch (o) {
1402c96f054SLionel Sambuc case '?':
1412c96f054SLionel Sambuc default:
1422c96f054SLionel Sambuc usage();
1432c96f054SLionel Sambuc return 1;
1442c96f054SLionel Sambuc }
1452c96f054SLionel Sambuc }
1462c96f054SLionel Sambuc argc -= optind;
1472c96f054SLionel Sambuc argv += optind;
1482c96f054SLionel Sambuc
1492c96f054SLionel Sambuc if (argc < 1) {
1502c96f054SLionel Sambuc usage();
1512c96f054SLionel Sambuc return 1;
1522c96f054SLionel Sambuc }
1532c96f054SLionel Sambuc
1542c96f054SLionel Sambuc format = *argv;
1552c96f054SLionel Sambuc gargv = ++argv;
1562c96f054SLionel Sambuc
1572c96f054SLionel Sambuc #define SKIP1 "#-+ 0'"
1582c96f054SLionel Sambuc #define SKIP2 "0123456789"
1592c96f054SLionel Sambuc do {
1602c96f054SLionel Sambuc /*
1612c96f054SLionel Sambuc * Basic algorithm is to scan the format string for conversion
1622c96f054SLionel Sambuc * specifications -- once one is found, find out if the field
1632c96f054SLionel Sambuc * width or precision is a '*'; if it is, gather up value.
1642c96f054SLionel Sambuc * Note, format strings are reused as necessary to use up the
1652c96f054SLionel Sambuc * provided arguments, arguments of zero/null string are
1662c96f054SLionel Sambuc * provided to use up the format string.
1672c96f054SLionel Sambuc */
1682c96f054SLionel Sambuc
1692c96f054SLionel Sambuc /* find next format specification */
1702c96f054SLionel Sambuc for (fmt = format; (ch = *fmt++) != '\0';) {
1712c96f054SLionel Sambuc if (ch == '\\') {
1722c96f054SLionel Sambuc char c_ch;
1732c96f054SLionel Sambuc fmt = conv_escape(fmt, &c_ch);
1742c96f054SLionel Sambuc putchar(c_ch);
1752c96f054SLionel Sambuc continue;
1762c96f054SLionel Sambuc }
1772c96f054SLionel Sambuc if (ch != '%' || (*fmt == '%' && ++fmt)) {
1782c96f054SLionel Sambuc (void)putchar(ch);
1792c96f054SLionel Sambuc continue;
1802c96f054SLionel Sambuc }
1812c96f054SLionel Sambuc
1822c96f054SLionel Sambuc /* Ok - we've found a format specification,
1832c96f054SLionel Sambuc Save its address for a later printf(). */
1842c96f054SLionel Sambuc start = fmt - 1;
1852c96f054SLionel Sambuc
1862c96f054SLionel Sambuc /* skip to field width */
1872c96f054SLionel Sambuc fmt += strspn(fmt, SKIP1);
1882c96f054SLionel Sambuc if (*fmt == '*') {
1892c96f054SLionel Sambuc fmt++;
1902c96f054SLionel Sambuc fieldwidth = getwidth();
1912c96f054SLionel Sambuc } else
1922c96f054SLionel Sambuc fieldwidth = -1;
1932c96f054SLionel Sambuc
1942c96f054SLionel Sambuc /* skip to possible '.', get following precision */
1952c96f054SLionel Sambuc fmt += strspn(fmt, SKIP2);
1962c96f054SLionel Sambuc if (*fmt == '.') {
1972c96f054SLionel Sambuc fmt++;
1982c96f054SLionel Sambuc if (*fmt == '*') {
1992c96f054SLionel Sambuc fmt++;
2002c96f054SLionel Sambuc precision = getwidth();
2012c96f054SLionel Sambuc } else
2022c96f054SLionel Sambuc precision = -1;
2032c96f054SLionel Sambuc } else
2042c96f054SLionel Sambuc precision = -1;
2052c96f054SLionel Sambuc
2062c96f054SLionel Sambuc fmt += strspn(fmt, SKIP2);
2072c96f054SLionel Sambuc
2082c96f054SLionel Sambuc ch = *fmt;
2092c96f054SLionel Sambuc if (!ch) {
2102c96f054SLionel Sambuc warnx("missing format character");
2112c96f054SLionel Sambuc return (1);
2122c96f054SLionel Sambuc }
2132c96f054SLionel Sambuc /* null terminate format string to we can use it
2142c96f054SLionel Sambuc as an argument to printf. */
2152c96f054SLionel Sambuc nextch = fmt[1];
2162c96f054SLionel Sambuc fmt[1] = 0;
2172c96f054SLionel Sambuc switch (ch) {
2182c96f054SLionel Sambuc
2192c96f054SLionel Sambuc case 'B': {
2202c96f054SLionel Sambuc const char *p = conv_expand(getstr());
2212c96f054SLionel Sambuc if (p == NULL)
2222c96f054SLionel Sambuc goto out;
2232c96f054SLionel Sambuc *fmt = 's';
2242c96f054SLionel Sambuc PF(start, p);
2252c96f054SLionel Sambuc if (error < 0)
2262c96f054SLionel Sambuc goto out;
2272c96f054SLionel Sambuc break;
2282c96f054SLionel Sambuc }
2292c96f054SLionel Sambuc case 'b': {
2302c96f054SLionel Sambuc /* There has to be a better way to do this,
2312c96f054SLionel Sambuc * but the string we generate might have
2322c96f054SLionel Sambuc * embedded nulls. */
2332c96f054SLionel Sambuc static char *a, *t;
2342c96f054SLionel Sambuc char *cp = getstr();
2352c96f054SLionel Sambuc /* Free on entry in case shell longjumped out */
2362c96f054SLionel Sambuc if (a != NULL)
2372c96f054SLionel Sambuc free(a);
2382c96f054SLionel Sambuc a = NULL;
2392c96f054SLionel Sambuc if (t != NULL)
2402c96f054SLionel Sambuc free(t);
2412c96f054SLionel Sambuc t = NULL;
2422c96f054SLionel Sambuc /* Count number of bytes we want to output */
2432c96f054SLionel Sambuc b_length = 0;
2442c96f054SLionel Sambuc conv_escape_str(cp, b_count);
2452c96f054SLionel Sambuc t = malloc(b_length + 1);
2462c96f054SLionel Sambuc if (t == NULL)
2472c96f054SLionel Sambuc goto out;
2482c96f054SLionel Sambuc (void)memset(t, 'x', b_length);
2492c96f054SLionel Sambuc t[b_length] = 0;
2502c96f054SLionel Sambuc /* Get printf to calculate the lengths */
2512c96f054SLionel Sambuc *fmt = 's';
2522c96f054SLionel Sambuc APF(&a, start, t);
2532c96f054SLionel Sambuc if (error == -1)
2542c96f054SLionel Sambuc goto out;
2552c96f054SLionel Sambuc b_fmt = a;
2562c96f054SLionel Sambuc /* Output leading spaces and data bytes */
2572c96f054SLionel Sambuc conv_escape_str(cp, b_output);
2582c96f054SLionel Sambuc /* Add any trailing spaces */
2592c96f054SLionel Sambuc printf("%s", b_fmt);
2602c96f054SLionel Sambuc break;
2612c96f054SLionel Sambuc }
2622c96f054SLionel Sambuc case 'c': {
2632c96f054SLionel Sambuc char p = getchr();
2642c96f054SLionel Sambuc PF(start, p);
2652c96f054SLionel Sambuc if (error < 0)
2662c96f054SLionel Sambuc goto out;
2672c96f054SLionel Sambuc break;
2682c96f054SLionel Sambuc }
2692c96f054SLionel Sambuc case 's': {
2702c96f054SLionel Sambuc char *p = getstr();
2712c96f054SLionel Sambuc PF(start, p);
2722c96f054SLionel Sambuc if (error < 0)
2732c96f054SLionel Sambuc goto out;
2742c96f054SLionel Sambuc break;
2752c96f054SLionel Sambuc }
2762c96f054SLionel Sambuc case 'd':
2772c96f054SLionel Sambuc case 'i': {
2782c96f054SLionel Sambuc intmax_t p = getintmax();
2792c96f054SLionel Sambuc char *f = mklong(start, ch);
2802c96f054SLionel Sambuc PF(f, p);
2812c96f054SLionel Sambuc if (error < 0)
2822c96f054SLionel Sambuc goto out;
2832c96f054SLionel Sambuc break;
2842c96f054SLionel Sambuc }
2852c96f054SLionel Sambuc case 'o':
2862c96f054SLionel Sambuc case 'u':
2872c96f054SLionel Sambuc case 'x':
2882c96f054SLionel Sambuc case 'X': {
2892c96f054SLionel Sambuc uintmax_t p = getuintmax();
2902c96f054SLionel Sambuc char *f = mklong(start, ch);
2912c96f054SLionel Sambuc PF(f, p);
2922c96f054SLionel Sambuc if (error < 0)
2932c96f054SLionel Sambuc goto out;
2942c96f054SLionel Sambuc break;
2952c96f054SLionel Sambuc }
2962c96f054SLionel Sambuc case 'e':
2972c96f054SLionel Sambuc case 'E':
2982c96f054SLionel Sambuc case 'f':
2992c96f054SLionel Sambuc case 'g':
3002c96f054SLionel Sambuc case 'G': {
3012c96f054SLionel Sambuc double p = getdouble();
3022c96f054SLionel Sambuc PF(start, p);
3032c96f054SLionel Sambuc if (error < 0)
3042c96f054SLionel Sambuc goto out;
3052c96f054SLionel Sambuc break;
3062c96f054SLionel Sambuc }
3072c96f054SLionel Sambuc default:
3082c96f054SLionel Sambuc warnx("%s: invalid directive", start);
3092c96f054SLionel Sambuc return 1;
3102c96f054SLionel Sambuc }
3112c96f054SLionel Sambuc *fmt++ = ch;
3122c96f054SLionel Sambuc *fmt = nextch;
3132c96f054SLionel Sambuc /* escape if a \c was encountered */
3142c96f054SLionel Sambuc if (rval & 0x100)
3152c96f054SLionel Sambuc return rval & ~0x100;
3162c96f054SLionel Sambuc }
3172c96f054SLionel Sambuc } while (gargv != argv && *gargv);
3182c96f054SLionel Sambuc
3192c96f054SLionel Sambuc return rval & ~0x100;
3202c96f054SLionel Sambuc out:
3212c96f054SLionel Sambuc warn("print failed");
3222c96f054SLionel Sambuc return 1;
3232c96f054SLionel Sambuc }
3242c96f054SLionel Sambuc
3252c96f054SLionel Sambuc /* helper functions for conv_escape_str */
3262c96f054SLionel Sambuc
3272c96f054SLionel Sambuc static void
3282c96f054SLionel Sambuc /*ARGSUSED*/
b_count(int ch)3292c96f054SLionel Sambuc b_count(int ch)
3302c96f054SLionel Sambuc {
3312c96f054SLionel Sambuc b_length++;
3322c96f054SLionel Sambuc }
3332c96f054SLionel Sambuc
3342c96f054SLionel Sambuc /* Output one converted character for every 'x' in the 'format' */
3352c96f054SLionel Sambuc
3362c96f054SLionel Sambuc static void
b_output(int ch)3372c96f054SLionel Sambuc b_output(int ch)
3382c96f054SLionel Sambuc {
3392c96f054SLionel Sambuc for (;;) {
3402c96f054SLionel Sambuc switch (*b_fmt++) {
3412c96f054SLionel Sambuc case 0:
3422c96f054SLionel Sambuc b_fmt--;
3432c96f054SLionel Sambuc return;
3442c96f054SLionel Sambuc case ' ':
3452c96f054SLionel Sambuc putchar(' ');
3462c96f054SLionel Sambuc break;
3472c96f054SLionel Sambuc default:
3482c96f054SLionel Sambuc putchar(ch);
3492c96f054SLionel Sambuc return;
3502c96f054SLionel Sambuc }
3512c96f054SLionel Sambuc }
3522c96f054SLionel Sambuc }
3532c96f054SLionel Sambuc
3542c96f054SLionel Sambuc
3552c96f054SLionel Sambuc /*
3562c96f054SLionel Sambuc * Print SysV echo(1) style escape string
3572c96f054SLionel Sambuc * Halts processing string if a \c escape is encountered.
3582c96f054SLionel Sambuc */
3592c96f054SLionel Sambuc static void
conv_escape_str(char * str,void (* do_putchar)(int))3602c96f054SLionel Sambuc conv_escape_str(char *str, void (*do_putchar)(int))
3612c96f054SLionel Sambuc {
3622c96f054SLionel Sambuc int value;
3632c96f054SLionel Sambuc int ch;
3642c96f054SLionel Sambuc char c;
3652c96f054SLionel Sambuc
3662c96f054SLionel Sambuc while ((ch = *str++) != '\0') {
3672c96f054SLionel Sambuc if (ch != '\\') {
3682c96f054SLionel Sambuc do_putchar(ch);
3692c96f054SLionel Sambuc continue;
3702c96f054SLionel Sambuc }
3712c96f054SLionel Sambuc
3722c96f054SLionel Sambuc ch = *str++;
3732c96f054SLionel Sambuc if (ch == 'c') {
3742c96f054SLionel Sambuc /* \c as in SYSV echo - abort all processing.... */
3752c96f054SLionel Sambuc rval |= 0x100;
3762c96f054SLionel Sambuc break;
3772c96f054SLionel Sambuc }
3782c96f054SLionel Sambuc
3792c96f054SLionel Sambuc /*
3802c96f054SLionel Sambuc * %b string octal constants are not like those in C.
3812c96f054SLionel Sambuc * They start with a \0, and are followed by 0, 1, 2,
3822c96f054SLionel Sambuc * or 3 octal digits.
3832c96f054SLionel Sambuc */
3842c96f054SLionel Sambuc if (ch == '0') {
3852c96f054SLionel Sambuc int octnum = 0, i;
3862c96f054SLionel Sambuc for (i = 0; i < 3; i++) {
3872c96f054SLionel Sambuc if (!isdigit((unsigned char)*str) || *str > '7')
3882c96f054SLionel Sambuc break;
3892c96f054SLionel Sambuc octnum = (octnum << 3) | (*str++ - '0');
3902c96f054SLionel Sambuc }
3912c96f054SLionel Sambuc do_putchar(octnum);
3922c96f054SLionel Sambuc continue;
3932c96f054SLionel Sambuc }
3942c96f054SLionel Sambuc
3952c96f054SLionel Sambuc /* \[M][^|-]C as defined by vis(3) */
3962c96f054SLionel Sambuc if (ch == 'M' && *str == '-') {
3972c96f054SLionel Sambuc do_putchar(0200 | str[1]);
3982c96f054SLionel Sambuc str += 2;
3992c96f054SLionel Sambuc continue;
4002c96f054SLionel Sambuc }
4012c96f054SLionel Sambuc if (ch == 'M' && *str == '^') {
4022c96f054SLionel Sambuc str++;
4032c96f054SLionel Sambuc value = 0200;
4042c96f054SLionel Sambuc ch = '^';
4052c96f054SLionel Sambuc } else
4062c96f054SLionel Sambuc value = 0;
4072c96f054SLionel Sambuc if (ch == '^') {
4082c96f054SLionel Sambuc ch = *str++;
4092c96f054SLionel Sambuc if (ch == '?')
4102c96f054SLionel Sambuc value |= 0177;
4112c96f054SLionel Sambuc else
4122c96f054SLionel Sambuc value |= ch & 037;
4132c96f054SLionel Sambuc do_putchar(value);
4142c96f054SLionel Sambuc continue;
4152c96f054SLionel Sambuc }
4162c96f054SLionel Sambuc
4172c96f054SLionel Sambuc /* Finally test for sequences valid in the format string */
4182c96f054SLionel Sambuc str = conv_escape(str - 1, &c);
4192c96f054SLionel Sambuc do_putchar(c);
4202c96f054SLionel Sambuc }
4212c96f054SLionel Sambuc }
4222c96f054SLionel Sambuc
4232c96f054SLionel Sambuc /*
4242c96f054SLionel Sambuc * Print "standard" escape characters
4252c96f054SLionel Sambuc */
4262c96f054SLionel Sambuc static char *
conv_escape(char * str,char * conv_ch)4272c96f054SLionel Sambuc conv_escape(char *str, char *conv_ch)
4282c96f054SLionel Sambuc {
42984d9c625SLionel Sambuc char value;
43084d9c625SLionel Sambuc char ch;
4312c96f054SLionel Sambuc char num_buf[4], *num_end;
4322c96f054SLionel Sambuc
4332c96f054SLionel Sambuc ch = *str++;
4342c96f054SLionel Sambuc
4352c96f054SLionel Sambuc switch (ch) {
4362c96f054SLionel Sambuc case '0': case '1': case '2': case '3':
4372c96f054SLionel Sambuc case '4': case '5': case '6': case '7':
4382c96f054SLionel Sambuc num_buf[0] = ch;
4392c96f054SLionel Sambuc ch = str[0];
4402c96f054SLionel Sambuc num_buf[1] = ch;
44184d9c625SLionel Sambuc num_buf[2] = (char)(ch != '\0' ? str[1] : '\0');
44284d9c625SLionel Sambuc num_buf[3] = '\0';
44384d9c625SLionel Sambuc value = (char)strtoul(num_buf, &num_end, 8);
4442c96f054SLionel Sambuc str += num_end - (num_buf + 1);
4452c96f054SLionel Sambuc break;
4462c96f054SLionel Sambuc
4472c96f054SLionel Sambuc case 'x':
4482c96f054SLionel Sambuc /* Hexadecimal character constants are not required to be
4492c96f054SLionel Sambuc supported (by SuS v1) because there is no consistent
4502c96f054SLionel Sambuc way to detect the end of the constant.
4512c96f054SLionel Sambuc Supporting 2 byte constants is a compromise. */
4522c96f054SLionel Sambuc ch = str[0];
4532c96f054SLionel Sambuc num_buf[0] = ch;
45484d9c625SLionel Sambuc num_buf[1] = (char)(ch != '\0' ? str[1] : '\0');
45584d9c625SLionel Sambuc num_buf[2] = '\0';
45684d9c625SLionel Sambuc value = (char)strtoul(num_buf, &num_end, 16);
4572c96f054SLionel Sambuc str += num_end - num_buf;
4582c96f054SLionel Sambuc break;
4592c96f054SLionel Sambuc
4602c96f054SLionel Sambuc case '\\': value = '\\'; break; /* backslash */
4612c96f054SLionel Sambuc case '\'': value = '\''; break; /* single quote */
4622c96f054SLionel Sambuc case '"': value = '"'; break; /* double quote */
4632c96f054SLionel Sambuc case 'a': value = '\a'; break; /* alert */
4642c96f054SLionel Sambuc case 'b': value = '\b'; break; /* backspace */
4652c96f054SLionel Sambuc case 'e': value = ESCAPE; break; /* escape */
4662c96f054SLionel Sambuc case 'f': value = '\f'; break; /* form-feed */
4672c96f054SLionel Sambuc case 'n': value = '\n'; break; /* newline */
4682c96f054SLionel Sambuc case 'r': value = '\r'; break; /* carriage-return */
4692c96f054SLionel Sambuc case 't': value = '\t'; break; /* tab */
4702c96f054SLionel Sambuc case 'v': value = '\v'; break; /* vertical-tab */
4712c96f054SLionel Sambuc
4722c96f054SLionel Sambuc default:
4732c96f054SLionel Sambuc warnx("unknown escape sequence `\\%c'", ch);
4742c96f054SLionel Sambuc rval = 1;
4752c96f054SLionel Sambuc value = ch;
4762c96f054SLionel Sambuc break;
4772c96f054SLionel Sambuc }
4782c96f054SLionel Sambuc
4792c96f054SLionel Sambuc *conv_ch = value;
4802c96f054SLionel Sambuc return str;
4812c96f054SLionel Sambuc }
4822c96f054SLionel Sambuc
4832c96f054SLionel Sambuc /* expand a string so that everything is printable */
4842c96f054SLionel Sambuc
4852c96f054SLionel Sambuc static char *
conv_expand(const char * str)4862c96f054SLionel Sambuc conv_expand(const char *str)
4872c96f054SLionel Sambuc {
4882c96f054SLionel Sambuc static char *conv_str;
4892c96f054SLionel Sambuc char *cp;
49084d9c625SLionel Sambuc char ch;
4912c96f054SLionel Sambuc
4922c96f054SLionel Sambuc if (conv_str)
4932c96f054SLionel Sambuc free(conv_str);
4942c96f054SLionel Sambuc /* get a buffer that is definitely large enough.... */
4952c96f054SLionel Sambuc conv_str = malloc(4 * strlen(str) + 1);
4962c96f054SLionel Sambuc if (!conv_str)
4972c96f054SLionel Sambuc return NULL;
4982c96f054SLionel Sambuc cp = conv_str;
4992c96f054SLionel Sambuc
50084d9c625SLionel Sambuc while ((ch = *(const char *)str++) != '\0') {
5012c96f054SLionel Sambuc switch (ch) {
5022c96f054SLionel Sambuc /* Use C escapes for expected control characters */
5032c96f054SLionel Sambuc case '\\': ch = '\\'; break; /* backslash */
5042c96f054SLionel Sambuc case '\'': ch = '\''; break; /* single quote */
5052c96f054SLionel Sambuc case '"': ch = '"'; break; /* double quote */
5062c96f054SLionel Sambuc case '\a': ch = 'a'; break; /* alert */
5072c96f054SLionel Sambuc case '\b': ch = 'b'; break; /* backspace */
5082c96f054SLionel Sambuc case ESCAPE: ch = 'e'; break; /* escape */
5092c96f054SLionel Sambuc case '\f': ch = 'f'; break; /* form-feed */
5102c96f054SLionel Sambuc case '\n': ch = 'n'; break; /* newline */
5112c96f054SLionel Sambuc case '\r': ch = 'r'; break; /* carriage-return */
5122c96f054SLionel Sambuc case '\t': ch = 't'; break; /* tab */
5132c96f054SLionel Sambuc case '\v': ch = 'v'; break; /* vertical-tab */
5142c96f054SLionel Sambuc default:
5152c96f054SLionel Sambuc /* Copy anything printable */
51684d9c625SLionel Sambuc if (isprint((unsigned char)ch)) {
5172c96f054SLionel Sambuc *cp++ = ch;
5182c96f054SLionel Sambuc continue;
5192c96f054SLionel Sambuc }
5202c96f054SLionel Sambuc /* Use vis(3) encodings for the rest */
5212c96f054SLionel Sambuc *cp++ = '\\';
5222c96f054SLionel Sambuc if (ch & 0200) {
5232c96f054SLionel Sambuc *cp++ = 'M';
52484d9c625SLionel Sambuc ch &= (char)~0200;
5252c96f054SLionel Sambuc }
5262c96f054SLionel Sambuc if (ch == 0177) {
5272c96f054SLionel Sambuc *cp++ = '^';
5282c96f054SLionel Sambuc *cp++ = '?';
5292c96f054SLionel Sambuc continue;
5302c96f054SLionel Sambuc }
5312c96f054SLionel Sambuc if (ch < 040) {
5322c96f054SLionel Sambuc *cp++ = '^';
5332c96f054SLionel Sambuc *cp++ = ch | 0100;
5342c96f054SLionel Sambuc continue;
5352c96f054SLionel Sambuc }
5362c96f054SLionel Sambuc *cp++ = '-';
5372c96f054SLionel Sambuc *cp++ = ch;
5382c96f054SLionel Sambuc continue;
5392c96f054SLionel Sambuc }
5402c96f054SLionel Sambuc *cp++ = '\\';
5412c96f054SLionel Sambuc *cp++ = ch;
5422c96f054SLionel Sambuc }
5432c96f054SLionel Sambuc
5442c96f054SLionel Sambuc *cp = 0;
5452c96f054SLionel Sambuc return conv_str;
5462c96f054SLionel Sambuc }
5472c96f054SLionel Sambuc
5482c96f054SLionel Sambuc static char *
mklong(const char * str,char ch)54984d9c625SLionel Sambuc mklong(const char *str, char ch)
5502c96f054SLionel Sambuc {
5512c96f054SLionel Sambuc static char copy[64];
5522c96f054SLionel Sambuc size_t len;
5532c96f054SLionel Sambuc
5542c96f054SLionel Sambuc len = strlen(str) + 2;
5552c96f054SLionel Sambuc if (len > sizeof copy) {
556*0a6a1f1dSLionel Sambuc warnx("format %s too complex", str);
5572c96f054SLionel Sambuc len = 4;
5582c96f054SLionel Sambuc }
5592c96f054SLionel Sambuc (void)memmove(copy, str, len - 3);
5602c96f054SLionel Sambuc copy[len - 3] = 'j';
5612c96f054SLionel Sambuc copy[len - 2] = ch;
5622c96f054SLionel Sambuc copy[len - 1] = '\0';
5632c96f054SLionel Sambuc return copy;
5642c96f054SLionel Sambuc }
5652c96f054SLionel Sambuc
56684d9c625SLionel Sambuc static char
getchr(void)5672c96f054SLionel Sambuc getchr(void)
5682c96f054SLionel Sambuc {
5692c96f054SLionel Sambuc if (!*gargv)
5702c96f054SLionel Sambuc return 0;
57184d9c625SLionel Sambuc return **gargv++;
5722c96f054SLionel Sambuc }
5732c96f054SLionel Sambuc
5742c96f054SLionel Sambuc static char *
getstr(void)5752c96f054SLionel Sambuc getstr(void)
5762c96f054SLionel Sambuc {
5772c96f054SLionel Sambuc static char empty[] = "";
5782c96f054SLionel Sambuc if (!*gargv)
5792c96f054SLionel Sambuc return empty;
5802c96f054SLionel Sambuc return *gargv++;
5812c96f054SLionel Sambuc }
5822c96f054SLionel Sambuc
5832c96f054SLionel Sambuc static int
getwidth(void)5842c96f054SLionel Sambuc getwidth(void)
5852c96f054SLionel Sambuc {
58684d9c625SLionel Sambuc unsigned long val;
5872c96f054SLionel Sambuc char *s, *ep;
5882c96f054SLionel Sambuc
5892c96f054SLionel Sambuc s = *gargv;
5902c96f054SLionel Sambuc if (!*gargv)
5912c96f054SLionel Sambuc return (0);
5922c96f054SLionel Sambuc gargv++;
5932c96f054SLionel Sambuc
5942c96f054SLionel Sambuc errno = 0;
5952c96f054SLionel Sambuc val = strtoul(s, &ep, 0);
5962c96f054SLionel Sambuc check_conversion(s, ep);
5972c96f054SLionel Sambuc
5982c96f054SLionel Sambuc /* Arbitrarily 'restrict' field widths to 1Mbyte */
59984d9c625SLionel Sambuc if (val > 1 << 20) {
6002c96f054SLionel Sambuc warnx("%s: invalid field width", s);
6012c96f054SLionel Sambuc return 0;
6022c96f054SLionel Sambuc }
6032c96f054SLionel Sambuc
60484d9c625SLionel Sambuc return (int)val;
6052c96f054SLionel Sambuc }
6062c96f054SLionel Sambuc
6072c96f054SLionel Sambuc static intmax_t
getintmax(void)6082c96f054SLionel Sambuc getintmax(void)
6092c96f054SLionel Sambuc {
6102c96f054SLionel Sambuc intmax_t val;
6112c96f054SLionel Sambuc char *cp, *ep;
6122c96f054SLionel Sambuc
6132c96f054SLionel Sambuc cp = *gargv;
6142c96f054SLionel Sambuc if (cp == NULL)
6152c96f054SLionel Sambuc return 0;
6162c96f054SLionel Sambuc gargv++;
6172c96f054SLionel Sambuc
6182c96f054SLionel Sambuc if (*cp == '\"' || *cp == '\'')
6192c96f054SLionel Sambuc return *(cp + 1);
6202c96f054SLionel Sambuc
6212c96f054SLionel Sambuc errno = 0;
6222c96f054SLionel Sambuc val = strtoimax(cp, &ep, 0);
6232c96f054SLionel Sambuc check_conversion(cp, ep);
6242c96f054SLionel Sambuc return val;
6252c96f054SLionel Sambuc }
6262c96f054SLionel Sambuc
6272c96f054SLionel Sambuc static uintmax_t
getuintmax(void)6282c96f054SLionel Sambuc getuintmax(void)
6292c96f054SLionel Sambuc {
6302c96f054SLionel Sambuc uintmax_t val;
6312c96f054SLionel Sambuc char *cp, *ep;
6322c96f054SLionel Sambuc
6332c96f054SLionel Sambuc cp = *gargv;
6342c96f054SLionel Sambuc if (cp == NULL)
6352c96f054SLionel Sambuc return 0;
6362c96f054SLionel Sambuc gargv++;
6372c96f054SLionel Sambuc
6382c96f054SLionel Sambuc if (*cp == '\"' || *cp == '\'')
63984d9c625SLionel Sambuc return (uintmax_t)*(cp + 1);
6402c96f054SLionel Sambuc
6412c96f054SLionel Sambuc /* strtoumax won't error -ve values */
6422c96f054SLionel Sambuc while (isspace(*(unsigned char *)cp))
6432c96f054SLionel Sambuc cp++;
6442c96f054SLionel Sambuc if (*cp == '-') {
6452c96f054SLionel Sambuc warnx("%s: expected positive numeric value", cp);
6462c96f054SLionel Sambuc rval = 1;
6472c96f054SLionel Sambuc return 0;
6482c96f054SLionel Sambuc }
6492c96f054SLionel Sambuc
6502c96f054SLionel Sambuc errno = 0;
6512c96f054SLionel Sambuc val = strtoumax(cp, &ep, 0);
6522c96f054SLionel Sambuc check_conversion(cp, ep);
6532c96f054SLionel Sambuc return val;
6542c96f054SLionel Sambuc }
6552c96f054SLionel Sambuc
6562c96f054SLionel Sambuc static double
getdouble(void)6572c96f054SLionel Sambuc getdouble(void)
6582c96f054SLionel Sambuc {
6592c96f054SLionel Sambuc double val;
6602c96f054SLionel Sambuc char *ep;
6612c96f054SLionel Sambuc
6622c96f054SLionel Sambuc if (!*gargv)
6632c96f054SLionel Sambuc return (0.0);
6642c96f054SLionel Sambuc
6652c96f054SLionel Sambuc if (**gargv == '\"' || **gargv == '\'')
6662c96f054SLionel Sambuc return (double) *((*gargv++)+1);
6672c96f054SLionel Sambuc
6682c96f054SLionel Sambuc errno = 0;
6692c96f054SLionel Sambuc val = strtod(*gargv, &ep);
6702c96f054SLionel Sambuc check_conversion(*gargv++, ep);
6712c96f054SLionel Sambuc return val;
6722c96f054SLionel Sambuc }
6732c96f054SLionel Sambuc
6742c96f054SLionel Sambuc static void
check_conversion(const char * s,const char * ep)6752c96f054SLionel Sambuc check_conversion(const char *s, const char *ep)
6762c96f054SLionel Sambuc {
6772c96f054SLionel Sambuc if (*ep) {
6782c96f054SLionel Sambuc if (ep == s)
6792c96f054SLionel Sambuc warnx("%s: expected numeric value", s);
6802c96f054SLionel Sambuc else
6812c96f054SLionel Sambuc warnx("%s: not completely converted", s);
6822c96f054SLionel Sambuc rval = 1;
6832c96f054SLionel Sambuc } else if (errno == ERANGE) {
6842c96f054SLionel Sambuc warnx("%s: %s", s, strerror(ERANGE));
6852c96f054SLionel Sambuc rval = 1;
6862c96f054SLionel Sambuc }
6872c96f054SLionel Sambuc }
6882c96f054SLionel Sambuc
6892c96f054SLionel Sambuc static void
usage(void)6902c96f054SLionel Sambuc usage(void)
6912c96f054SLionel Sambuc {
6922c96f054SLionel Sambuc (void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname());
6932c96f054SLionel Sambuc }
694