1*84d9c625SLionel Sambuc /* $NetBSD: printf.c,v 1.36 2013/07/16 17:48:22 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*84d9c625SLionel Sambuc __RCSID("$NetBSD: printf.c,v 1.36 2013/07/16 17:48:22 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 *); 71*84d9c625SLionel 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); 77*84d9c625SLionel 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 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; 131*84d9c625SLionel Sambuc char ch; 132*84d9c625SLionel Sambuc int error, o; 1332c96f054SLionel Sambuc 1342c96f054SLionel Sambuc #if !defined(SHELL) && !defined(BUILTIN) 1352c96f054SLionel Sambuc (void)setlocale (LC_ALL, ""); 1362c96f054SLionel Sambuc #endif 1372c96f054SLionel Sambuc 138*84d9c625SLionel Sambuc while ((o = getopt(argc, argv, "")) != -1) { 139*84d9c625SLionel 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*/ 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 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 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 * 4272c96f054SLionel Sambuc conv_escape(char *str, char *conv_ch) 4282c96f054SLionel Sambuc { 429*84d9c625SLionel Sambuc char value; 430*84d9c625SLionel 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; 441*84d9c625SLionel Sambuc num_buf[2] = (char)(ch != '\0' ? str[1] : '\0'); 442*84d9c625SLionel Sambuc num_buf[3] = '\0'; 443*84d9c625SLionel 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; 454*84d9c625SLionel Sambuc num_buf[1] = (char)(ch != '\0' ? str[1] : '\0'); 455*84d9c625SLionel Sambuc num_buf[2] = '\0'; 456*84d9c625SLionel 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 * 4862c96f054SLionel Sambuc conv_expand(const char *str) 4872c96f054SLionel Sambuc { 4882c96f054SLionel Sambuc static char *conv_str; 4892c96f054SLionel Sambuc char *cp; 490*84d9c625SLionel 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 500*84d9c625SLionel 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 */ 516*84d9c625SLionel 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'; 524*84d9c625SLionel 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 * 549*84d9c625SLionel 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) { 5562c96f054SLionel Sambuc warnx("format %s too complex\n", 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 566*84d9c625SLionel Sambuc static char 5672c96f054SLionel Sambuc getchr(void) 5682c96f054SLionel Sambuc { 5692c96f054SLionel Sambuc if (!*gargv) 5702c96f054SLionel Sambuc return 0; 571*84d9c625SLionel Sambuc return **gargv++; 5722c96f054SLionel Sambuc } 5732c96f054SLionel Sambuc 5742c96f054SLionel Sambuc static char * 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 5842c96f054SLionel Sambuc getwidth(void) 5852c96f054SLionel Sambuc { 586*84d9c625SLionel 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 */ 599*84d9c625SLionel Sambuc if (val > 1 << 20) { 6002c96f054SLionel Sambuc warnx("%s: invalid field width", s); 6012c96f054SLionel Sambuc return 0; 6022c96f054SLionel Sambuc } 6032c96f054SLionel Sambuc 604*84d9c625SLionel Sambuc return (int)val; 6052c96f054SLionel Sambuc } 6062c96f054SLionel Sambuc 6072c96f054SLionel Sambuc static intmax_t 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 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 == '\'') 639*84d9c625SLionel 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 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 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 6902c96f054SLionel Sambuc usage(void) 6912c96f054SLionel Sambuc { 6922c96f054SLionel Sambuc (void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname()); 6932c96f054SLionel Sambuc } 694