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