]> git.ipfire.org Git - thirdparty/bash.git/blob - examples/loadables/printf.c
Imported from ../bash-2.0.tar.gz.
[thirdparty/bash.git] / examples / loadables / printf.c
1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. 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 #if !defined(BUILTIN) && !defined(SHELL)
35 #ifndef lint
36 static char copyright[] =
37 "@(#) Copyright (c) 1989, 1993\n\
38 The Regents of the University of California. All rights reserved.\n";
39 #endif /* not lint */
40 #endif
41
42 #ifndef lint
43 static char sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
44 #endif /* not lint */
45
46 #include <sys/types.h>
47
48 #include <errno.h>
49 #include <limits.h>
50 #include <stdio.h>
51
52 #include "bashansi.h"
53 #include "shell.h"
54 #include "builtins.h"
55 #include "stdc.h"
56
57 #if !defined (errno)
58 extern int errno;
59 #endif
60
61 #define PF(f, func) { \
62 if (fieldwidth) \
63 if (precision) \
64 (void)printf(f, fieldwidth, precision, func); \
65 else \
66 (void)printf(f, fieldwidth, func); \
67 else if (precision) \
68 (void)printf(f, precision, func); \
69 else \
70 (void)printf(f, func); \
71 }
72
73 static int asciicode __P((void));
74 static void escape __P((char *));
75 static int getchr __P((void));
76 static double getdouble __P((void));
77 static int getint __P((int *));
78 static int getlong __P((long *));
79 static char *getstr __P((void));
80 static char *mklong __P((char *, int));
81 static void usage __P((void));
82
83 static char **gargv;
84
85 int printf_builtin ();
86 static int printf_main ();
87 extern char *this_command_name;
88 extern char *single_quote ();
89 extern char **make_builtin_argv ();
90
91 static char *printf_doc[] = {
92 "printf formats and prints its arguments, after the first, under control",
93 "of the format. The format is a character string which contains three",
94 "types of objects: plain characters, which are simply copied to standard",
95 "output, character escape sequences which are converted and copied to the",
96 "standard output, and format specifications, each of which causes printing",
97 "of the next successive argument. In addition to the standard printf(1)",
98 "formats, %%b means to expand escapes in the corresponding argument, and",
99 "%%q means to quote the argument in a way that can be reused as shell input.",
100 (char *)NULL
101 };
102
103 struct builtin printf_struct = {
104 "printf",
105 printf_builtin,
106 BUILTIN_ENABLED,
107 printf_doc,
108 "printf format [arguments]",
109 (char *)0
110 };
111
112 int
113 printf_builtin (list)
114 WORD_LIST *list;
115 {
116 int c, r;
117 char **v;
118 WORD_LIST *l;
119
120 v = make_builtin_argv (list, &c);
121 r = printf_main (c, v);
122 free (v);
123 fflush(stdout);
124
125 return r;
126 }
127
128 static int
129 printf_main(argc, argv)
130 int argc;
131 char *argv[];
132 {
133 extern int optind;
134 static char *skip1, *skip2;
135 int ch, end, fieldwidth, precision;
136 char convch, nextch, *format, *fmt, *start;
137
138 while ((ch = getopt(argc, argv, "")) != EOF)
139 switch (ch) {
140 case '?':
141 default:
142 usage();
143 return (1);
144 }
145 argc -= optind;
146 argv += optind;
147
148 if (argc < 1) {
149 usage();
150 return (1);
151 }
152
153 /*
154 * Basic algorithm is to scan the format string for conversion
155 * specifications -- once one is found, find out if the field
156 * width or precision is a '*'; if it is, gather up value. Note,
157 * format strings are reused as necessary to use up the provided
158 * arguments, arguments of zero/null string are provided to use
159 * up the format string.
160 */
161 skip1 = "#-+ 0";
162 skip2 = "*0123456789";
163
164 escape(fmt = format = *argv); /* backslash interpretation */
165 gargv = ++argv;
166 for (;;) {
167 end = 0;
168 /* find next format specification */
169 next: for (start = fmt;; ++fmt) {
170 if (!*fmt) {
171 /* avoid infinite loop */
172 if (end == 1) {
173 warnx("missing format character",
174 NULL, NULL);
175 return (1);
176 }
177 end = 1;
178 if (fmt > start)
179 (void)printf("%s", start);
180 if (!*gargv)
181 return (0);
182 fmt = format;
183 goto next;
184 }
185 /* %% prints a % */
186 if (*fmt == '%') {
187 if (*++fmt != '%')
188 break;
189 *fmt++ = '\0';
190 (void)printf("%s", start);
191 goto next;
192 }
193 }
194
195 /* skip to field width */
196 for (; strchr(skip1, *fmt); ++fmt);
197 if (*fmt == '*') {
198 if (getint(&fieldwidth))
199 return (1);
200 } else
201 fieldwidth = 0;
202
203 /* skip to possible '.', get following precision */
204 for (; strchr(skip2, *fmt); ++fmt);
205 if (*fmt == '.')
206 ++fmt;
207 if (*fmt == '*') {
208 if (getint(&precision))
209 return (1);
210 } else
211 precision = 0;
212
213 /* skip to conversion char */
214 for (; strchr(skip2, *fmt); ++fmt);
215 if (!*fmt) {
216 warnx("missing format character", NULL, NULL);
217 return (1);
218 }
219
220 convch = *fmt;
221 nextch = *++fmt;
222 *fmt = '\0';
223 switch(convch) {
224 case 'c': {
225 char p;
226
227 p = getchr();
228 PF(start, p);
229 break;
230 }
231 case 's': {
232 char *p;
233
234 p = getstr();
235 PF(start, p);
236 break;
237 }
238 case 'b': { /* expand escapes in argument */
239 char *p;
240
241 p = getstr();
242 escape(p);
243 PF("%s", p);
244 break;
245 }
246 case 'q': { /* print with shell single quoting */
247 char *p, *p2;
248
249 p = getstr();
250 p2 = single_quote(p);
251 PF("%s", p2);
252 free(p2);
253 break;
254 }
255 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
256 long p;
257 char *f;
258
259 if ((f = mklong(start, convch)) == NULL)
260 return (1);
261 if (getlong(&p))
262 return (1);
263 PF(f, p);
264 break;
265 }
266 case 'e': case 'E': case 'f': case 'g': case 'G': {
267 double p;
268
269 p = getdouble();
270 PF(start, p);
271 break;
272 }
273 default:
274 warnx("illegal format character", NULL, NULL);
275 return (1);
276 }
277 *fmt = nextch;
278 }
279 /* NOTREACHED */
280 }
281
282 static char *
283 mklong(str, ch)
284 char *str;
285 int ch;
286 {
287 static char copy[64];
288 int len;
289
290 len = strlen(str) + 2;
291 memmove(copy, str, len - 3);
292 copy[len - 3] = 'l';
293 copy[len - 2] = ch;
294 copy[len - 1] = '\0';
295 return (copy);
296 }
297
298 static void
299 escape(fmt)
300 register char *fmt;
301 {
302 register char *store;
303 register int value, c;
304
305 for (store = fmt; c = *fmt; ++fmt, ++store) {
306 if (c != '\\') {
307 *store = c;
308 continue;
309 }
310 switch (*++fmt) {
311 case '\0': /* EOS, user error */
312 *store = '\\';
313 *++store = '\0';
314 return;
315 case '\\': /* backslash */
316 case '\'': /* single quote */
317 *store = *fmt;
318 break;
319 case 'a': /* bell/alert */
320 *store = '\7';
321 break;
322 case 'b': /* backspace */
323 *store = '\b';
324 break;
325 case 'c':
326 return;
327 case 'e':
328 case 'E':
329 *store = '\033';
330 break;
331 case 'f': /* form-feed */
332 *store = '\f';
333 break;
334 case 'n': /* newline */
335 *store = '\n';
336 break;
337 case 'r': /* carriage-return */
338 *store = '\r';
339 break;
340 case 't': /* horizontal tab */
341 *store = '\t';
342 break;
343 case 'v': /* vertical tab */
344 *store = '\13';
345 break;
346 /* octal constant */
347 case '0': case '1': case '2': case '3':
348 case '4': case '5': case '6': case '7':
349 for (c = 3, value = 0;
350 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
351 value <<= 3;
352 value += *fmt - '0';
353 }
354 --fmt;
355 *store = value;
356 break;
357 default:
358 *store = *fmt;
359 break;
360 }
361 }
362 *store = '\0';
363 }
364
365 static int
366 getchr()
367 {
368 if (!*gargv)
369 return ('\0');
370 return ((int)**gargv++);
371 }
372
373 static char *
374 getstr()
375 {
376 if (!*gargv)
377 return ("");
378 return (*gargv++);
379 }
380
381 static char *Number = "+-.0123456789";
382 static int
383 getint(ip)
384 int *ip;
385 {
386 long val;
387
388 if (getlong(&val))
389 return (1);
390 if (val > INT_MAX) {
391 warnx("%s: %s", *gargv, strerror(ERANGE));
392 return (1);
393 }
394 *ip = val;
395 return (0);
396 }
397
398 static int
399 getlong(lp)
400 long *lp;
401 {
402 long val;
403 char *ep;
404
405 if (!*gargv) {
406 *lp = 0;
407 return (0);
408 }
409 if (strchr(Number, **gargv)) {
410 errno = 0;
411 val = strtol(*gargv, &ep, 0);
412 if (*ep != '\0') {
413 warnx("%s: illegal number", *gargv, NULL);
414 return (1);
415 }
416 if (errno == ERANGE)
417 if (val == LONG_MAX) {
418 warnx("%s: %s", *gargv, strerror(ERANGE));
419 return (1);
420 }
421 if (val == LONG_MIN) {
422 warnx("%s: %s", *gargv, strerror(ERANGE));
423 return (1);
424 }
425
426 *lp = val;
427 ++gargv;
428 return (0);
429 }
430 *lp = (long)asciicode();
431 return (0);
432 }
433
434 static double
435 getdouble()
436 {
437 if (!*gargv)
438 return ((double)0);
439 if (strchr(Number, **gargv))
440 return (atof(*gargv++));
441 return ((double)asciicode());
442 }
443
444 static int
445 asciicode()
446 {
447 register int ch;
448
449 ch = **gargv;
450 if (ch == '\'' || ch == '"')
451 ch = (*gargv)[1];
452 ++gargv;
453 return (ch);
454 }
455
456 static void
457 usage()
458 {
459 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
460 }