Annotation of src/usr.bin/printf/printf.c, Revision 1.5
1.5 ! mpech 1: /* $OpenBSD: printf.c,v 1.4 2000/12/22 22:53:10 deraadt Exp $ */
1.2 deraadt 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.5 ! mpech 46: static char rcsid[] = "$OpenBSD: printf.c,v 1.4 2000/12/22 22:53:10 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: {
1.5 ! mpech 134: char *fmt, *start;
! 135: int fieldwidth, precision;
1.1 deraadt 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 */
1.3 millert 193: for (; strchr(SKIP1, *fmt); ++fmt) ;
1.1 deraadt 194: fieldwidth = *fmt == '*' ? getint() : 0;
195:
196: /* skip to possible '.', get following precision */
1.3 millert 197: for (; strchr(SKIP2, *fmt); ++fmt) ;
1.1 deraadt 198: if (*fmt == '.')
199: ++fmt;
200: precision = *fmt == '*' ? getint() : 0;
201:
1.3 millert 202: for (; strchr(SKIP2, *fmt); ++fmt) ;
1.1 deraadt 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': {
1.4 deraadt 224: long p;
1.1 deraadt 225: char *f = mklong(start, convch);
1.4 deraadt 226: if (!f) {
227: warnx("out of memory");
228: return (1);
229: }
230: p = getlong();
1.1 deraadt 231: PF(f, p);
232: break;
233: }
234: case 'o':
235: case 'u':
236: case 'x':
237: case 'X': {
1.4 deraadt 238: unsigned long p;
1.1 deraadt 239: char *f = mklong(start, convch);
1.4 deraadt 240: if (!f) {
241: warnx("out of memory");
242: return (1);
243: }
244: p = getulong();
1.1 deraadt 245: PF(f, p);
246: break;
247: }
248: case 'e':
249: case 'E':
250: case 'f':
251: case 'g':
252: case 'G': {
253: double p = getdouble();
254: PF(start, p);
255: break;
256: }
257: default:
258: warnx ("%s: invalid directive", start);
259: return(1);
260: }
261: *(fmt + 1) = nextch;
262: break;
263:
264: case '\\':
265: fmt += print_escape(fmt);
266: break;
267:
268: default:
269: putchar (*fmt);
270: break;
271: }
272: }
273: } while (gargv > argv && *gargv);
274:
275: return (rval);
276: }
277:
278:
279: /*
280: * Print SysV echo(1) style escape string
281: * Halts processing string and returns 1 if a \c escape is encountered.
282: */
283: static int
284: print_escape_str(str)
1.5 ! mpech 285: const char *str;
1.1 deraadt 286: {
287: int value;
288: int c;
289:
290: while (*str) {
291: if (*str == '\\') {
292: str++;
293: /*
294: * %b string octal constants are not like those in C.
295: * They start with a \0, and are followed by 0, 1, 2,
296: * or 3 octal digits.
297: */
298: if (*str == '0') {
299: str++;
300: for (c = 3, value = 0; c-- && isodigit(*str); str++) {
301: value <<= 3;
302: value += octtobin(*str);
303: }
304: putchar (value);
305: str--;
306: } else if (*str == 'c') {
307: return 1;
308: } else {
309: str--;
310: str += print_escape(str);
311: }
312: } else {
313: putchar (*str);
314: }
315: str++;
316: }
317:
318: return 0;
319: }
320:
321: /*
322: * Print "standard" escape characters
323: */
324: static int
325: print_escape(str)
1.5 ! mpech 326: const char *str;
1.1 deraadt 327: {
328: const char *start = str;
329: int value;
330: int c;
331:
332: str++;
333:
334: switch (*str) {
335: case '0': case '1': case '2': case '3':
336: case '4': case '5': case '6': case '7':
337: for (c = 3, value = 0; c-- && isodigit(*str); str++) {
338: value <<= 3;
339: value += octtobin(*str);
340: }
341: putchar(value);
342: return str - start - 1;
343: /* NOTREACHED */
344:
345: case 'x':
346: str++;
347: for (value = 0; isxdigit(*str); str++) {
348: value <<= 4;
349: value += hextobin(*str);
350: }
351: if (value > UCHAR_MAX) {
352: warnx ("escape sequence out of range for character");
353: rval = 1;
354: }
355: putchar (value);
356: return str - start - 1;
357: /* NOTREACHED */
358:
359: case '\\': /* backslash */
360: putchar('\\');
361: break;
362:
363: case '\'': /* single quote */
364: putchar('\'');
365: break;
366:
367: case '"': /* double quote */
368: putchar('"');
369: break;
370:
371: case 'a': /* alert */
372: #ifdef __STDC__
373: putchar('\a');
374: #else
375: putchar(007);
376: #endif
377: break;
378:
379: case 'b': /* backspace */
380: putchar('\b');
381: break;
382:
383: case 'e': /* escape */
384: #ifdef __GNUC__
385: putchar('\e');
386: #else
387: putchar(033);
388: #endif
389: break;
390:
391: case 'f': /* form-feed */
392: putchar('\f');
393: break;
394:
395: case 'n': /* newline */
396: putchar('\n');
397: break;
398:
399: case 'r': /* carriage-return */
400: putchar('\r');
401: break;
402:
403: case 't': /* tab */
404: putchar('\t');
405: break;
406:
407: case 'v': /* vertical-tab */
408: putchar('\v');
409: break;
410:
411: default:
412: putchar(*str);
413: warnx("unknown escape sequence `\\%c'", *str);
414: rval = 1;
415: }
416:
417: return 1;
418: }
419:
420: static char *
421: mklong(str, ch)
422: const char *str;
423: char ch;
424: {
1.4 deraadt 425: static char *copy;
426: static int copysize;
1.1 deraadt 427: int len;
428:
429: len = strlen(str) + 2;
1.4 deraadt 430: if (copysize < len) {
431: char *newcopy;
432: copysize = len + 256;
433:
434: newcopy = realloc(copy, copysize);
435: if (newcopy == NULL) {
436: copysize = 0;
437: free(copy);
438: copy = NULL;
439: return (NULL);
440: }
441: copy = newcopy;
442: }
1.1 deraadt 443: (void) memmove(copy, str, len - 3);
444: copy[len - 3] = 'l';
445: copy[len - 2] = ch;
446: copy[len - 1] = '\0';
447: return (copy);
448: }
449:
450: static int
451: getchr()
452: {
453: if (!*gargv)
454: return((int)'\0');
455: return((int)**gargv++);
456: }
457:
458: static char *
459: getstr()
460: {
461: if (!*gargv)
462: return("");
463: return(*gargv++);
464: }
465:
466: static char *number = "+-.0123456789";
467: static int
468: getint()
469: {
470: if (!*gargv)
471: return(0);
472:
1.3 millert 473: if (strchr(number, **gargv))
1.1 deraadt 474: return(atoi(*gargv++));
475:
476: return 0;
477: }
478:
479: static long
480: getlong()
481: {
482: long val;
483: char *ep;
484:
485: if (!*gargv)
486: return(0L);
487:
488: if (**gargv == '\"' || **gargv == '\'')
489: return (long) *((*gargv++)+1);
490:
491: errno = 0;
492: val = strtol (*gargv, &ep, 0);
493: check_conversion(*gargv++, ep);
494: return val;
495: }
496:
497: static unsigned long
498: getulong()
499: {
500: unsigned long val;
501: char *ep;
502:
503: if (!*gargv)
504: return(0UL);
505:
506: if (**gargv == '\"' || **gargv == '\'')
507: return (unsigned long) *((*gargv++)+1);
508:
509: errno = 0;
510: val = strtoul (*gargv, &ep, 0);
511: check_conversion(*gargv++, ep);
512: return val;
513: }
514:
515: static double
516: getdouble()
517: {
518: double val;
519: char *ep;
520:
521: if (!*gargv)
522: return(0.0);
523:
524: if (**gargv == '\"' || **gargv == '\'')
525: return (double) *((*gargv++)+1);
526:
527: errno = 0;
528: val = strtod (*gargv, &ep);
529: check_conversion(*gargv++, ep);
530: return val;
531: }
532:
533: static void
534: check_conversion(s, ep)
535: const char *s;
536: const char *ep;
537: {
538: if (*ep) {
539: if (ep == s)
540: warnx ("%s: expected numeric value", s);
541: else
542: warnx ("%s: not completely converted", s);
543: rval = 1;
544: } else if (errno == ERANGE) {
545: warnx ("%s: %s", s, strerror(ERANGE));
546: rval = 1;
547: }
548: }
549:
550: static void
551: usage()
552: {
553: (void)fprintf(stderr, "usage: printf format [arg ...]\n");
554: }