new copyright notice
[unix-history] / usr / src / usr.bin / printf / printf.c
CommitLineData
6b8cdffd
KB
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
6d936b27 5 * %sccs.include.redist.c%
6b8cdffd
KB
6 */
7
8#ifndef lint
9char copyright[] =
10"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
11 All rights reserved.\n";
12#endif /* not lint */
13
14#ifndef lint
6d936b27 15static char sccsid[] = "@(#)printf.c 5.9 (Berkeley) %G%";
6b8cdffd
KB
16#endif /* not lint */
17
18#include <sys/types.h>
19#include <stdio.h>
20
21#define PF(f, func) { \
22 if (fieldwidth) \
23 if (precision) \
5d351dd2 24 (void)printf(f, fieldwidth, precision, func); \
6b8cdffd 25 else \
5d351dd2 26 (void)printf(f, fieldwidth, func); \
6b8cdffd 27 else if (precision) \
5d351dd2 28 (void)printf(f, precision, func); \
6b8cdffd 29 else \
5d351dd2 30 (void)printf(f, func); \
6b8cdffd
KB
31}
32
33char **gargv;
34
35main(argc, argv)
36 int argc;
37 char **argv;
38{
39 static char *skip1, *skip2;
40 register char *format, *fmt, *start;
5d351dd2 41 register int end, fieldwidth, precision;
6b8cdffd
KB
42 char convch, nextch, *getstr(), *index(), *mklong();
43 double getdouble();
44 long getlong();
45
46 if (argc < 2) {
47 fprintf(stderr, "usage: printf format [arg ...]\n");
5d351dd2 48 exit(1);
6b8cdffd
KB
49 }
50
51 /*
52 * Basic algorithm is to scan the format string for conversion
53 * specifications -- once one is found, find out if the field
54 * width or precision is a '*'; if it is, gather up value. Note,
55 * format strings are reused as necessary to use up the provided
56 * arguments, arguments of zero/null string are provided to use
57 * up the format string.
58 */
59 skip1 = "#-+ 0";
60 skip2 = "*0123456789";
61
62 escape(fmt = format = *++argv); /* backslash interpretation */
63 gargv = ++argv;
5d351dd2 64 for (;;) {
6b8cdffd
KB
65 end = 0;
66 /* find next format specification */
67next: for (start = fmt;; ++fmt) {
68 if (!*fmt) {
69 /* avoid infinite loop */
70 if (end == 1) {
71 fprintf(stderr,
72 "printf: missing format character.\n");
5d351dd2 73 exit(1);
6b8cdffd
KB
74 }
75 end = 1;
76 if (fmt > start)
5d351dd2 77 (void)printf("%s", start);
6b8cdffd 78 if (!*gargv)
5d351dd2 79 exit(0);
6b8cdffd
KB
80 fmt = format;
81 goto next;
82 }
83 /* %% prints a % */
8a6a6c83
KB
84 if (*fmt == '%') {
85 if (*++fmt != '%')
86 break;
87 *fmt++ = '\0';
88 (void)printf("%s", start);
89 goto next;
90 }
6b8cdffd
KB
91 }
92
93 /* skip to field width */
94 for (; index(skip1, *fmt); ++fmt);
62216505
KB
95 fieldwidth = *fmt == '*' ? getint() : 0;
96
97 /* skip to possible '.', get following precision */
6b8cdffd
KB
98 for (; index(skip2, *fmt); ++fmt);
99 if (*fmt == '.')
100 ++fmt;
62216505
KB
101 precision = *fmt == '*' ? getint() : 0;
102
6b8cdffd
KB
103 /* skip to conversion char */
104 for (; index(skip2, *fmt); ++fmt);
105 if (!*fmt) {
106 fprintf(stderr, "printf: missing format character.\n");
5d351dd2 107 exit(1);
6b8cdffd
KB
108 }
109
110 convch = *fmt;
111 nextch = *++fmt;
112 *fmt = '\0';
113 switch(convch) {
114 case 'c': {
115 char p = getchr();
116 PF(start, p);
117 break;
118 }
119 case 's': {
120 char *p = getstr();
121 PF(start, p);
122 break;
123 }
a0486b62 124 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
6b8cdffd
KB
125 char *f = mklong(start, convch);
126 long p = getlong();
127 PF(f, p);
128 break;
129 }
130 case 'e': case 'E': case 'f': case 'g': case 'G': {
131 double p = getdouble();
132 PF(start, p);
133 break;
134 }
135 default:
136 fprintf(stderr, "printf: illegal format character.\n");
5d351dd2 137 exit(1);
6b8cdffd
KB
138 }
139 *fmt = nextch;
140 }
141 /* NOTREACHED */
142}
143
144char *
145mklong(str, ch)
146 char *str, ch;
147{
148 int len;
149 char *copy, *malloc();
150
151 len = strlen(str) + 2;
152 if (!(copy = malloc((u_int)len))) { /* never freed; XXX */
153 fprintf(stderr, "printf: out of memory.\n");
5d351dd2 154 exit(1);
6b8cdffd
KB
155 }
156 bcopy(str, copy, len - 3);
157 copy[len - 3] = 'l';
158 copy[len - 2] = ch;
159 copy[len - 1] = '\0';
160 return(copy);
161}
162
163escape(fmt)
164 register char *fmt;
165{
166 register char *store;
167 register int value, c;
168
169 for (store = fmt; c = *fmt; ++fmt, ++store) {
170 if (c != '\\') {
171 *store = c;
172 continue;
173 }
174 switch (*++fmt) {
175 case '\0': /* EOS, user error */
176 *store = '\\';
177 *++store = '\0';
178 return;
179 case '\\': /* backslash */
93e4ef47
KB
180 case '\'': /* single quote */
181 *store = *fmt;
6b8cdffd
KB
182 break;
183 case 'a': /* bell/alert */
184 *store = '\7';
185 break;
186 case 'b': /* backspace */
187 *store = '\b';
188 break;
189 case 'f': /* form-feed */
190 *store = '\f';
191 break;
192 case 'n': /* newline */
193 *store = '\n';
194 break;
195 case 'r': /* carriage-return */
196 *store = '\r';
197 break;
198 case 't': /* horizontal tab */
199 *store = '\t';
200 break;
201 case 'v': /* vertical tab */
202 *store = '\13';
203 break;
204 /* octal constant */
205 case '0': case '1': case '2': case '3':
206 case '4': case '5': case '6': case '7':
207 for (c = 3, value = 0;
208 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
209 value <<= 3;
210 value += *fmt - '0';
211 }
212 --fmt;
213 *store = value;
214 break;
215 default:
216 *store = *fmt;
217 break;
218 }
219 }
220 *store = '\0';
221}
222
223getchr()
224{
225 if (!*gargv)
226 return((int)'\0');
227 return((int)**gargv++);
228}
229
230char *
231getstr()
232{
233 if (!*gargv)
234 return("");
235 return(*gargv++);
236}
237
256c1f3d 238static char *number = "+-.0123456789";
6b8cdffd
KB
239getint()
240{
241 if (!*gargv)
242 return(0);
243 if (index(number, **gargv))
244 return(atoi(*gargv++));
245 return(asciicode());
246}
247
248long
249getlong()
250{
251 long atol();
252
253 if (!*gargv)
254 return((long)0);
255 if (index(number, **gargv))
802c33fc 256 return(strtol(*gargv++, (char **)NULL, 0));
6b8cdffd
KB
257 return((long)asciicode());
258}
259
260double
261getdouble()
262{
263 double atof();
264
265 if (!*gargv)
266 return((double)0);
267 if (index(number, **gargv))
268 return(atof(*gargv++));
269 return((double)asciicode());
270}
271
272asciicode()
273{
274 register char ch;
275
276 ch = **gargv;
277 if (ch == '\'' || ch == '"')
278 ch = (*gargv)[1];
279 ++gargv;
280 return(ch);
281}