136725Sbostic /* 236725Sbostic * Copyright (c) 1989 The Regents of the University of California. 336725Sbostic * All rights reserved. 436725Sbostic * 542758Sbostic * %sccs.include.redist.c% 636725Sbostic */ 736725Sbostic 859549Sbostic #if !defined(BUILTIN) && !defined(SHELL) 936725Sbostic #ifndef lint 1036725Sbostic char copyright[] = 1136725Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 1236725Sbostic All rights reserved.\n"; 1336725Sbostic #endif /* not lint */ 1450477Sbostic #endif 1536725Sbostic 1636725Sbostic #ifndef lint 17*59551Sbostic static char sccsid[] = "@(#)printf.c 5.15 (Berkeley) 04/29/93"; 1836725Sbostic #endif /* not lint */ 1936725Sbostic 2059549Sbostic #include <sys/types.h> 2153564Smarc 2259549Sbostic #include <err.h> 2350477Sbostic #include <errno.h> 2459549Sbostic #include <limits.h> 2559549Sbostic #ifdef SHELL 2659549Sbostic #define EOF -1 2759549Sbostic #else 2836725Sbostic #include <stdio.h> 2953564Smarc #endif 3050477Sbostic #include <stdlib.h> 3150477Sbostic #include <string.h> 3236725Sbostic 3355444Smarc /* 3455444Smarc * XXX 3555444Smarc * This *has* to go away. TK. 3655444Smarc */ 3753564Smarc #ifdef SHELL 3853564Smarc #define main printfcmd 39*59551Sbostic #define warnx(a, b, c) { \ 4059549Sbostic char buf[64]; \ 41*59551Sbostic (void)sprintf(buf, sizeof(buf), a, b, c); \ 4259549Sbostic error(buf); \ 4359549Sbostic } 4455444Smarc #include "../../bin/sh/bltin/bltin.h" 4553564Smarc #endif 4653564Smarc 4736725Sbostic #define PF(f, func) { \ 4836725Sbostic if (fieldwidth) \ 4936725Sbostic if (precision) \ 5036811Sbostic (void)printf(f, fieldwidth, precision, func); \ 5136725Sbostic else \ 5236811Sbostic (void)printf(f, fieldwidth, func); \ 5336725Sbostic else if (precision) \ 5436811Sbostic (void)printf(f, precision, func); \ 5536725Sbostic else \ 5636811Sbostic (void)printf(f, func); \ 5736725Sbostic } 5836725Sbostic 5950477Sbostic static int asciicode __P((void)); 6050477Sbostic static void escape __P((char *)); 6150477Sbostic static int getchr __P((void)); 6250477Sbostic static double getdouble __P((void)); 6359549Sbostic static int getint __P((int *)); 6459549Sbostic static int getlong __P((long *)); 6550477Sbostic static char *getstr __P((void)); 6650477Sbostic static char *mklong __P((char *, int)); 6759549Sbostic static void usage __P((void)); 6836725Sbostic 6950477Sbostic static char **gargv; 7050477Sbostic 7150477Sbostic int 7250477Sbostic #ifdef BUILTIN 7350477Sbostic progprintf(argc, argv) 7450477Sbostic #else 7536725Sbostic main(argc, argv) 7650477Sbostic #endif 7736725Sbostic int argc; 7859549Sbostic char *argv[]; 7936725Sbostic { 8059550Sbostic extern int optind; 8136725Sbostic static char *skip1, *skip2; 8259549Sbostic int ch, end, fieldwidth, precision; 8359549Sbostic char convch, nextch, *format, *fmt, *start; 8436725Sbostic 8559549Sbostic while ((ch = getopt(argc, argv, "")) != EOF) 8659549Sbostic switch (ch) { 8759549Sbostic case '?': 8859549Sbostic default: 8959549Sbostic usage(); 9059549Sbostic return (1); 9159549Sbostic } 9259549Sbostic argc -= optind; 9359549Sbostic argv += optind; 9459549Sbostic 9559549Sbostic if (argc < 1) { 9659549Sbostic usage(); 9750477Sbostic return (1); 9836725Sbostic } 9936725Sbostic 10036725Sbostic /* 10136725Sbostic * Basic algorithm is to scan the format string for conversion 10236725Sbostic * specifications -- once one is found, find out if the field 10336725Sbostic * width or precision is a '*'; if it is, gather up value. Note, 10436725Sbostic * format strings are reused as necessary to use up the provided 10536725Sbostic * arguments, arguments of zero/null string are provided to use 10636725Sbostic * up the format string. 10736725Sbostic */ 10836725Sbostic skip1 = "#-+ 0"; 10936725Sbostic skip2 = "*0123456789"; 11036725Sbostic 11159549Sbostic escape(fmt = format = *argv); /* backslash interpretation */ 11236725Sbostic gargv = ++argv; 11336811Sbostic for (;;) { 11436725Sbostic end = 0; 11536725Sbostic /* find next format specification */ 11636725Sbostic next: for (start = fmt;; ++fmt) { 11736725Sbostic if (!*fmt) { 11836725Sbostic /* avoid infinite loop */ 11936725Sbostic if (end == 1) { 120*59551Sbostic warnx("missing format character", 121*59551Sbostic NULL, NULL); 12250477Sbostic return (1); 12336725Sbostic } 12436725Sbostic end = 1; 12536725Sbostic if (fmt > start) 12636811Sbostic (void)printf("%s", start); 12736725Sbostic if (!*gargv) 12850477Sbostic return (0); 12936725Sbostic fmt = format; 13036725Sbostic goto next; 13136725Sbostic } 13236725Sbostic /* %% prints a % */ 13338480Sbostic if (*fmt == '%') { 13438480Sbostic if (*++fmt != '%') 13538480Sbostic break; 13638480Sbostic *fmt++ = '\0'; 13738480Sbostic (void)printf("%s", start); 13838480Sbostic goto next; 13938480Sbostic } 14036725Sbostic } 14136725Sbostic 14236725Sbostic /* skip to field width */ 14359549Sbostic for (; strchr(skip1, *fmt); ++fmt); 14459549Sbostic if (*fmt == '*') { 14559549Sbostic if (getint(&fieldwidth)) 14659549Sbostic return (1); 14759549Sbostic } else 14859549Sbostic fieldwidth = 0; 14936879Sbostic 15036879Sbostic /* skip to possible '.', get following precision */ 15159549Sbostic for (; strchr(skip2, *fmt); ++fmt); 15236725Sbostic if (*fmt == '.') 15336725Sbostic ++fmt; 15459549Sbostic if (*fmt == '*') { 15559549Sbostic if (getint(&precision)) 15659549Sbostic return (1); 15759549Sbostic } else 15859549Sbostic precision = 0; 15936879Sbostic 16036725Sbostic /* skip to conversion char */ 16159549Sbostic for (; strchr(skip2, *fmt); ++fmt); 16236725Sbostic if (!*fmt) { 163*59551Sbostic warnx("missing format character", NULL, NULL); 16450477Sbostic return (1); 16536725Sbostic } 16636725Sbostic 16736725Sbostic convch = *fmt; 16836725Sbostic nextch = *++fmt; 16936725Sbostic *fmt = '\0'; 17036725Sbostic switch(convch) { 17136725Sbostic case 'c': { 17250477Sbostic char p; 17350477Sbostic 17450477Sbostic p = getchr(); 17536725Sbostic PF(start, p); 17636725Sbostic break; 17736725Sbostic } 17836725Sbostic case 's': { 17950477Sbostic char *p; 18050477Sbostic 18150477Sbostic p = getstr(); 18236725Sbostic PF(start, p); 18336725Sbostic break; 18436725Sbostic } 18536729Sbostic case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 18650477Sbostic long p; 18750477Sbostic char *f; 18850477Sbostic 18950477Sbostic if ((f = mklong(start, convch)) == NULL) 19050477Sbostic return (1); 19159549Sbostic if (getlong(&p)) 19259549Sbostic return (1); 19336725Sbostic PF(f, p); 19436725Sbostic break; 19536725Sbostic } 19636725Sbostic case 'e': case 'E': case 'f': case 'g': case 'G': { 19750477Sbostic double p; 19850477Sbostic 19950477Sbostic p = getdouble(); 20036725Sbostic PF(start, p); 20136725Sbostic break; 20236725Sbostic } 20336725Sbostic default: 204*59551Sbostic warnx("illegal format character", NULL, NULL); 20550477Sbostic return (1); 20636725Sbostic } 20736725Sbostic *fmt = nextch; 20836725Sbostic } 20936725Sbostic /* NOTREACHED */ 21036725Sbostic } 21136725Sbostic 21250477Sbostic static char * 21336725Sbostic mklong(str, ch) 21450477Sbostic char *str; 21550477Sbostic int ch; 21636725Sbostic { 21753564Smarc static char copy[64]; 21836725Sbostic int len; 21936725Sbostic 22036725Sbostic len = strlen(str) + 2; 22159549Sbostic memmove(copy, str, len - 3); 22253564Smarc copy[len - 3] = 'l'; 22353564Smarc copy[len - 2] = ch; 22453564Smarc copy[len - 1] = '\0'; 22559549Sbostic return (copy); 22636725Sbostic } 22736725Sbostic 22850477Sbostic static void 22936725Sbostic escape(fmt) 23036725Sbostic register char *fmt; 23136725Sbostic { 23236725Sbostic register char *store; 23336725Sbostic register int value, c; 23436725Sbostic 23536725Sbostic for (store = fmt; c = *fmt; ++fmt, ++store) { 23636725Sbostic if (c != '\\') { 23736725Sbostic *store = c; 23836725Sbostic continue; 23936725Sbostic } 24036725Sbostic switch (*++fmt) { 24136725Sbostic case '\0': /* EOS, user error */ 24236725Sbostic *store = '\\'; 24336725Sbostic *++store = '\0'; 24436725Sbostic return; 24536725Sbostic case '\\': /* backslash */ 24636728Sbostic case '\'': /* single quote */ 24736728Sbostic *store = *fmt; 24836725Sbostic break; 24936725Sbostic case 'a': /* bell/alert */ 25036725Sbostic *store = '\7'; 25136725Sbostic break; 25236725Sbostic case 'b': /* backspace */ 25336725Sbostic *store = '\b'; 25436725Sbostic break; 25536725Sbostic case 'f': /* form-feed */ 25636725Sbostic *store = '\f'; 25736725Sbostic break; 25836725Sbostic case 'n': /* newline */ 25936725Sbostic *store = '\n'; 26036725Sbostic break; 26136725Sbostic case 'r': /* carriage-return */ 26236725Sbostic *store = '\r'; 26336725Sbostic break; 26436725Sbostic case 't': /* horizontal tab */ 26536725Sbostic *store = '\t'; 26636725Sbostic break; 26736725Sbostic case 'v': /* vertical tab */ 26836725Sbostic *store = '\13'; 26936725Sbostic break; 27036725Sbostic /* octal constant */ 27136725Sbostic case '0': case '1': case '2': case '3': 27236725Sbostic case '4': case '5': case '6': case '7': 27336725Sbostic for (c = 3, value = 0; 27436725Sbostic c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 27536725Sbostic value <<= 3; 27636725Sbostic value += *fmt - '0'; 27736725Sbostic } 27836725Sbostic --fmt; 27936725Sbostic *store = value; 28036725Sbostic break; 28136725Sbostic default: 28236725Sbostic *store = *fmt; 28336725Sbostic break; 28436725Sbostic } 28536725Sbostic } 28636725Sbostic *store = '\0'; 28736725Sbostic } 28836725Sbostic 28950477Sbostic static int 29036725Sbostic getchr() 29136725Sbostic { 29236725Sbostic if (!*gargv) 29359549Sbostic return ('\0'); 29459549Sbostic return ((int)**gargv++); 29536725Sbostic } 29636725Sbostic 29750477Sbostic static char * 29836725Sbostic getstr() 29936725Sbostic { 30036725Sbostic if (!*gargv) 30159549Sbostic return (""); 30259549Sbostic return (*gargv++); 30336725Sbostic } 30436725Sbostic 30553564Smarc static char *Number = "+-.0123456789"; 30650477Sbostic static int 30759549Sbostic getint(ip) 30859549Sbostic int *ip; 30936725Sbostic { 31059549Sbostic long val; 31159549Sbostic 31259549Sbostic if (getlong(&val)) 31359549Sbostic return (1); 31459549Sbostic if (val > INT_MAX) { 315*59551Sbostic warnx("%s", *gargv, strerror(ERANGE)); 31659549Sbostic return (1); 31759549Sbostic } 31859549Sbostic *ip = val; 31959549Sbostic return (0); 32036725Sbostic } 32136725Sbostic 32259549Sbostic static int 32359549Sbostic getlong(lp) 32459549Sbostic long *lp; 32536725Sbostic { 32659549Sbostic long val; 32759549Sbostic char *ep; 32859549Sbostic 32959549Sbostic if (!*gargv) { 33059549Sbostic *lp = 0; 33159549Sbostic return (0); 33259549Sbostic } 33359549Sbostic if (strchr(Number, **gargv)) { 33459549Sbostic errno = 0; 33559549Sbostic val = strtol(*gargv, &ep, 0); 33659549Sbostic if (*ep != '\0') { 337*59551Sbostic warnx("%s: illegal number", *gargv, NULL); 33859549Sbostic return (1); 33959549Sbostic } 34059549Sbostic if (errno == ERANGE) 34159549Sbostic if (val == LONG_MAX) { 342*59551Sbostic warnx("%s", *gargv, strerror(ERANGE)); 34359549Sbostic return (1); 34459549Sbostic } 34559549Sbostic if (val == LONG_MIN) { 346*59551Sbostic warnx("%s", *gargv, strerror(ERANGE)); 34759549Sbostic return (1); 34859549Sbostic } 34959549Sbostic 35059549Sbostic *lp = val; 35159549Sbostic ++gargv; 35259549Sbostic return (0); 35359549Sbostic } 35459549Sbostic *lp = (long)asciicode(); 35559549Sbostic return (0); 35636725Sbostic } 35736725Sbostic 35850477Sbostic static double 35936725Sbostic getdouble() 36036725Sbostic { 36136725Sbostic if (!*gargv) 36259549Sbostic return ((double)0); 36359549Sbostic if (strchr(Number, **gargv)) 36459549Sbostic return (atof(*gargv++)); 36559549Sbostic return ((double)asciicode()); 36636725Sbostic } 36736725Sbostic 36850477Sbostic static int 36936725Sbostic asciicode() 37036725Sbostic { 37150477Sbostic register int ch; 37236725Sbostic 37336725Sbostic ch = **gargv; 37436725Sbostic if (ch == '\'' || ch == '"') 37536725Sbostic ch = (*gargv)[1]; 37636725Sbostic ++gargv; 37759549Sbostic return (ch); 37836725Sbostic } 37950477Sbostic 38050477Sbostic static void 38159549Sbostic usage() 38250477Sbostic { 38359549Sbostic (void)fprintf(stderr, "usage: printf format [arg ...]\n"); 38450477Sbostic } 385