printf.c revision 216447
1216417Sdelphij/*- 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 * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 30216310Sjilles#ifndef SHELL 311590Srgrimes#ifndef lint 3220409Sstevestatic char const copyright[] = 331590Srgrimes"@(#) Copyright (c) 1989, 1993\n\ 341590Srgrimes The Regents of the University of California. All rights reserved.\n"; 351590Srgrimes#endif /* not lint */ 361590Srgrimes#endif 371590Srgrimes 381590Srgrimes#ifndef lint 3965429Simp#if 0 4020409Sstevestatic char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93"; 4165429Simp#endif 4259435Scracauerstatic const char rcsid[] = 4359435Scracauer "$FreeBSD: head/usr.bin/printf/printf.c 216447 2010-12-14 20:35:08Z delphij $"; 441590Srgrimes#endif /* not lint */ 451590Srgrimes 461590Srgrimes#include <sys/types.h> 471590Srgrimes 481590Srgrimes#include <err.h> 491590Srgrimes#include <errno.h> 50148721Sstefanf#include <inttypes.h> 511590Srgrimes#include <limits.h> 52216417Sdelphij#include <locale.h> 531590Srgrimes#include <stdio.h> 541590Srgrimes#include <stdlib.h> 551590Srgrimes#include <string.h> 5627966Ssteve#include <unistd.h> 571590Srgrimes 581590Srgrimes#ifdef SHELL 591590Srgrimes#define main printfcmd 6012730Sjoerg#include "bltin/bltin.h" 6170256Sben#include "memalloc.h" 62215520Sjilles#include "error.h" 6341582Sbde#else 6441582Sbde#define warnx1(a, b, c) warnx(a) 6541582Sbde#define warnx2(a, b, c) warnx(a, b) 6641582Sbde#define warnx3(a, b, c) warnx(a, b, c) 671590Srgrimes#endif 681590Srgrimes 6995409Stjr#define PF(f, func) do { \ 7018613Speter char *b = NULL; \ 7198424Stjr if (havewidth) \ 7298424Stjr if (haveprec) \ 7318613Speter (void)asprintf(&b, f, fieldwidth, precision, func); \ 741590Srgrimes else \ 7518613Speter (void)asprintf(&b, f, fieldwidth, func); \ 7698424Stjr else if (haveprec) \ 7718613Speter (void)asprintf(&b, f, precision, func); \ 781590Srgrimes else \ 7918613Speter (void)asprintf(&b, f, func); \ 8018613Speter if (b) { \ 8118613Speter (void)fputs(b, stdout); \ 8218613Speter free(b); \ 8318613Speter } \ 8495409Stjr} while (0) 851590Srgrimes 8692921Simpstatic int asciicode(void); 87215520Sjillesstatic char *printf_doformat(char *, int *); 88145078Sstefanfstatic int escape(char *, int, size_t *); 8992921Simpstatic int getchr(void); 90143906Sdasstatic int getfloating(long double *, int); 9192921Simpstatic int getint(int *); 92148721Sstefanfstatic int getnum(intmax_t *, uintmax_t *, int); 9395409Stjrstatic const char 9495409Stjr *getstr(void); 95216418Sdelphijstatic char *mknum(char *, char); 9692921Simpstatic void usage(void); 971590Srgrimes 981590Srgrimesstatic char **gargv; 991590Srgrimes 1001590Srgrimesint 101102944Sdwmalonemain(int argc, char *argv[]) 1021590Srgrimes{ 103145078Sstefanf size_t len; 104216447Sdelphij int ch, chopped, end, rval; 105145061Sstefanf char *format, *fmt, *start; 1061590Srgrimes 107216310Sjilles#ifndef SHELL 108216424Sdelphij (void) setlocale(LC_ALL, ""); 10972304Sache#endif 110215520Sjilles#ifdef SHELL 111215520Sjilles optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ 112215520Sjilles#endif 113216447Sdelphij while ((ch = getopt(argc, argv, "")) != -1) 114216447Sdelphij switch (ch) { 115216447Sdelphij case '?': 116216447Sdelphij default: 117216447Sdelphij usage(); 118216447Sdelphij return (1); 119216447Sdelphij } 120216447Sdelphij argc -= optind; 121216447Sdelphij argv += optind; 1221590Srgrimes 1231590Srgrimes if (argc < 1) { 1241590Srgrimes usage(); 125216439Sdelphij return (1); 1261590Srgrimes } 1271590Srgrimes 128215520Sjilles#ifdef SHELL 129215520Sjilles INTOFF; 130215520Sjilles#endif 1311590Srgrimes /* 1321590Srgrimes * Basic algorithm is to scan the format string for conversion 1331590Srgrimes * specifications -- once one is found, find out if the field 1341590Srgrimes * width or precision is a '*'; if it is, gather up value. Note, 1351590Srgrimes * format strings are reused as necessary to use up the provided 1361590Srgrimes * arguments, arguments of zero/null string are provided to use 1371590Srgrimes * up the format string. 1381590Srgrimes */ 139145078Sstefanf fmt = format = *argv; 140145078Sstefanf chopped = escape(fmt, 1, &len); /* backslash interpretation */ 141145061Sstefanf rval = end = 0; 1421590Srgrimes gargv = ++argv; 1431590Srgrimes for (;;) { 144145061Sstefanf start = fmt; 145145078Sstefanf while (fmt < format + len) { 146145061Sstefanf if (fmt[0] == '%') { 147145061Sstefanf fwrite(start, 1, fmt - start, stdout); 148145061Sstefanf if (fmt[1] == '%') { 149145061Sstefanf /* %% prints a % */ 150145061Sstefanf putchar('%'); 151145061Sstefanf fmt += 2; 152145061Sstefanf } else { 153215520Sjilles fmt = printf_doformat(fmt, &rval); 154215520Sjilles if (fmt == NULL) { 155215520Sjilles#ifdef SHELL 156215520Sjilles INTON; 157215520Sjilles#endif 158145061Sstefanf return (1); 159215520Sjilles } 160145061Sstefanf end = 0; 16195300Sjmallett } 162145061Sstefanf start = fmt; 163145061Sstefanf } else 16498420Stjr fmt++; 1651590Srgrimes } 1661590Srgrimes 167145061Sstefanf if (end == 1) { 168145061Sstefanf warnx1("missing format character", NULL, NULL); 169215520Sjilles#ifdef SHELL 170215520Sjilles INTON; 171215520Sjilles#endif 172145061Sstefanf return (1); 173145061Sstefanf } 174145061Sstefanf fwrite(start, 1, fmt - start, stdout); 175215520Sjilles if (chopped || !*gargv) { 176215520Sjilles#ifdef SHELL 177215520Sjilles INTON; 178215520Sjilles#endif 179145061Sstefanf return (rval); 180215520Sjilles } 181145061Sstefanf /* Restart at the beginning of the format string. */ 182145061Sstefanf fmt = format; 183145061Sstefanf end = 1; 184145061Sstefanf } 185145061Sstefanf /* NOTREACHED */ 186145061Sstefanf} 187145061Sstefanf 188145061Sstefanf 189145061Sstefanfstatic char * 190215520Sjillesprintf_doformat(char *start, int *rval) 191145061Sstefanf{ 192145061Sstefanf static const char skip1[] = "#'-+ 0"; 193145061Sstefanf static const char skip2[] = "0123456789"; 194145061Sstefanf char *fmt; 195145061Sstefanf int fieldwidth, haveprec, havewidth, mod_ldbl, precision; 196145061Sstefanf char convch, nextch; 197145061Sstefanf 198145061Sstefanf fmt = start + 1; 199145061Sstefanf /* skip to field width */ 200145061Sstefanf fmt += strspn(fmt, skip1); 201145061Sstefanf if (*fmt == '*') { 202145061Sstefanf if (getint(&fieldwidth)) 203145061Sstefanf return (NULL); 204145061Sstefanf havewidth = 1; 205145061Sstefanf ++fmt; 206145061Sstefanf } else { 207145061Sstefanf havewidth = 0; 208145061Sstefanf 209145061Sstefanf /* skip to possible '.', get following precision */ 210145061Sstefanf fmt += strspn(fmt, skip2); 211145061Sstefanf } 212145061Sstefanf if (*fmt == '.') { 213145061Sstefanf /* precision present? */ 214145061Sstefanf ++fmt; 2151590Srgrimes if (*fmt == '*') { 216145061Sstefanf if (getint(&precision)) 217145061Sstefanf return (NULL); 218145061Sstefanf haveprec = 1; 2198323Sjoerg ++fmt; 2208323Sjoerg } else { 221145061Sstefanf haveprec = 0; 2221590Srgrimes 223145061Sstefanf /* skip to conversion char */ 224144902Sstefanf fmt += strspn(fmt, skip2); 2258323Sjoerg } 226145061Sstefanf } else 227145061Sstefanf haveprec = 0; 228145061Sstefanf if (!*fmt) { 229145061Sstefanf warnx1("missing format character", NULL, NULL); 230145061Sstefanf return (NULL); 231145061Sstefanf } 2328323Sjoerg 233145061Sstefanf /* 234145061Sstefanf * Look for a length modifier. POSIX doesn't have these, so 235145061Sstefanf * we only support them for floating-point conversions, which 236145061Sstefanf * are extensions. This is useful because the L modifier can 237145061Sstefanf * be used to gain extra range and precision, while omitting 238145061Sstefanf * it is more likely to produce consistent results on different 239145061Sstefanf * architectures. This is not so important for integers 240145061Sstefanf * because overflow is the only bad thing that can happen to 241145061Sstefanf * them, but consider the command printf %a 1.1 242145061Sstefanf */ 243145061Sstefanf if (*fmt == 'L') { 244145061Sstefanf mod_ldbl = 1; 245145061Sstefanf fmt++; 246145061Sstefanf if (!strchr("aAeEfFgG", *fmt)) { 247145061Sstefanf warnx2("bad modifier L for %%%c", *fmt, NULL); 248145061Sstefanf return (NULL); 2491590Srgrimes } 250145061Sstefanf } else { 251145061Sstefanf mod_ldbl = 0; 252145061Sstefanf } 2531590Srgrimes 254145061Sstefanf convch = *fmt; 255145061Sstefanf nextch = *++fmt; 256145061Sstefanf *fmt = '\0'; 257145061Sstefanf switch (convch) { 258145061Sstefanf case 'b': { 259145078Sstefanf size_t len; 260145061Sstefanf char *p; 261145061Sstefanf int getout; 262143906Sdas 26395409Stjr#ifdef SHELL 264145061Sstefanf p = savestr(getstr()); 26595409Stjr#else 266145061Sstefanf p = strdup(getstr()); 26795409Stjr#endif 268145061Sstefanf if (p == NULL) { 269145061Sstefanf warnx2("%s", strerror(ENOMEM), NULL); 270145061Sstefanf return (NULL); 271145061Sstefanf } 272145078Sstefanf getout = escape(p, 0, &len); 273145061Sstefanf *(fmt - 1) = 's'; 274145061Sstefanf PF(start, p); 275145061Sstefanf *(fmt - 1) = 'b'; 27695409Stjr#ifdef SHELL 277145061Sstefanf ckfree(p); 27895409Stjr#else 279145061Sstefanf free(p); 28095409Stjr#endif 281145061Sstefanf if (getout) 282145061Sstefanf return (fmt); 283145061Sstefanf break; 284145061Sstefanf } 285145061Sstefanf case 'c': { 286145061Sstefanf char p; 2871590Srgrimes 288145061Sstefanf p = getchr(); 289145061Sstefanf PF(start, p); 290145061Sstefanf break; 291145061Sstefanf } 292145061Sstefanf case 's': { 293145061Sstefanf const char *p; 2941590Srgrimes 295145061Sstefanf p = getstr(); 296145061Sstefanf PF(start, p); 297145061Sstefanf break; 298145061Sstefanf } 299145061Sstefanf case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 300145061Sstefanf char *f; 301148721Sstefanf intmax_t val; 302148721Sstefanf uintmax_t uval; 303145061Sstefanf int signedconv; 3048874Srgrimes 305145061Sstefanf signedconv = (convch == 'd' || convch == 'i'); 306148721Sstefanf if ((f = mknum(start, convch)) == NULL) 307145061Sstefanf return (NULL); 308148721Sstefanf if (getnum(&val, &uval, signedconv)) 309145061Sstefanf *rval = 1; 310145061Sstefanf if (signedconv) 311145061Sstefanf PF(f, val); 312145061Sstefanf else 313145061Sstefanf PF(f, uval); 314145061Sstefanf break; 315145061Sstefanf } 316145061Sstefanf case 'e': case 'E': 317145061Sstefanf case 'f': case 'F': 318145061Sstefanf case 'g': case 'G': 319145061Sstefanf case 'a': case 'A': { 320145061Sstefanf long double p; 3211590Srgrimes 322145061Sstefanf if (getfloating(&p, mod_ldbl)) 323145061Sstefanf *rval = 1; 324145061Sstefanf if (mod_ldbl) 325145061Sstefanf PF(start, p); 326145061Sstefanf else 327145061Sstefanf PF(start, (double)p); 328145061Sstefanf break; 3291590Srgrimes } 330145061Sstefanf default: 331145061Sstefanf warnx2("illegal format character %c", convch, NULL); 332145061Sstefanf return (NULL); 333145061Sstefanf } 334145061Sstefanf *fmt = nextch; 335145061Sstefanf return (fmt); 3361590Srgrimes} 3371590Srgrimes 3381590Srgrimesstatic char * 339216418Sdelphijmknum(char *str, char ch) 3401590Srgrimes{ 34170256Sben static char *copy; 34270256Sben static size_t copy_size; 34395409Stjr char *newcopy; 34470256Sben size_t len, newlen; 3451590Srgrimes 3461590Srgrimes len = strlen(str) + 2; 34770256Sben if (len > copy_size) { 34870256Sben newlen = ((len + 1023) >> 10) << 10; 34970256Sben#ifdef SHELL 35070256Sben if ((newcopy = ckrealloc(copy, newlen)) == NULL) 35170256Sben#else 35270256Sben if ((newcopy = realloc(copy, newlen)) == NULL) 35370256Sben#endif 35495409Stjr { 35595409Stjr warnx2("%s", strerror(ENOMEM), NULL); 35670256Sben return (NULL); 35795409Stjr } 35870256Sben copy = newcopy; 35970256Sben copy_size = newlen; 36070256Sben } 36162928Sse 3621590Srgrimes memmove(copy, str, len - 3); 363148721Sstefanf copy[len - 3] = 'j'; 3641590Srgrimes copy[len - 2] = ch; 3651590Srgrimes copy[len - 1] = '\0'; 3661590Srgrimes return (copy); 3671590Srgrimes} 3681590Srgrimes 36995300Sjmallettstatic int 370145078Sstefanfescape(char *fmt, int percent, size_t *len) 3711590Srgrimes{ 372145078Sstefanf char *save, *store; 373102944Sdwmalone int value, c; 3741590Srgrimes 375145078Sstefanf for (save = store = fmt; (c = *fmt); ++fmt, ++store) { 3761590Srgrimes if (c != '\\') { 3771590Srgrimes *store = c; 3781590Srgrimes continue; 3791590Srgrimes } 3801590Srgrimes switch (*++fmt) { 3811590Srgrimes case '\0': /* EOS, user error */ 3821590Srgrimes *store = '\\'; 3831590Srgrimes *++store = '\0'; 384145078Sstefanf *len = store - save; 38595300Sjmallett return (0); 3861590Srgrimes case '\\': /* backslash */ 3871590Srgrimes case '\'': /* single quote */ 3881590Srgrimes *store = *fmt; 3891590Srgrimes break; 3901590Srgrimes case 'a': /* bell/alert */ 391145074Sstefanf *store = '\a'; 3921590Srgrimes break; 3931590Srgrimes case 'b': /* backspace */ 3941590Srgrimes *store = '\b'; 3951590Srgrimes break; 39695300Sjmallett case 'c': 39795300Sjmallett *store = '\0'; 398145078Sstefanf *len = store - save; 39995300Sjmallett return (1); 4001590Srgrimes case 'f': /* form-feed */ 4011590Srgrimes *store = '\f'; 4021590Srgrimes break; 4031590Srgrimes case 'n': /* newline */ 4041590Srgrimes *store = '\n'; 4051590Srgrimes break; 4061590Srgrimes case 'r': /* carriage-return */ 4071590Srgrimes *store = '\r'; 4081590Srgrimes break; 4091590Srgrimes case 't': /* horizontal tab */ 4101590Srgrimes *store = '\t'; 4111590Srgrimes break; 4121590Srgrimes case 'v': /* vertical tab */ 413145074Sstefanf *store = '\v'; 4141590Srgrimes break; 4151590Srgrimes /* octal constant */ 4161590Srgrimes case '0': case '1': case '2': case '3': 4171590Srgrimes case '4': case '5': case '6': case '7': 418181153Sdas c = (!percent && *fmt == '0') ? 4 : 3; 419181153Sdas for (value = 0; 4201590Srgrimes c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 4211590Srgrimes value <<= 3; 4221590Srgrimes value += *fmt - '0'; 4231590Srgrimes } 4241590Srgrimes --fmt; 42598426Stjr if (percent && value == '%') { 42698419Stjr *store++ = '%'; 42798419Stjr *store = '%'; 42898419Stjr } else 42998419Stjr *store = value; 4301590Srgrimes break; 4311590Srgrimes default: 4321590Srgrimes *store = *fmt; 4331590Srgrimes break; 4341590Srgrimes } 4351590Srgrimes } 4361590Srgrimes *store = '\0'; 437145078Sstefanf *len = store - save; 43895300Sjmallett return (0); 4391590Srgrimes} 4401590Srgrimes 4411590Srgrimesstatic int 442102944Sdwmalonegetchr(void) 4431590Srgrimes{ 4441590Srgrimes if (!*gargv) 4451590Srgrimes return ('\0'); 4461590Srgrimes return ((int)**gargv++); 4471590Srgrimes} 4481590Srgrimes 44987298Sdwmalonestatic const char * 450102944Sdwmalonegetstr(void) 4511590Srgrimes{ 4521590Srgrimes if (!*gargv) 4531590Srgrimes return (""); 4541590Srgrimes return (*gargv++); 4551590Srgrimes} 4561590Srgrimes 4571590Srgrimesstatic int 458102944Sdwmalonegetint(int *ip) 4591590Srgrimes{ 460148721Sstefanf intmax_t val; 461148721Sstefanf uintmax_t uval; 46295409Stjr int rval; 4631590Srgrimes 464148721Sstefanf if (getnum(&val, &uval, 1)) 4651590Srgrimes return (1); 46695409Stjr rval = 0; 46795409Stjr if (val < INT_MIN || val > INT_MAX) { 46841582Sbde warnx3("%s: %s", *gargv, strerror(ERANGE)); 46995409Stjr rval = 1; 47095409Stjr } 47162928Sse *ip = (int)val; 47295409Stjr return (rval); 4731590Srgrimes} 4741590Srgrimes 4751590Srgrimesstatic int 476148721Sstefanfgetnum(intmax_t *ip, uintmax_t *uip, int signedconv) 4771590Srgrimes{ 4781590Srgrimes char *ep; 47995409Stjr int rval; 4801590Srgrimes 4811590Srgrimes if (!*gargv) { 482148721Sstefanf *ip = 0; 4831590Srgrimes return (0); 4841590Srgrimes } 48595300Sjmallett if (**gargv == '"' || **gargv == '\'') { 48695409Stjr if (signedconv) 487148721Sstefanf *ip = asciicode(); 48895409Stjr else 489148721Sstefanf *uip = asciicode(); 4901590Srgrimes return (0); 4911590Srgrimes } 49295409Stjr rval = 0; 49395300Sjmallett errno = 0; 49495409Stjr if (signedconv) 495148721Sstefanf *ip = strtoimax(*gargv, &ep, 0); 49695409Stjr else 497148721Sstefanf *uip = strtoumax(*gargv, &ep, 0); 49895409Stjr if (ep == *gargv) { 49995300Sjmallett warnx2("%s: expected numeric value", *gargv, NULL); 50095409Stjr rval = 1; 50195409Stjr } 50295409Stjr else if (*ep != '\0') { 50395300Sjmallett warnx2("%s: not completely converted", *gargv, NULL); 50495409Stjr rval = 1; 50595409Stjr } 50695409Stjr if (errno == ERANGE) { 50795300Sjmallett warnx3("%s: %s", *gargv, strerror(ERANGE)); 50895409Stjr rval = 1; 50995409Stjr } 51095300Sjmallett ++gargv; 51195409Stjr return (rval); 5121590Srgrimes} 5131590Srgrimes 51495409Stjrstatic int 515143906Sdasgetfloating(long double *dp, int mod_ldbl) 5161590Srgrimes{ 51795300Sjmallett char *ep; 51895409Stjr int rval; 51995300Sjmallett 520145027Sstefanf if (!*gargv) { 521145027Sstefanf *dp = 0.0; 52295409Stjr return (0); 523145027Sstefanf } 52495300Sjmallett if (**gargv == '"' || **gargv == '\'') { 52595409Stjr *dp = asciicode(); 52695409Stjr return (0); 52795300Sjmallett } 528126729Scperciva rval = 0; 52995300Sjmallett errno = 0; 530143906Sdas if (mod_ldbl) 531143906Sdas *dp = strtold(*gargv, &ep); 532143906Sdas else 533143906Sdas *dp = strtod(*gargv, &ep); 53495409Stjr if (ep == *gargv) { 53595300Sjmallett warnx2("%s: expected numeric value", *gargv, NULL); 53695409Stjr rval = 1; 53795409Stjr } else if (*ep != '\0') { 53895300Sjmallett warnx2("%s: not completely converted", *gargv, NULL); 53995409Stjr rval = 1; 54095409Stjr } 54195409Stjr if (errno == ERANGE) { 54295300Sjmallett warnx3("%s: %s", *gargv, strerror(ERANGE)); 54395409Stjr rval = 1; 54495409Stjr } 54595300Sjmallett ++gargv; 54695409Stjr return (rval); 5471590Srgrimes} 5481590Srgrimes 5491590Srgrimesstatic int 550102944Sdwmaloneasciicode(void) 5511590Srgrimes{ 552102944Sdwmalone int ch; 5531590Srgrimes 5541590Srgrimes ch = **gargv; 5551590Srgrimes if (ch == '\'' || ch == '"') 5561590Srgrimes ch = (*gargv)[1]; 5571590Srgrimes ++gargv; 5581590Srgrimes return (ch); 5591590Srgrimes} 5601590Srgrimes 5611590Srgrimesstatic void 562102944Sdwmaloneusage(void) 5631590Srgrimes{ 564146466Sru (void)fprintf(stderr, "usage: printf format [arguments ...]\n"); 5651590Srgrimes} 566