CFLAGS= to CFLAGS+=
[unix-history] / usr.bin / printf / printf.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35char copyright[] =
36"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
37 All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)printf.c 5.9 (Berkeley) 6/1/90";
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <stdio.h>
46
47#define PF(f, func) { \
48 if (fieldwidth) \
49 if (precision) \
50 (void)printf(f, fieldwidth, precision, func); \
51 else \
52 (void)printf(f, fieldwidth, func); \
53 else if (precision) \
54 (void)printf(f, precision, func); \
55 else \
56 (void)printf(f, func); \
57}
58
59char **gargv;
60
61main(argc, argv)
62 int argc;
63 char **argv;
64{
65 static char *skip1, *skip2;
66 register char *format, *fmt, *start;
67 register int end, fieldwidth, precision;
68 char convch, nextch, *getstr(), *index(), *mklong();
69 double getdouble();
70 long getlong();
71
72 if (argc < 2) {
73 fprintf(stderr, "usage: printf format [arg ...]\n");
74 exit(1);
75 }
76
77 /*
78 * Basic algorithm is to scan the format string for conversion
79 * specifications -- once one is found, find out if the field
80 * width or precision is a '*'; if it is, gather up value. Note,
81 * format strings are reused as necessary to use up the provided
82 * arguments, arguments of zero/null string are provided to use
83 * up the format string.
84 */
85 skip1 = "#-+ 0";
86 skip2 = "*0123456789";
87
88 escape(fmt = format = *++argv); /* backslash interpretation */
89 gargv = ++argv;
90 for (;;) {
91 end = 0;
92 /* find next format specification */
93next: for (start = fmt;; ++fmt) {
94 if (!*fmt) {
95 /* avoid infinite loop */
96 if (end == 1) {
97 fprintf(stderr,
98 "printf: missing format character.\n");
99 exit(1);
100 }
101 end = 1;
102 if (fmt > start)
103 (void)printf("%s", start);
104 if (!*gargv)
105 exit(0);
106 fmt = format;
107 goto next;
108 }
109 /* %% prints a % */
110 if (*fmt == '%') {
111 if (*++fmt != '%')
112 break;
113 *fmt++ = '\0';
114 (void)printf("%s", start);
115 goto next;
116 }
117 }
118
119 /* skip to field width */
120 for (; index(skip1, *fmt); ++fmt);
121 fieldwidth = *fmt == '*' ? getint() : 0;
122
123 /* skip to possible '.', get following precision */
124 for (; index(skip2, *fmt); ++fmt);
125 if (*fmt == '.')
126 ++fmt;
127 precision = *fmt == '*' ? getint() : 0;
128
129 /* skip to conversion char */
130 for (; index(skip2, *fmt); ++fmt);
131 if (!*fmt) {
132 fprintf(stderr, "printf: missing format character.\n");
133 exit(1);
134 }
135
136 convch = *fmt;
137 nextch = *++fmt;
138 *fmt = '\0';
139 switch(convch) {
140 case 'c': {
141 char p = getchr();
142 PF(start, p);
143 break;
144 }
145 case 's': {
146 char *p = getstr();
147 PF(start, p);
148 break;
149 }
150 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
151 char *f = mklong(start, convch);
152 long p = getlong();
153 PF(f, p);
154 break;
155 }
156 case 'e': case 'E': case 'f': case 'g': case 'G': {
157 double p = getdouble();
158 PF(start, p);
159 break;
160 }
161 default:
162 fprintf(stderr, "printf: illegal format character.\n");
163 exit(1);
164 }
165 *fmt = nextch;
166 }
167 /* NOTREACHED */
168}
169
170char *
171mklong(str, ch)
172 char *str, ch;
173{
174 int len;
175 char *copy, *malloc();
176
177 len = strlen(str) + 2;
178 if (!(copy = malloc((u_int)len))) { /* never freed; XXX */
179 fprintf(stderr, "printf: out of memory.\n");
180 exit(1);
181 }
182 bcopy(str, copy, len - 3);
183 copy[len - 3] = 'l';
184 copy[len - 2] = ch;
185 copy[len - 1] = '\0';
186 return(copy);
187}
188
189escape(fmt)
190 register char *fmt;
191{
192 register char *store;
193 register int value, c;
194
195 for (store = fmt; c = *fmt; ++fmt, ++store) {
196 if (c != '\\') {
197 *store = c;
198 continue;
199 }
200 switch (*++fmt) {
201 case '\0': /* EOS, user error */
202 *store = '\\';
203 *++store = '\0';
204 return;
205 case '\\': /* backslash */
206 case '\'': /* single quote */
207 *store = *fmt;
208 break;
209 case 'a': /* bell/alert */
210 *store = '\7';
211 break;
212 case 'b': /* backspace */
213 *store = '\b';
214 break;
215 case 'f': /* form-feed */
216 *store = '\f';
217 break;
218 case 'n': /* newline */
219 *store = '\n';
220 break;
221 case 'r': /* carriage-return */
222 *store = '\r';
223 break;
224 case 't': /* horizontal tab */
225 *store = '\t';
226 break;
227 case 'v': /* vertical tab */
228 *store = '\13';
229 break;
230 /* octal constant */
231 case '0': case '1': case '2': case '3':
232 case '4': case '5': case '6': case '7':
233 for (c = 3, value = 0;
234 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
235 value <<= 3;
236 value += *fmt - '0';
237 }
238 --fmt;
239 *store = value;
240 break;
241 default:
242 *store = *fmt;
243 break;
244 }
245 }
246 *store = '\0';
247}
248
249getchr()
250{
251 if (!*gargv)
252 return((int)'\0');
253 return((int)**gargv++);
254}
255
256char *
257getstr()
258{
259 if (!*gargv)
260 return("");
261 return(*gargv++);
262}
263
264static char *number = "+-.0123456789";
265getint()
266{
267 if (!*gargv)
268 return(0);
269 if (index(number, **gargv))
270 return(atoi(*gargv++));
271 return(asciicode());
272}
273
274long
275getlong()
276{
277 long atol();
278
279 if (!*gargv)
280 return((long)0);
281 if (index(number, **gargv))
282 return(strtol(*gargv++, (char **)NULL, 0));
283 return((long)asciicode());
284}
285
286double
287getdouble()
288{
289 double atof();
290
291 if (!*gargv)
292 return((double)0);
293 if (index(number, **gargv))
294 return(atof(*gargv++));
295 return((double)asciicode());
296}
297
298asciicode()
299{
300 register char ch;
301
302 ch = **gargv;
303 if (ch == '\'' || ch == '"')
304 ch = (*gargv)[1];
305 ++gargv;
306 return(ch);
307}