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