]> git.saurik.com Git - apple/shell_cmds.git/blob - printf/printf.c
shell_cmds-118.tar.gz
[apple/shell_cmds.git] / printf / printf.c
1 /* $NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if !defined(BUILTIN) && !defined(SHELL)
39 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
40 The Regents of the University of California. All rights reserved.\n");
41 #endif
42 #endif
43
44 #ifndef lint
45 #if 0
46 static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95";
47 #else
48 __RCSID("$NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $");
49 #endif
50 #endif /* not lint */
51
52 #include <sys/types.h>
53
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <limits.h>
58 #include <locale.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 static int print_escape_str __P((const char *));
66 static size_t print_escape __P((const char *));
67
68 static int getchr __P((void));
69 static double getdouble __P((void));
70 static int getint __P((void));
71 static long getlong __P((void));
72 static unsigned long getulong __P ((void));
73 static char *getstr __P((void));
74 static char *mklong __P((const char *, int));
75 static void check_conversion __P((const char *, const char *));
76 static void usage __P((void));
77
78 static int rval;
79 static char **gargv;
80
81 #ifdef BUILTIN
82 int progprintf __P((int, char **));
83 #else
84 int main __P((int, char **));
85 #endif
86
87 #define isodigit(c) ((c) >= '0' && (c) <= '7')
88 #define octtobin(c) ((c) - '0')
89 #define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
90
91 #ifdef SHELL
92 #define main printfcmd
93 #ifdef __APPLE__
94 #include "bltin/bltin.h"
95 #else
96 #include "../../bin/sh/bltin/bltin.h"
97 #endif
98
99 #ifdef __STDC__
100 #include <stdarg.h>
101 #else
102 #include <vararg.h>
103 #endif
104
105 static void warnx __P((const char *fmt, ...));
106
107 static void
108 #ifdef __STDC__
109 warnx(const char *fmt, ...)
110 #else
111 warnx(fmt, va_alist)
112 const char *fmt;
113 va_dcl
114 #endif
115 {
116
117 char buf[64];
118 va_list ap;
119
120 #ifdef __STDC__
121 va_start(ap, fmt);
122 #else
123 va_start(ap);
124 #endif
125 vsprintf(buf, fmt, ap);
126 va_end(ap);
127
128 error(buf);
129 }
130 #endif /* SHELL */
131
132 #define PF(f, func) { \
133 if (fieldwidth) \
134 if (precision) \
135 (void)printf(f, fieldwidth, precision, func); \
136 else \
137 (void)printf(f, fieldwidth, func); \
138 else if (precision) \
139 (void)printf(f, precision, func); \
140 else \
141 (void)printf(f, func); \
142 }
143
144 int
145 #ifdef BUILTIN
146 progprintf(argc, argv)
147 #else
148 main(argc, argv)
149 #endif
150 int argc;
151 char *argv[];
152 {
153 char *fmt, *start;
154 int fieldwidth, precision;
155 char convch, nextch;
156 char *format;
157 int ch;
158
159 #if !defined(SHELL) && !defined(BUILTIN)
160 (void)setlocale (LC_ALL, "");
161 #endif
162
163 while ((ch = getopt(argc, argv, "")) != -1) {
164 switch (ch) {
165 case '?':
166 default:
167 usage();
168 return (1);
169 }
170 }
171 argc -= optind;
172 argv += optind;
173
174 if (argc < 1) {
175 usage();
176 return (1);
177 }
178
179 format = *argv;
180 gargv = ++argv;
181
182 #define SKIP1 "#-+ 0"
183 #define SKIP2 "*0123456789"
184 do {
185 /*
186 * Basic algorithm is to scan the format string for conversion
187 * specifications -- once one is found, find out if the field
188 * width or precision is a '*'; if it is, gather up value.
189 * Note, format strings are reused as necessary to use up the
190 * provided arguments, arguments of zero/null string are
191 * provided to use up the format string.
192 */
193
194 /* find next format specification */
195 for (fmt = format; *fmt; fmt++) {
196 switch (*fmt) {
197 case '%':
198 start = fmt++;
199
200 if (*fmt == '%') {
201 (void)putchar('%');
202 break;
203 } else if (*fmt == 'b') {
204 char *p = getstr();
205 if (print_escape_str(p)) {
206 return (rval);
207 }
208 break;
209 }
210
211 /* skip to field width */
212 for (; strchr(SKIP1, *fmt); ++fmt) ;
213 fieldwidth = *fmt == '*' ? getint() : 0;
214
215 /* skip to possible '.', get following precision */
216 for (; strchr(SKIP2, *fmt); ++fmt) ;
217 if (*fmt == '.')
218 ++fmt;
219 precision = *fmt == '*' ? getint() : 0;
220
221 for (; strchr(SKIP2, *fmt); ++fmt) ;
222 if (!*fmt) {
223 warnx ("missing format character");
224 return(1);
225 }
226
227 convch = *fmt;
228 nextch = *(fmt + 1);
229 *(fmt + 1) = '\0';
230 switch(convch) {
231 case 'c': {
232 char p = getchr();
233 PF(start, p);
234 break;
235 }
236 case 's': {
237 char *p = getstr();
238 PF(start, p);
239 break;
240 }
241 case 'd':
242 case 'i': {
243 char *f = mklong(start, convch);
244 long p = getlong();
245 PF(f, p);
246 break;
247 }
248 case 'o':
249 case 'u':
250 case 'x':
251 case 'X': {
252 char *f = mklong(start, convch);
253 unsigned long p = getulong();
254 PF(f, p);
255 break;
256 }
257 case 'e':
258 case 'E':
259 case 'f':
260 case 'g':
261 case 'G': {
262 double p = getdouble();
263 PF(start, p);
264 break;
265 }
266 default:
267 warnx ("%s: invalid directive", start);
268 return(1);
269 }
270 *(fmt + 1) = nextch;
271 break;
272
273 case '\\':
274 fmt += print_escape(fmt);
275 break;
276
277 default:
278 (void)putchar(*fmt);
279 break;
280 }
281 }
282 } while (gargv > argv && *gargv);
283
284 return (rval);
285 }
286
287
288 /*
289 * Print SysV echo(1) style escape string
290 * Halts processing string and returns 1 if a \c escape is encountered.
291 */
292 static int
293 print_escape_str(str)
294 const char *str;
295 {
296 int value;
297 int c;
298
299 while (*str) {
300 if (*str == '\\') {
301 str++;
302 /*
303 * %b string octal constants are not like those in C.
304 * They start with a \0, and are followed by 0, 1, 2,
305 * or 3 octal digits.
306 */
307 if (*str == '0') {
308 str++;
309 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
310 value <<= 3;
311 value += octtobin(*str);
312 }
313 (void)putchar(value);
314 str--;
315 } else if (*str == 'c') {
316 return 1;
317 } else {
318 str--;
319 str += print_escape(str);
320 }
321 } else {
322 (void)putchar(*str);
323 }
324 str++;
325 }
326
327 return 0;
328 }
329
330 /*
331 * Print "standard" escape characters
332 */
333 static size_t
334 print_escape(str)
335 const char *str;
336 {
337 const char *start = str;
338 int value;
339 int c;
340
341 str++;
342
343 switch (*str) {
344 case '0': case '1': case '2': case '3':
345 case '4': case '5': case '6': case '7':
346 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
347 value <<= 3;
348 value += octtobin(*str);
349 }
350 (void)putchar(value);
351 return str - start - 1;
352 /* NOTREACHED */
353
354 case 'x':
355 str++;
356 for (value = 0; isxdigit(*str); str++) {
357 value <<= 4;
358 value += hextobin(*str);
359 }
360 if (value > UCHAR_MAX) {
361 warnx ("escape sequence out of range for character");
362 rval = 1;
363 }
364 (void)putchar (value);
365 return str - start - 1;
366 /* NOTREACHED */
367
368 case '\\': /* backslash */
369 (void)putchar('\\');
370 break;
371
372 case '\'': /* single quote */
373 (void)putchar('\'');
374 break;
375
376 case '"': /* double quote */
377 (void)putchar('"');
378 break;
379
380 case 'a': /* alert */
381 #ifdef __STDC__
382 (void)putchar('\a');
383 #else
384 (void)putchar(007);
385 #endif
386 break;
387
388 case 'b': /* backspace */
389 (void)putchar('\b');
390 break;
391
392 case 'e': /* escape */
393 #ifdef __GNUC__
394 (void)putchar('\e');
395 #else
396 (void)putchar(033);
397 #endif
398 break;
399
400 case 'f': /* form-feed */
401 (void)putchar('\f');
402 break;
403
404 case 'n': /* newline */
405 (void)putchar('\n');
406 break;
407
408 case 'r': /* carriage-return */
409 (void)putchar('\r');
410 break;
411
412 case 't': /* tab */
413 (void)putchar('\t');
414 break;
415
416 case 'v': /* vertical-tab */
417 (void)putchar('\v');
418 break;
419
420 default:
421 (void)putchar(*str);
422 warnx("unknown escape sequence `\\%c'", *str);
423 rval = 1;
424 break;
425 }
426
427 return 1;
428 }
429
430 static char *
431 mklong(str, ch)
432 const char *str;
433 char ch;
434 {
435 static char copy[64];
436 size_t len;
437
438 len = strlen(str) + 2;
439 (void)memmove(copy, str, len - 3);
440 copy[len - 3] = 'l';
441 copy[len - 2] = ch;
442 copy[len - 1] = '\0';
443 return (copy);
444 }
445
446 static int
447 getchr()
448 {
449 if (!*gargv)
450 return ('\0');
451 return ((int)**gargv++);
452 }
453
454 static char *
455 getstr()
456 {
457 if (!*gargv)
458 return ("");
459 return (*gargv++);
460 }
461
462 static char *Number = "+-.0123456789";
463 static int
464 getint()
465 {
466 if (!*gargv)
467 return(0);
468
469 if (strchr(Number, **gargv))
470 return(atoi(*gargv++));
471
472 return 0;
473 }
474
475 static long
476 getlong()
477 {
478 long val;
479 char *ep;
480
481 if (!*gargv)
482 return(0L);
483
484 if (**gargv == '\"' || **gargv == '\'')
485 return (long) *((*gargv++)+1);
486
487 errno = 0;
488 val = strtol (*gargv, &ep, 0);
489 check_conversion(*gargv++, ep);
490 return val;
491 }
492
493 static unsigned long
494 getulong()
495 {
496 unsigned long val;
497 char *ep;
498
499 if (!*gargv)
500 return(0UL);
501
502 if (**gargv == '\"' || **gargv == '\'')
503 return (unsigned long) *((*gargv++)+1);
504
505 errno = 0;
506 val = strtoul (*gargv, &ep, 0);
507 check_conversion(*gargv++, ep);
508 return val;
509 }
510
511 static double
512 getdouble()
513 {
514 double val;
515 char *ep;
516
517 if (!*gargv)
518 return(0.0);
519
520 if (**gargv == '\"' || **gargv == '\'')
521 return (double) *((*gargv++)+1);
522
523 errno = 0;
524 val = strtod (*gargv, &ep);
525 check_conversion(*gargv++, ep);
526 return val;
527 }
528
529 static void
530 check_conversion(s, ep)
531 const char *s;
532 const char *ep;
533 {
534 if (*ep) {
535 if (ep == s)
536 warnx ("%s: expected numeric value", s);
537 else
538 warnx ("%s: not completely converted", s);
539 rval = 1;
540 } else if (errno == ERANGE) {
541 warnx ("%s: %s", s, strerror(ERANGE));
542 rval = 1;
543 }
544 }
545
546 static void
547 usage()
548 {
549 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
550 }