136725Sbostic /*
263940Sbostic * Copyright (c) 1989, 1993
363940Sbostic * The Regents of the University of California. All rights reserved.
436725Sbostic *
542758Sbostic * %sccs.include.redist.c%
636725Sbostic */
736725Sbostic
859549Sbostic #if !defined(BUILTIN) && !defined(SHELL)
936725Sbostic #ifndef lint
1063940Sbostic static char copyright[] =
1163940Sbostic "@(#) Copyright (c) 1989, 1993\n\
1263940Sbostic The Regents of the University of California. All rights reserved.\n";
1336725Sbostic #endif /* not lint */
1450477Sbostic #endif
1536725Sbostic
1636725Sbostic #ifndef lint
17*68574Schristos static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 03/22/95";
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
3959551Sbostic #define warnx(a, b, c) { \
4059549Sbostic char buf[64]; \
4159551Sbostic (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
progprintf(argc,argv)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) {
12059551Sbostic warnx("missing format character",
12159551Sbostic 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) {
16359551Sbostic 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:
20459551Sbostic warnx("illegal format character", NULL, NULL);
20550477Sbostic return (1);
20636725Sbostic }
20736725Sbostic *fmt = nextch;
20836725Sbostic }
20936725Sbostic /* NOTREACHED */
21036725Sbostic }
21136725Sbostic
21250477Sbostic static char *
mklong(str,ch)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
escape(fmt)22936725Sbostic escape(fmt)
23036725Sbostic register char *fmt;
23136725Sbostic {
23236725Sbostic register char *store;
23336725Sbostic register int value, c;
23436725Sbostic
235*68574Schristos for (store = fmt; (c = *fmt) != '\0'; ++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
getchr()29036725Sbostic getchr()
29136725Sbostic {
29236725Sbostic if (!*gargv)
29359549Sbostic return ('\0');
29459549Sbostic return ((int)**gargv++);
29536725Sbostic }
29636725Sbostic
29750477Sbostic static char *
getstr()29836725Sbostic getstr()
29936725Sbostic {
30036725Sbostic if (!*gargv)
30159549Sbostic return ("");
30259549Sbostic return (*gargv++);
30336725Sbostic }
30436725Sbostic
30553564Smarc static char *Number = "+-.0123456789";
30650477Sbostic static int
getint(ip)30759549Sbostic getint(ip)
30859549Sbostic int *ip;
30936725Sbostic {
31059549Sbostic long val;
31159549Sbostic
31259549Sbostic if (getlong(&val))
31359549Sbostic return (1);
31459549Sbostic if (val > INT_MAX) {
31563939Sbostic warnx("%s: %s", *gargv, strerror(ERANGE));
31659549Sbostic return (1);
31759549Sbostic }
31859549Sbostic *ip = val;
31959549Sbostic return (0);
32036725Sbostic }
32136725Sbostic
32259549Sbostic static int
getlong(lp)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') {
33759551Sbostic warnx("%s: illegal number", *gargv, NULL);
33859549Sbostic return (1);
33959549Sbostic }
34059549Sbostic if (errno == ERANGE)
34159549Sbostic if (val == LONG_MAX) {
34263939Sbostic warnx("%s: %s", *gargv, strerror(ERANGE));
34359549Sbostic return (1);
34459549Sbostic }
34559549Sbostic if (val == LONG_MIN) {
34663939Sbostic warnx("%s: %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
getdouble()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
asciicode()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
usage()38159549Sbostic usage()
38250477Sbostic {
38359549Sbostic (void)fprintf(stderr, "usage: printf format [arg ...]\n");
38450477Sbostic }
385