printf.c revision 216310
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1989, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 34216310Sjilles#ifndef SHELL 351590Srgrimes#ifndef lint 3620409Sstevestatic char const copyright[] = 371590Srgrimes"@(#) Copyright (c) 1989, 1993\n\ 381590Srgrimes The Regents of the University of California. All rights reserved.\n"; 391590Srgrimes#endif /* not lint */ 401590Srgrimes#endif 411590Srgrimes 421590Srgrimes#ifndef lint 4365429Simp#if 0 4420409Sstevestatic char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93"; 4565429Simp#endif 4659435Scracauerstatic const char rcsid[] = 4759435Scracauer "$FreeBSD: head/usr.bin/printf/printf.c 216310 2010-12-08 22:13:27Z jilles $"; 481590Srgrimes#endif /* not lint */ 491590Srgrimes 501590Srgrimes#include <sys/types.h> 511590Srgrimes 521590Srgrimes#include <err.h> 531590Srgrimes#include <errno.h> 54148721Sstefanf#include <inttypes.h> 551590Srgrimes#include <limits.h> 561590Srgrimes#include <stdio.h> 571590Srgrimes#include <stdlib.h> 581590Srgrimes#include <string.h> 5927966Ssteve#include <unistd.h> 601590Srgrimes 611590Srgrimes#ifdef SHELL 621590Srgrimes#define main printfcmd 6312730Sjoerg#include "bltin/bltin.h" 6470256Sben#include "memalloc.h" 65215520Sjilles#include "error.h" 6641582Sbde#else 6741582Sbde#define warnx1(a, b, c) warnx(a) 6841582Sbde#define warnx2(a, b, c) warnx(a, b) 6941582Sbde#define warnx3(a, b, c) warnx(a, b, c) 701590Srgrimes#endif 711590Srgrimes 7272304Sache#include <locale.h> 7372304Sache 7495409Stjr#define PF(f, func) do { \ 7518613Speter char *b = NULL; \ 7698424Stjr if (havewidth) \ 7798424Stjr if (haveprec) \ 7818613Speter (void)asprintf(&b, f, fieldwidth, precision, func); \ 791590Srgrimes else \ 8018613Speter (void)asprintf(&b, f, fieldwidth, func); \ 8198424Stjr else if (haveprec) \ 8218613Speter (void)asprintf(&b, f, precision, func); \ 831590Srgrimes else \ 8418613Speter (void)asprintf(&b, f, func); \ 8518613Speter if (b) { \ 8618613Speter (void)fputs(b, stdout); \ 8718613Speter free(b); \ 8818613Speter } \ 8995409Stjr} while (0) 901590Srgrimes 9192921Simpstatic int asciicode(void); 92215520Sjillesstatic char *printf_doformat(char *, int *); 93145078Sstefanfstatic int escape(char *, int, size_t *); 9492921Simpstatic int getchr(void); 95143906Sdasstatic int getfloating(long double *, int); 9692921Simpstatic int getint(int *); 97148721Sstefanfstatic int getnum(intmax_t *, uintmax_t *, int); 9895409Stjrstatic const char 9995409Stjr *getstr(void); 100148721Sstefanfstatic char *mknum(char *, int); 10192921Simpstatic void usage(void); 1021590Srgrimes 1031590Srgrimesstatic char **gargv; 1041590Srgrimes 1051590Srgrimesint 106102944Sdwmalonemain(int argc, char *argv[]) 1071590Srgrimes{ 108145078Sstefanf size_t len; 109145061Sstefanf int ch, chopped, end, rval; 110145061Sstefanf char *format, *fmt, *start; 1111590Srgrimes 112216310Sjilles#ifndef SHELL 11372304Sache (void) setlocale(LC_NUMERIC, ""); 11472304Sache#endif 115215520Sjilles#ifdef SHELL 116215520Sjilles optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 117215520Sjilles#endif 11824360Simp while ((ch = getopt(argc, argv, "")) != -1) 1191590Srgrimes switch (ch) { 1201590Srgrimes case '?': 1211590Srgrimes default: 1221590Srgrimes usage(); 1231590Srgrimes return (1); 1241590Srgrimes } 1251590Srgrimes argc -= optind; 1261590Srgrimes argv += optind; 1271590Srgrimes 1281590Srgrimes if (argc < 1) { 1291590Srgrimes usage(); 1301590Srgrimes return (1); 1311590Srgrimes } 1321590Srgrimes 133215520Sjilles#ifdef SHELL 134215520Sjilles INTOFF; 135215520Sjilles#endif 1361590Srgrimes /* 1371590Srgrimes * Basic algorithm is to scan the format string for conversion 1381590Srgrimes * specifications -- once one is found, find out if the field 1391590Srgrimes * width or precision is a '*'; if it is, gather up value. Note, 1401590Srgrimes * format strings are reused as necessary to use up the provided 1411590Srgrimes * arguments, arguments of zero/null string are provided to use 1421590Srgrimes * up the format string. 1431590Srgrimes */ 144145078Sstefanf fmt = format = *argv; 145145078Sstefanf chopped = escape(fmt, 1, &len); /* backslash interpretation */ 146145061Sstefanf rval = end = 0; 1471590Srgrimes gargv = ++argv; 1481590Srgrimes for (;;) { 149145061Sstefanf start = fmt; 150145078Sstefanf while (fmt < format + len) { 151145061Sstefanf if (fmt[0] == '%') { 152145061Sstefanf fwrite(start, 1, fmt - start, stdout); 153145061Sstefanf if (fmt[1] == '%') { 154145061Sstefanf /* %% prints a % */ 155145061Sstefanf putchar('%'); 156145061Sstefanf fmt += 2; 157145061Sstefanf } else { 158215520Sjilles fmt = printf_doformat(fmt, &rval); 159215520Sjilles if (fmt == NULL) { 160215520Sjilles#ifdef SHELL 161215520Sjilles INTON; 162215520Sjilles#endif 163145061Sstefanf return (1); 164215520Sjilles } 165145061Sstefanf end = 0; 16695300Sjmallett } 167145061Sstefanf start = fmt; 168145061Sstefanf } else 16998420Stjr fmt++; 1701590Srgrimes } 1711590Srgrimes 172145061Sstefanf if (end == 1) { 173145061Sstefanf warnx1("missing format character", NULL, NULL); 174215520Sjilles#ifdef SHELL 175215520Sjilles INTON; 176215520Sjilles#endif 177145061Sstefanf return (1); 178145061Sstefanf } 179145061Sstefanf fwrite(start, 1, fmt - start, stdout); 180215520Sjilles if (chopped || !*gargv) { 181215520Sjilles#ifdef SHELL 182215520Sjilles INTON; 183215520Sjilles#endif 184145061Sstefanf return (rval); 185215520Sjilles } 186145061Sstefanf /* Restart at the beginning of the format string. */ 187145061Sstefanf fmt = format; 188145061Sstefanf end = 1; 189145061Sstefanf } 190145061Sstefanf /* NOTREACHED */ 191145061Sstefanf} 192145061Sstefanf 193145061Sstefanf 194145061Sstefanfstatic char * 195215520Sjillesprintf_doformat(char *start, int *rval) 196145061Sstefanf{ 197145061Sstefanf static const char skip1[] = "#'-+ 0"; 198145061Sstefanf static const char skip2[] = "0123456789"; 199145061Sstefanf char *fmt; 200145061Sstefanf int fieldwidth, haveprec, havewidth, mod_ldbl, precision; 201145061Sstefanf char convch, nextch; 202145061Sstefanf 203145061Sstefanf fmt = start + 1; 204145061Sstefanf /* skip to field width */ 205145061Sstefanf fmt += strspn(fmt, skip1); 206145061Sstefanf if (*fmt == '*') { 207145061Sstefanf if (getint(&fieldwidth)) 208145061Sstefanf return (NULL); 209145061Sstefanf havewidth = 1; 210145061Sstefanf ++fmt; 211145061Sstefanf } else { 212145061Sstefanf havewidth = 0; 213145061Sstefanf 214145061Sstefanf /* skip to possible '.', get following precision */ 215145061Sstefanf fmt += strspn(fmt, skip2); 216145061Sstefanf } 217145061Sstefanf if (*fmt == '.') { 218145061Sstefanf /* precision present? */ 219145061Sstefanf ++fmt; 2201590Srgrimes if (*fmt == '*') { 221145061Sstefanf if (getint(&precision)) 222145061Sstefanf return (NULL); 223145061Sstefanf haveprec = 1; 2248323Sjoerg ++fmt; 2258323Sjoerg } else { 226145061Sstefanf haveprec = 0; 2271590Srgrimes 228145061Sstefanf /* skip to conversion char */ 229144902Sstefanf fmt += strspn(fmt, skip2); 2308323Sjoerg } 231145061Sstefanf } else 232145061Sstefanf haveprec = 0; 233145061Sstefanf if (!*fmt) { 234145061Sstefanf warnx1("missing format character", NULL, NULL); 235145061Sstefanf return (NULL); 236145061Sstefanf } 2378323Sjoerg 238145061Sstefanf /* 239145061Sstefanf * Look for a length modifier. POSIX doesn't have these, so 240145061Sstefanf * we only support them for floating-point conversions, which 241145061Sstefanf * are extensions. This is useful because the L modifier can 242145061Sstefanf * be used to gain extra range and precision, while omitting 243145061Sstefanf * it is more likely to produce consistent results on different 244145061Sstefanf * architectures. This is not so important for integers 245145061Sstefanf * because overflow is the only bad thing that can happen to 246145061Sstefanf * them, but consider the command printf %a 1.1 247145061Sstefanf */ 248145061Sstefanf if (*fmt == 'L') { 249145061Sstefanf mod_ldbl = 1; 250145061Sstefanf fmt++; 251145061Sstefanf if (!strchr("aAeEfFgG", *fmt)) { 252145061Sstefanf warnx2("bad modifier L for %%%c", *fmt, NULL); 253145061Sstefanf return (NULL); 2541590Srgrimes } 255145061Sstefanf } else { 256145061Sstefanf mod_ldbl = 0; 257145061Sstefanf } 2581590Srgrimes 259145061Sstefanf convch = *fmt; 260145061Sstefanf nextch = *++fmt; 261145061Sstefanf *fmt = '\0'; 262145061Sstefanf switch (convch) { 263145061Sstefanf case 'b': { 264145078Sstefanf size_t len; 265145061Sstefanf char *p; 266145061Sstefanf int getout; 267143906Sdas 26895409Stjr#ifdef SHELL 269145061Sstefanf p = savestr(getstr()); 27095409Stjr#else 271145061Sstefanf p = strdup(getstr()); 27295409Stjr#endif 273145061Sstefanf if (p == NULL) { 274145061Sstefanf warnx2("%s", strerror(ENOMEM), NULL); 275145061Sstefanf return (NULL); 276145061Sstefanf } 277145078Sstefanf getout = escape(p, 0, &len); 278145061Sstefanf *(fmt - 1) = 's'; 279145061Sstefanf PF(start, p); 280145061Sstefanf *(fmt - 1) = 'b'; 28195409Stjr#ifdef SHELL 282145061Sstefanf ckfree(p); 28395409Stjr#else 284145061Sstefanf free(p); 28595409Stjr#endif 286145061Sstefanf if (getout) 287145061Sstefanf return (fmt); 288145061Sstefanf break; 289145061Sstefanf } 290145061Sstefanf case 'c': { 291145061Sstefanf char p; 2921590Srgrimes 293145061Sstefanf p = getchr(); 294145061Sstefanf PF(start, p); 295145061Sstefanf break; 296145061Sstefanf } 297145061Sstefanf case 's': { 298145061Sstefanf const char *p; 2991590Srgrimes 300145061Sstefanf p = getstr(); 301145061Sstefanf PF(start, p); 302145061Sstefanf break; 303145061Sstefanf } 304145061Sstefanf case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 305145061Sstefanf char *f; 306148721Sstefanf intmax_t val; 307148721Sstefanf uintmax_t uval; 308145061Sstefanf int signedconv; 3098874Srgrimes 310145061Sstefanf signedconv = (convch == 'd' || convch == 'i'); 311148721Sstefanf if ((f = mknum(start, convch)) == NULL) 312145061Sstefanf return (NULL); 313148721Sstefanf if (getnum(&val, &uval, signedconv)) 314145061Sstefanf *rval = 1; 315145061Sstefanf if (signedconv) 316145061Sstefanf PF(f, val); 317145061Sstefanf else 318145061Sstefanf PF(f, uval); 319145061Sstefanf break; 320145061Sstefanf } 321145061Sstefanf case 'e': case 'E': 322145061Sstefanf case 'f': case 'F': 323145061Sstefanf case 'g': case 'G': 324145061Sstefanf case 'a': case 'A': { 325145061Sstefanf long double p; 3261590Srgrimes 327145061Sstefanf if (getfloating(&p, mod_ldbl)) 328145061Sstefanf *rval = 1; 329145061Sstefanf if (mod_ldbl) 330145061Sstefanf PF(start, p); 331145061Sstefanf else 332145061Sstefanf PF(start, (double)p); 333145061Sstefanf break; 3341590Srgrimes } 335145061Sstefanf default: 336145061Sstefanf warnx2("illegal format character %c", convch, NULL); 337145061Sstefanf return (NULL); 338145061Sstefanf } 339145061Sstefanf *fmt = nextch; 340145061Sstefanf return (fmt); 3411590Srgrimes} 3421590Srgrimes 3431590Srgrimesstatic char * 344148721Sstefanfmknum(char *str, int ch) 3451590Srgrimes{ 34670256Sben static char *copy; 34770256Sben static size_t copy_size; 34895409Stjr char *newcopy; 34970256Sben size_t len, newlen; 3501590Srgrimes 3511590Srgrimes len = strlen(str) + 2; 35270256Sben if (len > copy_size) { 35370256Sben newlen = ((len + 1023) >> 10) << 10; 35470256Sben#ifdef SHELL 35570256Sben if ((newcopy = ckrealloc(copy, newlen)) == NULL) 35670256Sben#else 35770256Sben if ((newcopy = realloc(copy, newlen)) == NULL) 35870256Sben#endif 35995409Stjr { 36095409Stjr warnx2("%s", strerror(ENOMEM), NULL); 36170256Sben return (NULL); 36295409Stjr } 36370256Sben copy = newcopy; 36470256Sben copy_size = newlen; 36570256Sben } 36662928Sse 3671590Srgrimes memmove(copy, str, len - 3); 368148721Sstefanf copy[len - 3] = 'j'; 3691590Srgrimes copy[len - 2] = ch; 3701590Srgrimes copy[len - 1] = '\0'; 3711590Srgrimes return (copy); 3721590Srgrimes} 3731590Srgrimes 37495300Sjmallettstatic int 375145078Sstefanfescape(char *fmt, int percent, size_t *len) 3761590Srgrimes{ 377145078Sstefanf char *save, *store; 378102944Sdwmalone int value, c; 3791590Srgrimes 380145078Sstefanf for (save = store = fmt; (c = *fmt); ++fmt, ++store) { 3811590Srgrimes if (c != '\\') { 3821590Srgrimes *store = c; 3831590Srgrimes continue; 3841590Srgrimes } 3851590Srgrimes switch (*++fmt) { 3861590Srgrimes case '\0': /* EOS, user error */ 3871590Srgrimes *store = '\\'; 3881590Srgrimes *++store = '\0'; 389145078Sstefanf *len = store - save; 39095300Sjmallett return (0); 3911590Srgrimes case '\\': /* backslash */ 3921590Srgrimes case '\'': /* single quote */ 3931590Srgrimes *store = *fmt; 3941590Srgrimes break; 3951590Srgrimes case 'a': /* bell/alert */ 396145074Sstefanf *store = '\a'; 3971590Srgrimes break; 3981590Srgrimes case 'b': /* backspace */ 3991590Srgrimes *store = '\b'; 4001590Srgrimes break; 40195300Sjmallett case 'c': 40295300Sjmallett *store = '\0'; 403145078Sstefanf *len = store - save; 40495300Sjmallett return (1); 4051590Srgrimes case 'f': /* form-feed */ 4061590Srgrimes *store = '\f'; 4071590Srgrimes break; 4081590Srgrimes case 'n': /* newline */ 4091590Srgrimes *store = '\n'; 4101590Srgrimes break; 4111590Srgrimes case 'r': /* carriage-return */ 4121590Srgrimes *store = '\r'; 4131590Srgrimes break; 4141590Srgrimes case 't': /* horizontal tab */ 4151590Srgrimes *store = '\t'; 4161590Srgrimes break; 4171590Srgrimes case 'v': /* vertical tab */ 418145074Sstefanf *store = '\v'; 4191590Srgrimes break; 4201590Srgrimes /* octal constant */ 4211590Srgrimes case '0': case '1': case '2': case '3': 4221590Srgrimes case '4': case '5': case '6': case '7': 423181153Sdas c = (!percent && *fmt == '0') ? 4 : 3; 424181153Sdas for (value = 0; 4251590Srgrimes c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 4261590Srgrimes value <<= 3; 4271590Srgrimes value += *fmt - '0'; 4281590Srgrimes } 4291590Srgrimes --fmt; 43098426Stjr if (percent && value == '%') { 43198419Stjr *store++ = '%'; 43298419Stjr *store = '%'; 43398419Stjr } else 43498419Stjr *store = value; 4351590Srgrimes break; 4361590Srgrimes default: 4371590Srgrimes *store = *fmt; 4381590Srgrimes break; 4391590Srgrimes } 4401590Srgrimes } 4411590Srgrimes *store = '\0'; 442145078Sstefanf *len = store - save; 44395300Sjmallett return (0); 4441590Srgrimes} 4451590Srgrimes 4461590Srgrimesstatic int 447102944Sdwmalonegetchr(void) 4481590Srgrimes{ 4491590Srgrimes if (!*gargv) 4501590Srgrimes return ('\0'); 4511590Srgrimes return ((int)**gargv++); 4521590Srgrimes} 4531590Srgrimes 45487298Sdwmalonestatic const char * 455102944Sdwmalonegetstr(void) 4561590Srgrimes{ 4571590Srgrimes if (!*gargv) 4581590Srgrimes return (""); 4591590Srgrimes return (*gargv++); 4601590Srgrimes} 4611590Srgrimes 4621590Srgrimesstatic int 463102944Sdwmalonegetint(int *ip) 4641590Srgrimes{ 465148721Sstefanf intmax_t val; 466148721Sstefanf uintmax_t uval; 46795409Stjr int rval; 4681590Srgrimes 469148721Sstefanf if (getnum(&val, &uval, 1)) 4701590Srgrimes return (1); 47195409Stjr rval = 0; 47295409Stjr if (val < INT_MIN || val > INT_MAX) { 47341582Sbde warnx3("%s: %s", *gargv, strerror(ERANGE)); 47495409Stjr rval = 1; 47595409Stjr } 47662928Sse *ip = (int)val; 47795409Stjr return (rval); 4781590Srgrimes} 4791590Srgrimes 4801590Srgrimesstatic int 481148721Sstefanfgetnum(intmax_t *ip, uintmax_t *uip, int signedconv) 4821590Srgrimes{ 4831590Srgrimes char *ep; 48495409Stjr int rval; 4851590Srgrimes 4861590Srgrimes if (!*gargv) { 487148721Sstefanf *ip = 0; 4881590Srgrimes return (0); 4891590Srgrimes } 49095300Sjmallett if (**gargv == '"' || **gargv == '\'') { 49195409Stjr if (signedconv) 492148721Sstefanf *ip = asciicode(); 49395409Stjr else 494148721Sstefanf *uip = asciicode(); 4951590Srgrimes return (0); 4961590Srgrimes } 49795409Stjr rval = 0; 49895300Sjmallett errno = 0; 49995409Stjr if (signedconv) 500148721Sstefanf *ip = strtoimax(*gargv, &ep, 0); 50195409Stjr else 502148721Sstefanf *uip = strtoumax(*gargv, &ep, 0); 50395409Stjr if (ep == *gargv) { 50495300Sjmallett warnx2("%s: expected numeric value", *gargv, NULL); 50595409Stjr rval = 1; 50695409Stjr } 50795409Stjr else if (*ep != '\0') { 50895300Sjmallett warnx2("%s: not completely converted", *gargv, NULL); 50995409Stjr rval = 1; 51095409Stjr } 51195409Stjr if (errno == ERANGE) { 51295300Sjmallett warnx3("%s: %s", *gargv, strerror(ERANGE)); 51395409Stjr rval = 1; 51495409Stjr } 51595300Sjmallett ++gargv; 51695409Stjr return (rval); 5171590Srgrimes} 5181590Srgrimes 51995409Stjrstatic int 520143906Sdasgetfloating(long double *dp, int mod_ldbl) 5211590Srgrimes{ 52295300Sjmallett char *ep; 52395409Stjr int rval; 52495300Sjmallett 525145027Sstefanf if (!*gargv) { 526145027Sstefanf *dp = 0.0; 52795409Stjr return (0); 528145027Sstefanf } 52995300Sjmallett if (**gargv == '"' || **gargv == '\'') { 53095409Stjr *dp = asciicode(); 53195409Stjr return (0); 53295300Sjmallett } 533126729Scperciva rval = 0; 53495300Sjmallett errno = 0; 535143906Sdas if (mod_ldbl) 536143906Sdas *dp = strtold(*gargv, &ep); 537143906Sdas else 538143906Sdas *dp = strtod(*gargv, &ep); 53995409Stjr if (ep == *gargv) { 54095300Sjmallett warnx2("%s: expected numeric value", *gargv, NULL); 54195409Stjr rval = 1; 54295409Stjr } else if (*ep != '\0') { 54395300Sjmallett warnx2("%s: not completely converted", *gargv, NULL); 54495409Stjr rval = 1; 54595409Stjr } 54695409Stjr if (errno == ERANGE) { 54795300Sjmallett warnx3("%s: %s", *gargv, strerror(ERANGE)); 54895409Stjr rval = 1; 54995409Stjr } 55095300Sjmallett ++gargv; 55195409Stjr return (rval); 5521590Srgrimes} 5531590Srgrimes 5541590Srgrimesstatic int 555102944Sdwmaloneasciicode(void) 5561590Srgrimes{ 557102944Sdwmalone int ch; 5581590Srgrimes 5591590Srgrimes ch = **gargv; 5601590Srgrimes if (ch == '\'' || ch == '"') 5611590Srgrimes ch = (*gargv)[1]; 5621590Srgrimes ++gargv; 5631590Srgrimes return (ch); 5641590Srgrimes} 5651590Srgrimes 5661590Srgrimesstatic void 567102944Sdwmaloneusage(void) 5681590Srgrimes{ 569146466Sru (void)fprintf(stderr, "usage: printf format [arguments ...]\n"); 5701590Srgrimes} 571