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 */ 29219153Sjilles/* 30219153Sjilles * Important: This file is used both as a standalone program /usr/bin/printf 31219153Sjilles * and as a builtin for /bin/sh (#define SHELL). 32219153Sjilles */ 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$"; 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> 56216417Sdelphij#include <locale.h> 571590Srgrimes#include <stdio.h> 581590Srgrimes#include <stdlib.h> 591590Srgrimes#include <string.h> 6027966Ssteve#include <unistd.h> 61222418Sjilles#include <wchar.h> 621590Srgrimes 631590Srgrimes#ifdef SHELL 64265161Spfg#define main printfcmd 6512730Sjoerg#include "bltin/bltin.h" 66215520Sjilles#include "error.h" 671590Srgrimes#endif 681590Srgrimes 69265161Spfg#define PF(f, func) do { \ 70230961Spfg char *b = NULL; \ 71230961Spfg if (havewidth) \ 72230961Spfg if (haveprec) \ 7318613Speter (void)asprintf(&b, f, fieldwidth, precision, func); \ 74230961Spfg else \ 75230961Spfg (void)asprintf(&b, f, fieldwidth, func); \ 76230961Spfg else if (haveprec) \ 77230961Spfg (void)asprintf(&b, f, precision, func); \ 78230961Spfg else \ 79230961Spfg (void)asprintf(&b, f, func); \ 80230961Spfg if (b) { \ 81230961Spfg (void)fputs(b, stdout); \ 82230961Spfg free(b); \ 83230961Spfg } \ 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) { 168216606Sjilles warnx("missing format character"); 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) { 229216606Sjilles warnx("missing format character"); 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)) { 247216606Sjilles warnx("bad modifier L for %%%c", *fmt); 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 263145061Sstefanf p = strdup(getstr()); 264145061Sstefanf if (p == NULL) { 265216606Sjilles warnx("%s", strerror(ENOMEM)); 266145061Sstefanf return (NULL); 267145061Sstefanf } 268145078Sstefanf getout = escape(p, 0, &len); 269145061Sstefanf *(fmt - 1) = 's'; 270145061Sstefanf PF(start, p); 271145061Sstefanf *(fmt - 1) = 'b'; 272145061Sstefanf free(p); 273145061Sstefanf if (getout) 274145061Sstefanf return (fmt); 275145061Sstefanf break; 276145061Sstefanf } 277145061Sstefanf case 'c': { 278145061Sstefanf char p; 2791590Srgrimes 280145061Sstefanf p = getchr(); 281145061Sstefanf PF(start, p); 282145061Sstefanf break; 283145061Sstefanf } 284145061Sstefanf case 's': { 285145061Sstefanf const char *p; 2861590Srgrimes 287145061Sstefanf p = getstr(); 288145061Sstefanf PF(start, p); 289145061Sstefanf break; 290145061Sstefanf } 291145061Sstefanf case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 292145061Sstefanf char *f; 293148721Sstefanf intmax_t val; 294148721Sstefanf uintmax_t uval; 295145061Sstefanf int signedconv; 2968874Srgrimes 297145061Sstefanf signedconv = (convch == 'd' || convch == 'i'); 298148721Sstefanf if ((f = mknum(start, convch)) == NULL) 299145061Sstefanf return (NULL); 300148721Sstefanf if (getnum(&val, &uval, signedconv)) 301145061Sstefanf *rval = 1; 302145061Sstefanf if (signedconv) 303145061Sstefanf PF(f, val); 304145061Sstefanf else 305145061Sstefanf PF(f, uval); 306145061Sstefanf break; 307145061Sstefanf } 308145061Sstefanf case 'e': case 'E': 309145061Sstefanf case 'f': case 'F': 310145061Sstefanf case 'g': case 'G': 311145061Sstefanf case 'a': case 'A': { 312145061Sstefanf long double p; 3131590Srgrimes 314145061Sstefanf if (getfloating(&p, mod_ldbl)) 315145061Sstefanf *rval = 1; 316145061Sstefanf if (mod_ldbl) 317145061Sstefanf PF(start, p); 318145061Sstefanf else 319145061Sstefanf PF(start, (double)p); 320145061Sstefanf break; 3211590Srgrimes } 322145061Sstefanf default: 323216606Sjilles warnx("illegal format character %c", convch); 324145061Sstefanf return (NULL); 325145061Sstefanf } 326145061Sstefanf *fmt = nextch; 327145061Sstefanf return (fmt); 3281590Srgrimes} 3291590Srgrimes 3301590Srgrimesstatic char * 331216418Sdelphijmknum(char *str, char ch) 3321590Srgrimes{ 33370256Sben static char *copy; 33470256Sben static size_t copy_size; 33595409Stjr char *newcopy; 33670256Sben size_t len, newlen; 3371590Srgrimes 3381590Srgrimes len = strlen(str) + 2; 33970256Sben if (len > copy_size) { 34070256Sben newlen = ((len + 1023) >> 10) << 10; 34170256Sben if ((newcopy = realloc(copy, newlen)) == NULL) 34295409Stjr { 343216606Sjilles warnx("%s", strerror(ENOMEM)); 34470256Sben return (NULL); 34595409Stjr } 34670256Sben copy = newcopy; 34770256Sben copy_size = newlen; 34870256Sben } 34962928Sse 3501590Srgrimes memmove(copy, str, len - 3); 351148721Sstefanf copy[len - 3] = 'j'; 3521590Srgrimes copy[len - 2] = ch; 3531590Srgrimes copy[len - 1] = '\0'; 3541590Srgrimes return (copy); 3551590Srgrimes} 3561590Srgrimes 35795300Sjmallettstatic int 358145078Sstefanfescape(char *fmt, int percent, size_t *len) 3591590Srgrimes{ 360230961Spfg char *save, *store, c; 361230961Spfg int value; 3621590Srgrimes 363230961Spfg for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { 3641590Srgrimes if (c != '\\') { 3651590Srgrimes *store = c; 3661590Srgrimes continue; 3671590Srgrimes } 3681590Srgrimes switch (*++fmt) { 3691590Srgrimes case '\0': /* EOS, user error */ 3701590Srgrimes *store = '\\'; 3711590Srgrimes *++store = '\0'; 372145078Sstefanf *len = store - save; 37395300Sjmallett return (0); 3741590Srgrimes case '\\': /* backslash */ 3751590Srgrimes case '\'': /* single quote */ 3761590Srgrimes *store = *fmt; 3771590Srgrimes break; 3781590Srgrimes case 'a': /* bell/alert */ 379145074Sstefanf *store = '\a'; 3801590Srgrimes break; 3811590Srgrimes case 'b': /* backspace */ 3821590Srgrimes *store = '\b'; 3831590Srgrimes break; 38495300Sjmallett case 'c': 38595300Sjmallett *store = '\0'; 386145078Sstefanf *len = store - save; 38795300Sjmallett return (1); 3881590Srgrimes case 'f': /* form-feed */ 3891590Srgrimes *store = '\f'; 3901590Srgrimes break; 3911590Srgrimes case 'n': /* newline */ 3921590Srgrimes *store = '\n'; 3931590Srgrimes break; 3941590Srgrimes case 'r': /* carriage-return */ 3951590Srgrimes *store = '\r'; 3961590Srgrimes break; 3971590Srgrimes case 't': /* horizontal tab */ 3981590Srgrimes *store = '\t'; 3991590Srgrimes break; 4001590Srgrimes case 'v': /* vertical tab */ 401145074Sstefanf *store = '\v'; 4021590Srgrimes break; 4031590Srgrimes /* octal constant */ 4041590Srgrimes case '0': case '1': case '2': case '3': 4051590Srgrimes case '4': case '5': case '6': case '7': 406181153Sdas c = (!percent && *fmt == '0') ? 4 : 3; 407181153Sdas for (value = 0; 4081590Srgrimes c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 4091590Srgrimes value <<= 3; 4101590Srgrimes value += *fmt - '0'; 4111590Srgrimes } 4121590Srgrimes --fmt; 41398426Stjr if (percent && value == '%') { 41498419Stjr *store++ = '%'; 41598419Stjr *store = '%'; 41698419Stjr } else 417230961Spfg *store = (char)value; 4181590Srgrimes break; 4191590Srgrimes default: 4201590Srgrimes *store = *fmt; 4211590Srgrimes break; 4221590Srgrimes } 4231590Srgrimes } 4241590Srgrimes *store = '\0'; 425145078Sstefanf *len = store - save; 42695300Sjmallett return (0); 4271590Srgrimes} 4281590Srgrimes 4291590Srgrimesstatic int 430102944Sdwmalonegetchr(void) 4311590Srgrimes{ 4321590Srgrimes if (!*gargv) 4331590Srgrimes return ('\0'); 4341590Srgrimes return ((int)**gargv++); 4351590Srgrimes} 4361590Srgrimes 43787298Sdwmalonestatic const char * 438102944Sdwmalonegetstr(void) 4391590Srgrimes{ 4401590Srgrimes if (!*gargv) 4411590Srgrimes return (""); 4421590Srgrimes return (*gargv++); 4431590Srgrimes} 4441590Srgrimes 4451590Srgrimesstatic int 446102944Sdwmalonegetint(int *ip) 4471590Srgrimes{ 448148721Sstefanf intmax_t val; 449148721Sstefanf uintmax_t uval; 45095409Stjr int rval; 4511590Srgrimes 452148721Sstefanf if (getnum(&val, &uval, 1)) 4531590Srgrimes return (1); 45495409Stjr rval = 0; 45595409Stjr if (val < INT_MIN || val > INT_MAX) { 456216606Sjilles warnx("%s: %s", *gargv, strerror(ERANGE)); 45795409Stjr rval = 1; 45895409Stjr } 45962928Sse *ip = (int)val; 46095409Stjr return (rval); 4611590Srgrimes} 4621590Srgrimes 4631590Srgrimesstatic int 464148721Sstefanfgetnum(intmax_t *ip, uintmax_t *uip, int signedconv) 4651590Srgrimes{ 4661590Srgrimes char *ep; 46795409Stjr int rval; 4681590Srgrimes 4691590Srgrimes if (!*gargv) { 470246277Seadler *ip = *uip = 0; 4711590Srgrimes return (0); 4721590Srgrimes } 47395300Sjmallett if (**gargv == '"' || **gargv == '\'') { 47495409Stjr if (signedconv) 475148721Sstefanf *ip = asciicode(); 47695409Stjr else 477148721Sstefanf *uip = asciicode(); 4781590Srgrimes return (0); 4791590Srgrimes } 48095409Stjr rval = 0; 48195300Sjmallett errno = 0; 48295409Stjr if (signedconv) 483148721Sstefanf *ip = strtoimax(*gargv, &ep, 0); 48495409Stjr else 485148721Sstefanf *uip = strtoumax(*gargv, &ep, 0); 48695409Stjr if (ep == *gargv) { 487216606Sjilles warnx("%s: expected numeric value", *gargv); 48895409Stjr rval = 1; 48995409Stjr } 49095409Stjr else if (*ep != '\0') { 491216606Sjilles warnx("%s: not completely converted", *gargv); 49295409Stjr rval = 1; 49395409Stjr } 49495409Stjr if (errno == ERANGE) { 495216606Sjilles warnx("%s: %s", *gargv, strerror(ERANGE)); 49695409Stjr rval = 1; 49795409Stjr } 49895300Sjmallett ++gargv; 49995409Stjr return (rval); 5001590Srgrimes} 5011590Srgrimes 50295409Stjrstatic int 503143906Sdasgetfloating(long double *dp, int mod_ldbl) 5041590Srgrimes{ 50595300Sjmallett char *ep; 50695409Stjr int rval; 50795300Sjmallett 508145027Sstefanf if (!*gargv) { 509145027Sstefanf *dp = 0.0; 51095409Stjr return (0); 511145027Sstefanf } 51295300Sjmallett if (**gargv == '"' || **gargv == '\'') { 51395409Stjr *dp = asciicode(); 51495409Stjr return (0); 51595300Sjmallett } 516126729Scperciva rval = 0; 51795300Sjmallett errno = 0; 518143906Sdas if (mod_ldbl) 519143906Sdas *dp = strtold(*gargv, &ep); 520143906Sdas else 521143906Sdas *dp = strtod(*gargv, &ep); 52295409Stjr if (ep == *gargv) { 523216606Sjilles warnx("%s: expected numeric value", *gargv); 52495409Stjr rval = 1; 52595409Stjr } else if (*ep != '\0') { 526216606Sjilles warnx("%s: not completely converted", *gargv); 52795409Stjr rval = 1; 52895409Stjr } 52995409Stjr if (errno == ERANGE) { 530216606Sjilles warnx("%s: %s", *gargv, strerror(ERANGE)); 53195409Stjr rval = 1; 53295409Stjr } 53395300Sjmallett ++gargv; 53495409Stjr return (rval); 5351590Srgrimes} 5361590Srgrimes 5371590Srgrimesstatic int 538102944Sdwmaloneasciicode(void) 5391590Srgrimes{ 540102944Sdwmalone int ch; 541222418Sjilles wchar_t wch; 542222418Sjilles mbstate_t mbs; 5431590Srgrimes 544222418Sjilles ch = (unsigned char)**gargv; 545222418Sjilles if (ch == '\'' || ch == '"') { 546222418Sjilles memset(&mbs, 0, sizeof(mbs)); 547222418Sjilles switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) { 548222418Sjilles case (size_t)-2: 549222418Sjilles case (size_t)-1: 550222418Sjilles wch = (unsigned char)gargv[0][1]; 551222418Sjilles break; 552222418Sjilles case 0: 553222418Sjilles wch = 0; 554222418Sjilles break; 555222418Sjilles } 556222418Sjilles ch = wch; 557222418Sjilles } 5581590Srgrimes ++gargv; 5591590Srgrimes return (ch); 5601590Srgrimes} 5611590Srgrimes 5621590Srgrimesstatic void 563102944Sdwmaloneusage(void) 5641590Srgrimes{ 565146466Sru (void)fprintf(stderr, "usage: printf format [arguments ...]\n"); 5661590Srgrimes} 567