Annotation of src/usr.bin/printf/printf.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD$ */
! 2:
1.1 deraadt 3: /*
4: * Copyright (c) 1989 The Regents of the University of California.
5: * 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: #ifndef lint
37: #if !defined(SHELL) && !defined(BUILTIN)
38: char copyright[] =
39: "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
40: All rights reserved.\n";
41: #endif
42: #endif /* not lint */
43:
44: #ifndef lint
45: /*static char sccsid[] = "from: @(#)printf.c 5.9 (Berkeley) 6/1/90";*/
1.2 ! deraadt 46: static char rcsid[] = "$OpenBSD: printf.c,v 1.1.1.1 1995/10/18 08:45:57 deraadt Exp $";
1.1 deraadt 47: #endif /* not lint */
48:
49: #include <ctype.h>
50: #include <stdio.h>
51: #include <stdlib.h>
52: #include <string.h>
53: #include <limits.h>
54: #include <locale.h>
55: #include <errno.h>
56: #include <err.h>
57:
58: static int print_escape_str __P((const char *));
59: static int print_escape __P((const char *));
60:
61: static int getchr __P((void));
62: static double getdouble __P((void));
63: static int getint __P((void));
64: static long getlong __P((void));
65: static unsigned long getulong __P ((void));
66: static char *getstr __P((void));
67: static char *mklong __P((const char *, int));
68: static void check_conversion __P((const char *, const char *));
69: static void usage __P((void));
70:
71: static int rval;
72: static char **gargv;
73:
74: #define isodigit(c) ((c) >= '0' && (c) <= '7')
75: #define octtobin(c) ((c) - '0')
76: #define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
77:
78: #ifdef SHELL
79: #define main printfcmd
80: #include "../../bin/sh/bltin/bltin.h"
81:
82: #ifdef __STDC__
83: #include <stdarg.h>
84: #else
85: #include <vararg.h>
86: #endif
87:
88: static void
89: #ifdef __STDC__
90: warnx(const char *fmt, ...)
91: #else
92: warnx(fmt, va_alist)
93: const char *fmt;
94: va_dcl
95: #endif
96: {
97:
98: char buf[64];
99: va_list ap;
100:
101: #ifdef __STDC__
102: va_start(ap, fmt);
103: #else
104: va_start(ap);
105: #endif
106: vsprintf(buf, fmt, ap);
107: va_end(ap);
108:
109: error(buf);
110: }
111: #endif /* SHELL */
112:
113: #define PF(f, func) { \
114: if (fieldwidth) \
115: if (precision) \
116: (void)printf(f, fieldwidth, precision, func); \
117: else \
118: (void)printf(f, fieldwidth, func); \
119: else if (precision) \
120: (void)printf(f, precision, func); \
121: else \
122: (void)printf(f, func); \
123: }
124:
125: int
126: #ifdef BUILTIN
127: progprintf(argc, argv)
128: #else
129: main(argc, argv)
130: #endif
131: int argc;
132: char **argv;
133: {
134: register char *fmt, *start;
135: register int fieldwidth, precision;
136: char convch, nextch;
137: char *format;
138: int ch;
139:
140: #if !defined(SHELL) && !defined(BUILTIN)
141: setlocale (LC_ALL, "");
142: #endif
143:
144: while ((ch = getopt(argc, argv, "")) != -1) {
145: switch (ch) {
146: case '?':
147: default:
148: usage();
149: return (1);
150: }
151: }
152: argc -= optind;
153: argv += optind;
154:
155: if (argc < 1) {
156: usage();
157: return (1);
158: }
159:
160: format = *argv;
161: gargv = ++argv;
162:
163: #define SKIP1 "#-+ 0"
164: #define SKIP2 "*0123456789"
165: do {
166: /*
167: * Basic algorithm is to scan the format string for conversion
168: * specifications -- once one is found, find out if the field
169: * width or precision is a '*'; if it is, gather up value.
170: * Note, format strings are reused as necessary to use up the
171: * provided arguments, arguments of zero/null string are
172: * provided to use up the format string.
173: */
174:
175: /* find next format specification */
176: for (fmt = format; *fmt; fmt++) {
177: switch (*fmt) {
178: case '%':
179: start = fmt++;
180:
181: if (*fmt == '%') {
182: putchar ('%');
183: break;
184: } else if (*fmt == 'b') {
185: char *p = getstr();
186: if (print_escape_str(p)) {
187: return (rval);
188: }
189: break;
190: }
191:
192: /* skip to field width */
193: for (; index(SKIP1, *fmt); ++fmt) ;
194: fieldwidth = *fmt == '*' ? getint() : 0;
195:
196: /* skip to possible '.', get following precision */
197: for (; index(SKIP2, *fmt); ++fmt) ;
198: if (*fmt == '.')
199: ++fmt;
200: precision = *fmt == '*' ? getint() : 0;
201:
202: for (; index(SKIP2, *fmt); ++fmt) ;
203: if (!*fmt) {
204: warnx ("missing format character");
205: return(1);
206: }
207:
208: convch = *fmt;
209: nextch = *(fmt + 1);
210: *(fmt + 1) = '\0';
211: switch(convch) {
212: case 'c': {
213: char p = getchr();
214: PF(start, p);
215: break;
216: }
217: case 's': {
218: char *p = getstr();
219: PF(start, p);
220: break;
221: }
222: case 'd':
223: case 'i': {
224: char *f = mklong(start, convch);
225: long p = getlong();
226: PF(f, p);
227: break;
228: }
229: case 'o':
230: case 'u':
231: case 'x':
232: case 'X': {
233: char *f = mklong(start, convch);
234: unsigned long p = getulong();
235: PF(f, p);
236: break;
237: }
238: case 'e':
239: case 'E':
240: case 'f':
241: case 'g':
242: case 'G': {
243: double p = getdouble();
244: PF(start, p);
245: break;
246: }
247: default:
248: warnx ("%s: invalid directive", start);
249: return(1);
250: }
251: *(fmt + 1) = nextch;
252: break;
253:
254: case '\\':
255: fmt += print_escape(fmt);
256: break;
257:
258: default:
259: putchar (*fmt);
260: break;
261: }
262: }
263: } while (gargv > argv && *gargv);
264:
265: return (rval);
266: }
267:
268:
269: /*
270: * Print SysV echo(1) style escape string
271: * Halts processing string and returns 1 if a \c escape is encountered.
272: */
273: static int
274: print_escape_str(str)
275: register const char *str;
276: {
277: int value;
278: int c;
279:
280: while (*str) {
281: if (*str == '\\') {
282: str++;
283: /*
284: * %b string octal constants are not like those in C.
285: * They start with a \0, and are followed by 0, 1, 2,
286: * or 3 octal digits.
287: */
288: if (*str == '0') {
289: str++;
290: for (c = 3, value = 0; c-- && isodigit(*str); str++) {
291: value <<= 3;
292: value += octtobin(*str);
293: }
294: putchar (value);
295: str--;
296: } else if (*str == 'c') {
297: return 1;
298: } else {
299: str--;
300: str += print_escape(str);
301: }
302: } else {
303: putchar (*str);
304: }
305: str++;
306: }
307:
308: return 0;
309: }
310:
311: /*
312: * Print "standard" escape characters
313: */
314: static int
315: print_escape(str)
316: register const char *str;
317: {
318: const char *start = str;
319: int value;
320: int c;
321:
322: str++;
323:
324: switch (*str) {
325: case '0': case '1': case '2': case '3':
326: case '4': case '5': case '6': case '7':
327: for (c = 3, value = 0; c-- && isodigit(*str); str++) {
328: value <<= 3;
329: value += octtobin(*str);
330: }
331: putchar(value);
332: return str - start - 1;
333: /* NOTREACHED */
334:
335: case 'x':
336: str++;
337: for (value = 0; isxdigit(*str); str++) {
338: value <<= 4;
339: value += hextobin(*str);
340: }
341: if (value > UCHAR_MAX) {
342: warnx ("escape sequence out of range for character");
343: rval = 1;
344: }
345: putchar (value);
346: return str - start - 1;
347: /* NOTREACHED */
348:
349: case '\\': /* backslash */
350: putchar('\\');
351: break;
352:
353: case '\'': /* single quote */
354: putchar('\'');
355: break;
356:
357: case '"': /* double quote */
358: putchar('"');
359: break;
360:
361: case 'a': /* alert */
362: #ifdef __STDC__
363: putchar('\a');
364: #else
365: putchar(007);
366: #endif
367: break;
368:
369: case 'b': /* backspace */
370: putchar('\b');
371: break;
372:
373: case 'e': /* escape */
374: #ifdef __GNUC__
375: putchar('\e');
376: #else
377: putchar(033);
378: #endif
379: break;
380:
381: case 'f': /* form-feed */
382: putchar('\f');
383: break;
384:
385: case 'n': /* newline */
386: putchar('\n');
387: break;
388:
389: case 'r': /* carriage-return */
390: putchar('\r');
391: break;
392:
393: case 't': /* tab */
394: putchar('\t');
395: break;
396:
397: case 'v': /* vertical-tab */
398: putchar('\v');
399: break;
400:
401: default:
402: putchar(*str);
403: warnx("unknown escape sequence `\\%c'", *str);
404: rval = 1;
405: }
406:
407: return 1;
408: }
409:
410: static char *
411: mklong(str, ch)
412: const char *str;
413: char ch;
414: {
415: static char copy[64];
416: int len;
417:
418: len = strlen(str) + 2;
419: (void) memmove(copy, str, len - 3);
420: copy[len - 3] = 'l';
421: copy[len - 2] = ch;
422: copy[len - 1] = '\0';
423: return (copy);
424: }
425:
426: static int
427: getchr()
428: {
429: if (!*gargv)
430: return((int)'\0');
431: return((int)**gargv++);
432: }
433:
434: static char *
435: getstr()
436: {
437: if (!*gargv)
438: return("");
439: return(*gargv++);
440: }
441:
442: static char *number = "+-.0123456789";
443: static int
444: getint()
445: {
446: if (!*gargv)
447: return(0);
448:
449: if (index(number, **gargv))
450: return(atoi(*gargv++));
451:
452: return 0;
453: }
454:
455: static long
456: getlong()
457: {
458: long val;
459: char *ep;
460:
461: if (!*gargv)
462: return(0L);
463:
464: if (**gargv == '\"' || **gargv == '\'')
465: return (long) *((*gargv++)+1);
466:
467: errno = 0;
468: val = strtol (*gargv, &ep, 0);
469: check_conversion(*gargv++, ep);
470: return val;
471: }
472:
473: static unsigned long
474: getulong()
475: {
476: unsigned long val;
477: char *ep;
478:
479: if (!*gargv)
480: return(0UL);
481:
482: if (**gargv == '\"' || **gargv == '\'')
483: return (unsigned long) *((*gargv++)+1);
484:
485: errno = 0;
486: val = strtoul (*gargv, &ep, 0);
487: check_conversion(*gargv++, ep);
488: return val;
489: }
490:
491: static double
492: getdouble()
493: {
494: double val;
495: char *ep;
496:
497: if (!*gargv)
498: return(0.0);
499:
500: if (**gargv == '\"' || **gargv == '\'')
501: return (double) *((*gargv++)+1);
502:
503: errno = 0;
504: val = strtod (*gargv, &ep);
505: check_conversion(*gargv++, ep);
506: return val;
507: }
508:
509: static void
510: check_conversion(s, ep)
511: const char *s;
512: const char *ep;
513: {
514: if (*ep) {
515: if (ep == s)
516: warnx ("%s: expected numeric value", s);
517: else
518: warnx ("%s: not completely converted", s);
519: rval = 1;
520: } else if (errno == ERANGE) {
521: warnx ("%s: %s", s, strerror(ERANGE));
522: rval = 1;
523: }
524: }
525:
526: static void
527: usage()
528: {
529: (void)fprintf(stderr, "usage: printf format [arg ...]\n");
530: }