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