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