1*2c96f054SLionel Sambuc /* $NetBSD: printf.c,v 1.35 2011/03/15 23:11:49 christos Exp $ */ 2*2c96f054SLionel Sambuc 3*2c96f054SLionel Sambuc /* 4*2c96f054SLionel Sambuc * Copyright (c) 1989, 1993 5*2c96f054SLionel Sambuc * The Regents of the University of California. All rights reserved. 6*2c96f054SLionel Sambuc * 7*2c96f054SLionel Sambuc * Redistribution and use in source and binary forms, with or without 8*2c96f054SLionel Sambuc * modification, are permitted provided that the following conditions 9*2c96f054SLionel Sambuc * are met: 10*2c96f054SLionel Sambuc * 1. Redistributions of source code must retain the above copyright 11*2c96f054SLionel Sambuc * notice, this list of conditions and the following disclaimer. 12*2c96f054SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright 13*2c96f054SLionel Sambuc * notice, this list of conditions and the following disclaimer in the 14*2c96f054SLionel Sambuc * documentation and/or other materials provided with the distribution. 15*2c96f054SLionel Sambuc * 3. Neither the name of the University nor the names of its contributors 16*2c96f054SLionel Sambuc * may be used to endorse or promote products derived from this software 17*2c96f054SLionel Sambuc * without specific prior written permission. 18*2c96f054SLionel Sambuc * 19*2c96f054SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20*2c96f054SLionel Sambuc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21*2c96f054SLionel Sambuc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22*2c96f054SLionel Sambuc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23*2c96f054SLionel Sambuc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24*2c96f054SLionel Sambuc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25*2c96f054SLionel Sambuc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26*2c96f054SLionel Sambuc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27*2c96f054SLionel Sambuc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28*2c96f054SLionel Sambuc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29*2c96f054SLionel Sambuc * SUCH DAMAGE. 30*2c96f054SLionel Sambuc */ 31*2c96f054SLionel Sambuc 32*2c96f054SLionel Sambuc #include <sys/cdefs.h> 33*2c96f054SLionel Sambuc #ifndef lint 34*2c96f054SLionel Sambuc #if !defined(BUILTIN) && !defined(SHELL) 35*2c96f054SLionel Sambuc __COPYRIGHT("@(#) Copyright (c) 1989, 1993\ 36*2c96f054SLionel Sambuc The Regents of the University of California. All rights reserved."); 37*2c96f054SLionel Sambuc #endif 38*2c96f054SLionel Sambuc #endif 39*2c96f054SLionel Sambuc 40*2c96f054SLionel Sambuc #ifndef lint 41*2c96f054SLionel Sambuc #if 0 42*2c96f054SLionel Sambuc static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95"; 43*2c96f054SLionel Sambuc #else 44*2c96f054SLionel Sambuc __RCSID("$NetBSD: printf.c,v 1.35 2011/03/15 23:11:49 christos Exp $"); 45*2c96f054SLionel Sambuc #endif 46*2c96f054SLionel Sambuc #endif /* not lint */ 47*2c96f054SLionel Sambuc 48*2c96f054SLionel Sambuc #include <sys/types.h> 49*2c96f054SLionel Sambuc 50*2c96f054SLionel Sambuc #include <ctype.h> 51*2c96f054SLionel Sambuc #include <err.h> 52*2c96f054SLionel Sambuc #include <errno.h> 53*2c96f054SLionel Sambuc #include <inttypes.h> 54*2c96f054SLionel Sambuc #include <limits.h> 55*2c96f054SLionel Sambuc #include <locale.h> 56*2c96f054SLionel Sambuc #include <stdarg.h> 57*2c96f054SLionel Sambuc #include <stdio.h> 58*2c96f054SLionel Sambuc #include <stdlib.h> 59*2c96f054SLionel Sambuc #include <string.h> 60*2c96f054SLionel Sambuc #include <unistd.h> 61*2c96f054SLionel Sambuc 62*2c96f054SLionel Sambuc #ifdef __GNUC__ 63*2c96f054SLionel Sambuc #define ESCAPE '\e' 64*2c96f054SLionel Sambuc #else 65*2c96f054SLionel Sambuc #define ESCAPE 033 66*2c96f054SLionel Sambuc #endif 67*2c96f054SLionel Sambuc 68*2c96f054SLionel Sambuc static void conv_escape_str(char *, void (*)(int)); 69*2c96f054SLionel Sambuc static char *conv_escape(char *, char *); 70*2c96f054SLionel Sambuc static char *conv_expand(const char *); 71*2c96f054SLionel Sambuc static int getchr(void); 72*2c96f054SLionel Sambuc static double getdouble(void); 73*2c96f054SLionel Sambuc static int getwidth(void); 74*2c96f054SLionel Sambuc static intmax_t getintmax(void); 75*2c96f054SLionel Sambuc static uintmax_t getuintmax(void); 76*2c96f054SLionel Sambuc static char *getstr(void); 77*2c96f054SLionel Sambuc static char *mklong(const char *, int); 78*2c96f054SLionel Sambuc static void check_conversion(const char *, const char *); 79*2c96f054SLionel Sambuc static void usage(void); 80*2c96f054SLionel Sambuc 81*2c96f054SLionel Sambuc static void b_count(int); 82*2c96f054SLionel Sambuc static void b_output(int); 83*2c96f054SLionel Sambuc static size_t b_length; 84*2c96f054SLionel Sambuc static char *b_fmt; 85*2c96f054SLionel Sambuc 86*2c96f054SLionel Sambuc static int rval; 87*2c96f054SLionel Sambuc static char **gargv; 88*2c96f054SLionel Sambuc 89*2c96f054SLionel Sambuc #ifdef BUILTIN /* csh builtin */ 90*2c96f054SLionel Sambuc #define main progprintf 91*2c96f054SLionel Sambuc #endif 92*2c96f054SLionel Sambuc 93*2c96f054SLionel Sambuc #ifdef SHELL /* sh (aka ash) builtin */ 94*2c96f054SLionel Sambuc #define main printfcmd 95*2c96f054SLionel Sambuc #include "../../bin/sh/bltin/bltin.h" 96*2c96f054SLionel Sambuc #endif /* SHELL */ 97*2c96f054SLionel Sambuc 98*2c96f054SLionel Sambuc #define PF(f, func) { \ 99*2c96f054SLionel Sambuc if (fieldwidth != -1) { \ 100*2c96f054SLionel Sambuc if (precision != -1) \ 101*2c96f054SLionel Sambuc error = printf(f, fieldwidth, precision, func); \ 102*2c96f054SLionel Sambuc else \ 103*2c96f054SLionel Sambuc error = printf(f, fieldwidth, func); \ 104*2c96f054SLionel Sambuc } else if (precision != -1) \ 105*2c96f054SLionel Sambuc error = printf(f, precision, func); \ 106*2c96f054SLionel Sambuc else \ 107*2c96f054SLionel Sambuc error = printf(f, func); \ 108*2c96f054SLionel Sambuc } 109*2c96f054SLionel Sambuc 110*2c96f054SLionel Sambuc #define APF(cpp, f, func) { \ 111*2c96f054SLionel Sambuc if (fieldwidth != -1) { \ 112*2c96f054SLionel Sambuc if (precision != -1) \ 113*2c96f054SLionel Sambuc error = asprintf(cpp, f, fieldwidth, precision, func); \ 114*2c96f054SLionel Sambuc else \ 115*2c96f054SLionel Sambuc error = asprintf(cpp, f, fieldwidth, func); \ 116*2c96f054SLionel Sambuc } else if (precision != -1) \ 117*2c96f054SLionel Sambuc error = asprintf(cpp, f, precision, func); \ 118*2c96f054SLionel Sambuc else \ 119*2c96f054SLionel Sambuc error = asprintf(cpp, f, func); \ 120*2c96f054SLionel Sambuc } 121*2c96f054SLionel Sambuc 122*2c96f054SLionel Sambuc #ifdef main 123*2c96f054SLionel Sambuc int main(int, char *[]); 124*2c96f054SLionel Sambuc #endif 125*2c96f054SLionel Sambuc int main(int argc, char *argv[]) 126*2c96f054SLionel Sambuc { 127*2c96f054SLionel Sambuc char *fmt, *start; 128*2c96f054SLionel Sambuc int fieldwidth, precision; 129*2c96f054SLionel Sambuc char nextch; 130*2c96f054SLionel Sambuc char *format; 131*2c96f054SLionel Sambuc int ch; 132*2c96f054SLionel Sambuc int error; 133*2c96f054SLionel Sambuc 134*2c96f054SLionel Sambuc #if !defined(SHELL) && !defined(BUILTIN) 135*2c96f054SLionel Sambuc (void)setlocale (LC_ALL, ""); 136*2c96f054SLionel Sambuc #endif 137*2c96f054SLionel Sambuc 138*2c96f054SLionel Sambuc while ((ch = getopt(argc, argv, "")) != -1) { 139*2c96f054SLionel Sambuc switch (ch) { 140*2c96f054SLionel Sambuc case '?': 141*2c96f054SLionel Sambuc default: 142*2c96f054SLionel Sambuc usage(); 143*2c96f054SLionel Sambuc return 1; 144*2c96f054SLionel Sambuc } 145*2c96f054SLionel Sambuc } 146*2c96f054SLionel Sambuc argc -= optind; 147*2c96f054SLionel Sambuc argv += optind; 148*2c96f054SLionel Sambuc 149*2c96f054SLionel Sambuc if (argc < 1) { 150*2c96f054SLionel Sambuc usage(); 151*2c96f054SLionel Sambuc return 1; 152*2c96f054SLionel Sambuc } 153*2c96f054SLionel Sambuc 154*2c96f054SLionel Sambuc format = *argv; 155*2c96f054SLionel Sambuc gargv = ++argv; 156*2c96f054SLionel Sambuc 157*2c96f054SLionel Sambuc #define SKIP1 "#-+ 0'" 158*2c96f054SLionel Sambuc #define SKIP2 "0123456789" 159*2c96f054SLionel Sambuc do { 160*2c96f054SLionel Sambuc /* 161*2c96f054SLionel Sambuc * Basic algorithm is to scan the format string for conversion 162*2c96f054SLionel Sambuc * specifications -- once one is found, find out if the field 163*2c96f054SLionel Sambuc * width or precision is a '*'; if it is, gather up value. 164*2c96f054SLionel Sambuc * Note, format strings are reused as necessary to use up the 165*2c96f054SLionel Sambuc * provided arguments, arguments of zero/null string are 166*2c96f054SLionel Sambuc * provided to use up the format string. 167*2c96f054SLionel Sambuc */ 168*2c96f054SLionel Sambuc 169*2c96f054SLionel Sambuc /* find next format specification */ 170*2c96f054SLionel Sambuc for (fmt = format; (ch = *fmt++) != '\0';) { 171*2c96f054SLionel Sambuc if (ch == '\\') { 172*2c96f054SLionel Sambuc char c_ch; 173*2c96f054SLionel Sambuc fmt = conv_escape(fmt, &c_ch); 174*2c96f054SLionel Sambuc putchar(c_ch); 175*2c96f054SLionel Sambuc continue; 176*2c96f054SLionel Sambuc } 177*2c96f054SLionel Sambuc if (ch != '%' || (*fmt == '%' && ++fmt)) { 178*2c96f054SLionel Sambuc (void)putchar(ch); 179*2c96f054SLionel Sambuc continue; 180*2c96f054SLionel Sambuc } 181*2c96f054SLionel Sambuc 182*2c96f054SLionel Sambuc /* Ok - we've found a format specification, 183*2c96f054SLionel Sambuc Save its address for a later printf(). */ 184*2c96f054SLionel Sambuc start = fmt - 1; 185*2c96f054SLionel Sambuc 186*2c96f054SLionel Sambuc /* skip to field width */ 187*2c96f054SLionel Sambuc fmt += strspn(fmt, SKIP1); 188*2c96f054SLionel Sambuc if (*fmt == '*') { 189*2c96f054SLionel Sambuc fmt++; 190*2c96f054SLionel Sambuc fieldwidth = getwidth(); 191*2c96f054SLionel Sambuc } else 192*2c96f054SLionel Sambuc fieldwidth = -1; 193*2c96f054SLionel Sambuc 194*2c96f054SLionel Sambuc /* skip to possible '.', get following precision */ 195*2c96f054SLionel Sambuc fmt += strspn(fmt, SKIP2); 196*2c96f054SLionel Sambuc if (*fmt == '.') { 197*2c96f054SLionel Sambuc fmt++; 198*2c96f054SLionel Sambuc if (*fmt == '*') { 199*2c96f054SLionel Sambuc fmt++; 200*2c96f054SLionel Sambuc precision = getwidth(); 201*2c96f054SLionel Sambuc } else 202*2c96f054SLionel Sambuc precision = -1; 203*2c96f054SLionel Sambuc } else 204*2c96f054SLionel Sambuc precision = -1; 205*2c96f054SLionel Sambuc 206*2c96f054SLionel Sambuc fmt += strspn(fmt, SKIP2); 207*2c96f054SLionel Sambuc 208*2c96f054SLionel Sambuc ch = *fmt; 209*2c96f054SLionel Sambuc if (!ch) { 210*2c96f054SLionel Sambuc warnx("missing format character"); 211*2c96f054SLionel Sambuc return (1); 212*2c96f054SLionel Sambuc } 213*2c96f054SLionel Sambuc /* null terminate format string to we can use it 214*2c96f054SLionel Sambuc as an argument to printf. */ 215*2c96f054SLionel Sambuc nextch = fmt[1]; 216*2c96f054SLionel Sambuc fmt[1] = 0; 217*2c96f054SLionel Sambuc switch (ch) { 218*2c96f054SLionel Sambuc 219*2c96f054SLionel Sambuc case 'B': { 220*2c96f054SLionel Sambuc const char *p = conv_expand(getstr()); 221*2c96f054SLionel Sambuc if (p == NULL) 222*2c96f054SLionel Sambuc goto out; 223*2c96f054SLionel Sambuc *fmt = 's'; 224*2c96f054SLionel Sambuc PF(start, p); 225*2c96f054SLionel Sambuc if (error < 0) 226*2c96f054SLionel Sambuc goto out; 227*2c96f054SLionel Sambuc break; 228*2c96f054SLionel Sambuc } 229*2c96f054SLionel Sambuc case 'b': { 230*2c96f054SLionel Sambuc /* There has to be a better way to do this, 231*2c96f054SLionel Sambuc * but the string we generate might have 232*2c96f054SLionel Sambuc * embedded nulls. */ 233*2c96f054SLionel Sambuc static char *a, *t; 234*2c96f054SLionel Sambuc char *cp = getstr(); 235*2c96f054SLionel Sambuc /* Free on entry in case shell longjumped out */ 236*2c96f054SLionel Sambuc if (a != NULL) 237*2c96f054SLionel Sambuc free(a); 238*2c96f054SLionel Sambuc a = NULL; 239*2c96f054SLionel Sambuc if (t != NULL) 240*2c96f054SLionel Sambuc free(t); 241*2c96f054SLionel Sambuc t = NULL; 242*2c96f054SLionel Sambuc /* Count number of bytes we want to output */ 243*2c96f054SLionel Sambuc b_length = 0; 244*2c96f054SLionel Sambuc conv_escape_str(cp, b_count); 245*2c96f054SLionel Sambuc t = malloc(b_length + 1); 246*2c96f054SLionel Sambuc if (t == NULL) 247*2c96f054SLionel Sambuc goto out; 248*2c96f054SLionel Sambuc (void)memset(t, 'x', b_length); 249*2c96f054SLionel Sambuc t[b_length] = 0; 250*2c96f054SLionel Sambuc /* Get printf to calculate the lengths */ 251*2c96f054SLionel Sambuc *fmt = 's'; 252*2c96f054SLionel Sambuc APF(&a, start, t); 253*2c96f054SLionel Sambuc if (error == -1) 254*2c96f054SLionel Sambuc goto out; 255*2c96f054SLionel Sambuc b_fmt = a; 256*2c96f054SLionel Sambuc /* Output leading spaces and data bytes */ 257*2c96f054SLionel Sambuc conv_escape_str(cp, b_output); 258*2c96f054SLionel Sambuc /* Add any trailing spaces */ 259*2c96f054SLionel Sambuc printf("%s", b_fmt); 260*2c96f054SLionel Sambuc break; 261*2c96f054SLionel Sambuc } 262*2c96f054SLionel Sambuc case 'c': { 263*2c96f054SLionel Sambuc char p = getchr(); 264*2c96f054SLionel Sambuc PF(start, p); 265*2c96f054SLionel Sambuc if (error < 0) 266*2c96f054SLionel Sambuc goto out; 267*2c96f054SLionel Sambuc break; 268*2c96f054SLionel Sambuc } 269*2c96f054SLionel Sambuc case 's': { 270*2c96f054SLionel Sambuc char *p = getstr(); 271*2c96f054SLionel Sambuc PF(start, p); 272*2c96f054SLionel Sambuc if (error < 0) 273*2c96f054SLionel Sambuc goto out; 274*2c96f054SLionel Sambuc break; 275*2c96f054SLionel Sambuc } 276*2c96f054SLionel Sambuc case 'd': 277*2c96f054SLionel Sambuc case 'i': { 278*2c96f054SLionel Sambuc intmax_t p = getintmax(); 279*2c96f054SLionel Sambuc char *f = mklong(start, ch); 280*2c96f054SLionel Sambuc PF(f, p); 281*2c96f054SLionel Sambuc if (error < 0) 282*2c96f054SLionel Sambuc goto out; 283*2c96f054SLionel Sambuc break; 284*2c96f054SLionel Sambuc } 285*2c96f054SLionel Sambuc case 'o': 286*2c96f054SLionel Sambuc case 'u': 287*2c96f054SLionel Sambuc case 'x': 288*2c96f054SLionel Sambuc case 'X': { 289*2c96f054SLionel Sambuc uintmax_t p = getuintmax(); 290*2c96f054SLionel Sambuc char *f = mklong(start, ch); 291*2c96f054SLionel Sambuc PF(f, p); 292*2c96f054SLionel Sambuc if (error < 0) 293*2c96f054SLionel Sambuc goto out; 294*2c96f054SLionel Sambuc break; 295*2c96f054SLionel Sambuc } 296*2c96f054SLionel Sambuc case 'e': 297*2c96f054SLionel Sambuc case 'E': 298*2c96f054SLionel Sambuc case 'f': 299*2c96f054SLionel Sambuc case 'g': 300*2c96f054SLionel Sambuc case 'G': { 301*2c96f054SLionel Sambuc double p = getdouble(); 302*2c96f054SLionel Sambuc PF(start, p); 303*2c96f054SLionel Sambuc if (error < 0) 304*2c96f054SLionel Sambuc goto out; 305*2c96f054SLionel Sambuc break; 306*2c96f054SLionel Sambuc } 307*2c96f054SLionel Sambuc default: 308*2c96f054SLionel Sambuc warnx("%s: invalid directive", start); 309*2c96f054SLionel Sambuc return 1; 310*2c96f054SLionel Sambuc } 311*2c96f054SLionel Sambuc *fmt++ = ch; 312*2c96f054SLionel Sambuc *fmt = nextch; 313*2c96f054SLionel Sambuc /* escape if a \c was encountered */ 314*2c96f054SLionel Sambuc if (rval & 0x100) 315*2c96f054SLionel Sambuc return rval & ~0x100; 316*2c96f054SLionel Sambuc } 317*2c96f054SLionel Sambuc } while (gargv != argv && *gargv); 318*2c96f054SLionel Sambuc 319*2c96f054SLionel Sambuc return rval & ~0x100; 320*2c96f054SLionel Sambuc out: 321*2c96f054SLionel Sambuc warn("print failed"); 322*2c96f054SLionel Sambuc return 1; 323*2c96f054SLionel Sambuc } 324*2c96f054SLionel Sambuc 325*2c96f054SLionel Sambuc /* helper functions for conv_escape_str */ 326*2c96f054SLionel Sambuc 327*2c96f054SLionel Sambuc static void 328*2c96f054SLionel Sambuc /*ARGSUSED*/ 329*2c96f054SLionel Sambuc b_count(int ch) 330*2c96f054SLionel Sambuc { 331*2c96f054SLionel Sambuc b_length++; 332*2c96f054SLionel Sambuc } 333*2c96f054SLionel Sambuc 334*2c96f054SLionel Sambuc /* Output one converted character for every 'x' in the 'format' */ 335*2c96f054SLionel Sambuc 336*2c96f054SLionel Sambuc static void 337*2c96f054SLionel Sambuc b_output(int ch) 338*2c96f054SLionel Sambuc { 339*2c96f054SLionel Sambuc for (;;) { 340*2c96f054SLionel Sambuc switch (*b_fmt++) { 341*2c96f054SLionel Sambuc case 0: 342*2c96f054SLionel Sambuc b_fmt--; 343*2c96f054SLionel Sambuc return; 344*2c96f054SLionel Sambuc case ' ': 345*2c96f054SLionel Sambuc putchar(' '); 346*2c96f054SLionel Sambuc break; 347*2c96f054SLionel Sambuc default: 348*2c96f054SLionel Sambuc putchar(ch); 349*2c96f054SLionel Sambuc return; 350*2c96f054SLionel Sambuc } 351*2c96f054SLionel Sambuc } 352*2c96f054SLionel Sambuc } 353*2c96f054SLionel Sambuc 354*2c96f054SLionel Sambuc 355*2c96f054SLionel Sambuc /* 356*2c96f054SLionel Sambuc * Print SysV echo(1) style escape string 357*2c96f054SLionel Sambuc * Halts processing string if a \c escape is encountered. 358*2c96f054SLionel Sambuc */ 359*2c96f054SLionel Sambuc static void 360*2c96f054SLionel Sambuc conv_escape_str(char *str, void (*do_putchar)(int)) 361*2c96f054SLionel Sambuc { 362*2c96f054SLionel Sambuc int value; 363*2c96f054SLionel Sambuc int ch; 364*2c96f054SLionel Sambuc char c; 365*2c96f054SLionel Sambuc 366*2c96f054SLionel Sambuc while ((ch = *str++) != '\0') { 367*2c96f054SLionel Sambuc if (ch != '\\') { 368*2c96f054SLionel Sambuc do_putchar(ch); 369*2c96f054SLionel Sambuc continue; 370*2c96f054SLionel Sambuc } 371*2c96f054SLionel Sambuc 372*2c96f054SLionel Sambuc ch = *str++; 373*2c96f054SLionel Sambuc if (ch == 'c') { 374*2c96f054SLionel Sambuc /* \c as in SYSV echo - abort all processing.... */ 375*2c96f054SLionel Sambuc rval |= 0x100; 376*2c96f054SLionel Sambuc break; 377*2c96f054SLionel Sambuc } 378*2c96f054SLionel Sambuc 379*2c96f054SLionel Sambuc /* 380*2c96f054SLionel Sambuc * %b string octal constants are not like those in C. 381*2c96f054SLionel Sambuc * They start with a \0, and are followed by 0, 1, 2, 382*2c96f054SLionel Sambuc * or 3 octal digits. 383*2c96f054SLionel Sambuc */ 384*2c96f054SLionel Sambuc if (ch == '0') { 385*2c96f054SLionel Sambuc int octnum = 0, i; 386*2c96f054SLionel Sambuc for (i = 0; i < 3; i++) { 387*2c96f054SLionel Sambuc if (!isdigit((unsigned char)*str) || *str > '7') 388*2c96f054SLionel Sambuc break; 389*2c96f054SLionel Sambuc octnum = (octnum << 3) | (*str++ - '0'); 390*2c96f054SLionel Sambuc } 391*2c96f054SLionel Sambuc do_putchar(octnum); 392*2c96f054SLionel Sambuc continue; 393*2c96f054SLionel Sambuc } 394*2c96f054SLionel Sambuc 395*2c96f054SLionel Sambuc /* \[M][^|-]C as defined by vis(3) */ 396*2c96f054SLionel Sambuc if (ch == 'M' && *str == '-') { 397*2c96f054SLionel Sambuc do_putchar(0200 | str[1]); 398*2c96f054SLionel Sambuc str += 2; 399*2c96f054SLionel Sambuc continue; 400*2c96f054SLionel Sambuc } 401*2c96f054SLionel Sambuc if (ch == 'M' && *str == '^') { 402*2c96f054SLionel Sambuc str++; 403*2c96f054SLionel Sambuc value = 0200; 404*2c96f054SLionel Sambuc ch = '^'; 405*2c96f054SLionel Sambuc } else 406*2c96f054SLionel Sambuc value = 0; 407*2c96f054SLionel Sambuc if (ch == '^') { 408*2c96f054SLionel Sambuc ch = *str++; 409*2c96f054SLionel Sambuc if (ch == '?') 410*2c96f054SLionel Sambuc value |= 0177; 411*2c96f054SLionel Sambuc else 412*2c96f054SLionel Sambuc value |= ch & 037; 413*2c96f054SLionel Sambuc do_putchar(value); 414*2c96f054SLionel Sambuc continue; 415*2c96f054SLionel Sambuc } 416*2c96f054SLionel Sambuc 417*2c96f054SLionel Sambuc /* Finally test for sequences valid in the format string */ 418*2c96f054SLionel Sambuc str = conv_escape(str - 1, &c); 419*2c96f054SLionel Sambuc do_putchar(c); 420*2c96f054SLionel Sambuc } 421*2c96f054SLionel Sambuc } 422*2c96f054SLionel Sambuc 423*2c96f054SLionel Sambuc /* 424*2c96f054SLionel Sambuc * Print "standard" escape characters 425*2c96f054SLionel Sambuc */ 426*2c96f054SLionel Sambuc static char * 427*2c96f054SLionel Sambuc conv_escape(char *str, char *conv_ch) 428*2c96f054SLionel Sambuc { 429*2c96f054SLionel Sambuc int value; 430*2c96f054SLionel Sambuc int ch; 431*2c96f054SLionel Sambuc char num_buf[4], *num_end; 432*2c96f054SLionel Sambuc 433*2c96f054SLionel Sambuc ch = *str++; 434*2c96f054SLionel Sambuc 435*2c96f054SLionel Sambuc switch (ch) { 436*2c96f054SLionel Sambuc case '0': case '1': case '2': case '3': 437*2c96f054SLionel Sambuc case '4': case '5': case '6': case '7': 438*2c96f054SLionel Sambuc num_buf[0] = ch; 439*2c96f054SLionel Sambuc ch = str[0]; 440*2c96f054SLionel Sambuc num_buf[1] = ch; 441*2c96f054SLionel Sambuc num_buf[2] = ch ? str[1] : 0; 442*2c96f054SLionel Sambuc num_buf[3] = 0; 443*2c96f054SLionel Sambuc value = strtoul(num_buf, &num_end, 8); 444*2c96f054SLionel Sambuc str += num_end - (num_buf + 1); 445*2c96f054SLionel Sambuc break; 446*2c96f054SLionel Sambuc 447*2c96f054SLionel Sambuc case 'x': 448*2c96f054SLionel Sambuc /* Hexadecimal character constants are not required to be 449*2c96f054SLionel Sambuc supported (by SuS v1) because there is no consistent 450*2c96f054SLionel Sambuc way to detect the end of the constant. 451*2c96f054SLionel Sambuc Supporting 2 byte constants is a compromise. */ 452*2c96f054SLionel Sambuc ch = str[0]; 453*2c96f054SLionel Sambuc num_buf[0] = ch; 454*2c96f054SLionel Sambuc num_buf[1] = ch ? str[1] : 0; 455*2c96f054SLionel Sambuc num_buf[2] = 0; 456*2c96f054SLionel Sambuc value = strtoul(num_buf, &num_end, 16); 457*2c96f054SLionel Sambuc str += num_end - num_buf; 458*2c96f054SLionel Sambuc break; 459*2c96f054SLionel Sambuc 460*2c96f054SLionel Sambuc case '\\': value = '\\'; break; /* backslash */ 461*2c96f054SLionel Sambuc case '\'': value = '\''; break; /* single quote */ 462*2c96f054SLionel Sambuc case '"': value = '"'; break; /* double quote */ 463*2c96f054SLionel Sambuc case 'a': value = '\a'; break; /* alert */ 464*2c96f054SLionel Sambuc case 'b': value = '\b'; break; /* backspace */ 465*2c96f054SLionel Sambuc case 'e': value = ESCAPE; break; /* escape */ 466*2c96f054SLionel Sambuc case 'f': value = '\f'; break; /* form-feed */ 467*2c96f054SLionel Sambuc case 'n': value = '\n'; break; /* newline */ 468*2c96f054SLionel Sambuc case 'r': value = '\r'; break; /* carriage-return */ 469*2c96f054SLionel Sambuc case 't': value = '\t'; break; /* tab */ 470*2c96f054SLionel Sambuc case 'v': value = '\v'; break; /* vertical-tab */ 471*2c96f054SLionel Sambuc 472*2c96f054SLionel Sambuc default: 473*2c96f054SLionel Sambuc warnx("unknown escape sequence `\\%c'", ch); 474*2c96f054SLionel Sambuc rval = 1; 475*2c96f054SLionel Sambuc value = ch; 476*2c96f054SLionel Sambuc break; 477*2c96f054SLionel Sambuc } 478*2c96f054SLionel Sambuc 479*2c96f054SLionel Sambuc *conv_ch = value; 480*2c96f054SLionel Sambuc return str; 481*2c96f054SLionel Sambuc } 482*2c96f054SLionel Sambuc 483*2c96f054SLionel Sambuc /* expand a string so that everything is printable */ 484*2c96f054SLionel Sambuc 485*2c96f054SLionel Sambuc static char * 486*2c96f054SLionel Sambuc conv_expand(const char *str) 487*2c96f054SLionel Sambuc { 488*2c96f054SLionel Sambuc static char *conv_str; 489*2c96f054SLionel Sambuc char *cp; 490*2c96f054SLionel Sambuc int ch; 491*2c96f054SLionel Sambuc 492*2c96f054SLionel Sambuc if (conv_str) 493*2c96f054SLionel Sambuc free(conv_str); 494*2c96f054SLionel Sambuc /* get a buffer that is definitely large enough.... */ 495*2c96f054SLionel Sambuc conv_str = malloc(4 * strlen(str) + 1); 496*2c96f054SLionel Sambuc if (!conv_str) 497*2c96f054SLionel Sambuc return NULL; 498*2c96f054SLionel Sambuc cp = conv_str; 499*2c96f054SLionel Sambuc 500*2c96f054SLionel Sambuc while ((ch = *(const unsigned char *)str++) != '\0') { 501*2c96f054SLionel Sambuc switch (ch) { 502*2c96f054SLionel Sambuc /* Use C escapes for expected control characters */ 503*2c96f054SLionel Sambuc case '\\': ch = '\\'; break; /* backslash */ 504*2c96f054SLionel Sambuc case '\'': ch = '\''; break; /* single quote */ 505*2c96f054SLionel Sambuc case '"': ch = '"'; break; /* double quote */ 506*2c96f054SLionel Sambuc case '\a': ch = 'a'; break; /* alert */ 507*2c96f054SLionel Sambuc case '\b': ch = 'b'; break; /* backspace */ 508*2c96f054SLionel Sambuc case ESCAPE: ch = 'e'; break; /* escape */ 509*2c96f054SLionel Sambuc case '\f': ch = 'f'; break; /* form-feed */ 510*2c96f054SLionel Sambuc case '\n': ch = 'n'; break; /* newline */ 511*2c96f054SLionel Sambuc case '\r': ch = 'r'; break; /* carriage-return */ 512*2c96f054SLionel Sambuc case '\t': ch = 't'; break; /* tab */ 513*2c96f054SLionel Sambuc case '\v': ch = 'v'; break; /* vertical-tab */ 514*2c96f054SLionel Sambuc default: 515*2c96f054SLionel Sambuc /* Copy anything printable */ 516*2c96f054SLionel Sambuc if (isprint(ch)) { 517*2c96f054SLionel Sambuc *cp++ = ch; 518*2c96f054SLionel Sambuc continue; 519*2c96f054SLionel Sambuc } 520*2c96f054SLionel Sambuc /* Use vis(3) encodings for the rest */ 521*2c96f054SLionel Sambuc *cp++ = '\\'; 522*2c96f054SLionel Sambuc if (ch & 0200) { 523*2c96f054SLionel Sambuc *cp++ = 'M'; 524*2c96f054SLionel Sambuc ch &= ~0200; 525*2c96f054SLionel Sambuc } 526*2c96f054SLionel Sambuc if (ch == 0177) { 527*2c96f054SLionel Sambuc *cp++ = '^'; 528*2c96f054SLionel Sambuc *cp++ = '?'; 529*2c96f054SLionel Sambuc continue; 530*2c96f054SLionel Sambuc } 531*2c96f054SLionel Sambuc if (ch < 040) { 532*2c96f054SLionel Sambuc *cp++ = '^'; 533*2c96f054SLionel Sambuc *cp++ = ch | 0100; 534*2c96f054SLionel Sambuc continue; 535*2c96f054SLionel Sambuc } 536*2c96f054SLionel Sambuc *cp++ = '-'; 537*2c96f054SLionel Sambuc *cp++ = ch; 538*2c96f054SLionel Sambuc continue; 539*2c96f054SLionel Sambuc } 540*2c96f054SLionel Sambuc *cp++ = '\\'; 541*2c96f054SLionel Sambuc *cp++ = ch; 542*2c96f054SLionel Sambuc } 543*2c96f054SLionel Sambuc 544*2c96f054SLionel Sambuc *cp = 0; 545*2c96f054SLionel Sambuc return conv_str; 546*2c96f054SLionel Sambuc } 547*2c96f054SLionel Sambuc 548*2c96f054SLionel Sambuc static char * 549*2c96f054SLionel Sambuc mklong(const char *str, int ch) 550*2c96f054SLionel Sambuc { 551*2c96f054SLionel Sambuc static char copy[64]; 552*2c96f054SLionel Sambuc size_t len; 553*2c96f054SLionel Sambuc 554*2c96f054SLionel Sambuc len = strlen(str) + 2; 555*2c96f054SLionel Sambuc if (len > sizeof copy) { 556*2c96f054SLionel Sambuc warnx("format %s too complex\n", str); 557*2c96f054SLionel Sambuc len = 4; 558*2c96f054SLionel Sambuc } 559*2c96f054SLionel Sambuc (void)memmove(copy, str, len - 3); 560*2c96f054SLionel Sambuc copy[len - 3] = 'j'; 561*2c96f054SLionel Sambuc copy[len - 2] = ch; 562*2c96f054SLionel Sambuc copy[len - 1] = '\0'; 563*2c96f054SLionel Sambuc return copy; 564*2c96f054SLionel Sambuc } 565*2c96f054SLionel Sambuc 566*2c96f054SLionel Sambuc static int 567*2c96f054SLionel Sambuc getchr(void) 568*2c96f054SLionel Sambuc { 569*2c96f054SLionel Sambuc if (!*gargv) 570*2c96f054SLionel Sambuc return 0; 571*2c96f054SLionel Sambuc return (int)**gargv++; 572*2c96f054SLionel Sambuc } 573*2c96f054SLionel Sambuc 574*2c96f054SLionel Sambuc static char * 575*2c96f054SLionel Sambuc getstr(void) 576*2c96f054SLionel Sambuc { 577*2c96f054SLionel Sambuc static char empty[] = ""; 578*2c96f054SLionel Sambuc if (!*gargv) 579*2c96f054SLionel Sambuc return empty; 580*2c96f054SLionel Sambuc return *gargv++; 581*2c96f054SLionel Sambuc } 582*2c96f054SLionel Sambuc 583*2c96f054SLionel Sambuc static int 584*2c96f054SLionel Sambuc getwidth(void) 585*2c96f054SLionel Sambuc { 586*2c96f054SLionel Sambuc long val; 587*2c96f054SLionel Sambuc char *s, *ep; 588*2c96f054SLionel Sambuc 589*2c96f054SLionel Sambuc s = *gargv; 590*2c96f054SLionel Sambuc if (!*gargv) 591*2c96f054SLionel Sambuc return (0); 592*2c96f054SLionel Sambuc gargv++; 593*2c96f054SLionel Sambuc 594*2c96f054SLionel Sambuc errno = 0; 595*2c96f054SLionel Sambuc val = strtoul(s, &ep, 0); 596*2c96f054SLionel Sambuc check_conversion(s, ep); 597*2c96f054SLionel Sambuc 598*2c96f054SLionel Sambuc /* Arbitrarily 'restrict' field widths to 1Mbyte */ 599*2c96f054SLionel Sambuc if (val < 0 || val > 1 << 20) { 600*2c96f054SLionel Sambuc warnx("%s: invalid field width", s); 601*2c96f054SLionel Sambuc return 0; 602*2c96f054SLionel Sambuc } 603*2c96f054SLionel Sambuc 604*2c96f054SLionel Sambuc return val; 605*2c96f054SLionel Sambuc } 606*2c96f054SLionel Sambuc 607*2c96f054SLionel Sambuc static intmax_t 608*2c96f054SLionel Sambuc getintmax(void) 609*2c96f054SLionel Sambuc { 610*2c96f054SLionel Sambuc intmax_t val; 611*2c96f054SLionel Sambuc char *cp, *ep; 612*2c96f054SLionel Sambuc 613*2c96f054SLionel Sambuc cp = *gargv; 614*2c96f054SLionel Sambuc if (cp == NULL) 615*2c96f054SLionel Sambuc return 0; 616*2c96f054SLionel Sambuc gargv++; 617*2c96f054SLionel Sambuc 618*2c96f054SLionel Sambuc if (*cp == '\"' || *cp == '\'') 619*2c96f054SLionel Sambuc return *(cp+1); 620*2c96f054SLionel Sambuc 621*2c96f054SLionel Sambuc errno = 0; 622*2c96f054SLionel Sambuc val = strtoimax(cp, &ep, 0); 623*2c96f054SLionel Sambuc check_conversion(cp, ep); 624*2c96f054SLionel Sambuc return val; 625*2c96f054SLionel Sambuc } 626*2c96f054SLionel Sambuc 627*2c96f054SLionel Sambuc static uintmax_t 628*2c96f054SLionel Sambuc getuintmax(void) 629*2c96f054SLionel Sambuc { 630*2c96f054SLionel Sambuc uintmax_t val; 631*2c96f054SLionel Sambuc char *cp, *ep; 632*2c96f054SLionel Sambuc 633*2c96f054SLionel Sambuc cp = *gargv; 634*2c96f054SLionel Sambuc if (cp == NULL) 635*2c96f054SLionel Sambuc return 0; 636*2c96f054SLionel Sambuc gargv++; 637*2c96f054SLionel Sambuc 638*2c96f054SLionel Sambuc if (*cp == '\"' || *cp == '\'') 639*2c96f054SLionel Sambuc return *(cp + 1); 640*2c96f054SLionel Sambuc 641*2c96f054SLionel Sambuc /* strtoumax won't error -ve values */ 642*2c96f054SLionel Sambuc while (isspace(*(unsigned char *)cp)) 643*2c96f054SLionel Sambuc cp++; 644*2c96f054SLionel Sambuc if (*cp == '-') { 645*2c96f054SLionel Sambuc warnx("%s: expected positive numeric value", cp); 646*2c96f054SLionel Sambuc rval = 1; 647*2c96f054SLionel Sambuc return 0; 648*2c96f054SLionel Sambuc } 649*2c96f054SLionel Sambuc 650*2c96f054SLionel Sambuc errno = 0; 651*2c96f054SLionel Sambuc val = strtoumax(cp, &ep, 0); 652*2c96f054SLionel Sambuc check_conversion(cp, ep); 653*2c96f054SLionel Sambuc return val; 654*2c96f054SLionel Sambuc } 655*2c96f054SLionel Sambuc 656*2c96f054SLionel Sambuc static double 657*2c96f054SLionel Sambuc getdouble(void) 658*2c96f054SLionel Sambuc { 659*2c96f054SLionel Sambuc double val; 660*2c96f054SLionel Sambuc char *ep; 661*2c96f054SLionel Sambuc 662*2c96f054SLionel Sambuc if (!*gargv) 663*2c96f054SLionel Sambuc return (0.0); 664*2c96f054SLionel Sambuc 665*2c96f054SLionel Sambuc if (**gargv == '\"' || **gargv == '\'') 666*2c96f054SLionel Sambuc return (double) *((*gargv++)+1); 667*2c96f054SLionel Sambuc 668*2c96f054SLionel Sambuc errno = 0; 669*2c96f054SLionel Sambuc val = strtod(*gargv, &ep); 670*2c96f054SLionel Sambuc check_conversion(*gargv++, ep); 671*2c96f054SLionel Sambuc return val; 672*2c96f054SLionel Sambuc } 673*2c96f054SLionel Sambuc 674*2c96f054SLionel Sambuc static void 675*2c96f054SLionel Sambuc check_conversion(const char *s, const char *ep) 676*2c96f054SLionel Sambuc { 677*2c96f054SLionel Sambuc if (*ep) { 678*2c96f054SLionel Sambuc if (ep == s) 679*2c96f054SLionel Sambuc warnx("%s: expected numeric value", s); 680*2c96f054SLionel Sambuc else 681*2c96f054SLionel Sambuc warnx("%s: not completely converted", s); 682*2c96f054SLionel Sambuc rval = 1; 683*2c96f054SLionel Sambuc } else if (errno == ERANGE) { 684*2c96f054SLionel Sambuc warnx("%s: %s", s, strerror(ERANGE)); 685*2c96f054SLionel Sambuc rval = 1; 686*2c96f054SLionel Sambuc } 687*2c96f054SLionel Sambuc } 688*2c96f054SLionel Sambuc 689*2c96f054SLionel Sambuc static void 690*2c96f054SLionel Sambuc usage(void) 691*2c96f054SLionel Sambuc { 692*2c96f054SLionel Sambuc (void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname()); 693*2c96f054SLionel Sambuc } 694