Annotation of src/usr.bin/printf/printf.c, Revision 1.27
1.27 ! martijn 1: /* $OpenBSD: printf.c,v 1.26 2016/11/18 15:53:16 schwarze 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;
1.27 ! martijn 278: int value = 0;
1.1 deraadt 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':
1.27 ! martijn 286: for (c = 3; c-- && isodigit(*str); str++) {
1.1 deraadt 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.27 ! martijn 296: for (c = 2; c-- && isxdigit((unsigned char)*str); str++) {
1.1 deraadt 297: value <<= 4;
298: value += hextobin(*str);
299: }
300: putchar (value);
301: return str - start - 1;
302: /* NOTREACHED */
303:
304: case '\\': /* backslash */
305: putchar('\\');
306: break;
307:
308: case '\'': /* single quote */
309: putchar('\'');
310: break;
311:
312: case '"': /* double quote */
313: putchar('"');
314: break;
315:
316: case 'a': /* alert */
317: putchar('\a');
318: break;
319:
320: case 'b': /* backspace */
321: putchar('\b');
322: break;
323:
324: case 'e': /* escape */
325: #ifdef __GNUC__
326: putchar('\e');
327: #else
328: putchar(033);
329: #endif
330: break;
331:
332: case 'f': /* form-feed */
333: putchar('\f');
334: break;
335:
336: case 'n': /* newline */
337: putchar('\n');
338: break;
339:
340: case 'r': /* carriage-return */
341: putchar('\r');
342: break;
343:
344: case 't': /* tab */
345: putchar('\t');
346: break;
347:
348: case 'v': /* vertical-tab */
349: putchar('\v');
350: break;
1.18 guenther 351:
352: case '\0':
353: warnx("null escape sequence");
354: rval = 1;
355: return 0;
1.1 deraadt 356:
357: default:
358: putchar(*str);
359: warnx("unknown escape sequence `\\%c'", *str);
360: rval = 1;
361: }
362:
363: return 1;
364: }
365:
366: static char *
1.10 deraadt 367: mklong(const char *str, int ch)
1.1 deraadt 368: {
1.4 deraadt 369: static char *copy;
370: static int copysize;
1.1 deraadt 371: int len;
372:
373: len = strlen(str) + 2;
1.4 deraadt 374: if (copysize < len) {
375: char *newcopy;
376: copysize = len + 256;
377:
378: newcopy = realloc(copy, copysize);
379: if (newcopy == NULL) {
380: copysize = 0;
381: free(copy);
382: copy = NULL;
383: return (NULL);
384: }
385: copy = newcopy;
386: }
1.1 deraadt 387: (void) memmove(copy, str, len - 3);
388: copy[len - 3] = 'l';
389: copy[len - 2] = ch;
390: copy[len - 1] = '\0';
391: return (copy);
392: }
393:
394: static int
1.10 deraadt 395: getchr(void)
1.1 deraadt 396: {
397: if (!*gargv)
398: return((int)'\0');
399: return((int)**gargv++);
400: }
401:
402: static char *
1.10 deraadt 403: getstr(void)
1.1 deraadt 404: {
405: if (!*gargv)
406: return("");
407: return(*gargv++);
408: }
409:
410: static char *number = "+-.0123456789";
411: static int
1.10 deraadt 412: getint(void)
1.1 deraadt 413: {
414: if (!*gargv)
415: return(0);
416:
1.3 millert 417: if (strchr(number, **gargv))
1.1 deraadt 418: return(atoi(*gargv++));
419:
420: return 0;
421: }
422:
423: static long
1.10 deraadt 424: getlong(void)
1.1 deraadt 425: {
426: long val;
427: char *ep;
428:
429: if (!*gargv)
430: return(0L);
431:
432: if (**gargv == '\"' || **gargv == '\'')
1.25 tedu 433: return (unsigned char) *((*gargv++)+1);
1.1 deraadt 434:
435: errno = 0;
436: val = strtol (*gargv, &ep, 0);
437: check_conversion(*gargv++, ep);
438: return val;
439: }
440:
441: static unsigned long
1.10 deraadt 442: getulong(void)
1.1 deraadt 443: {
444: unsigned long val;
445: char *ep;
446:
447: if (!*gargv)
448: return(0UL);
449:
450: if (**gargv == '\"' || **gargv == '\'')
1.25 tedu 451: return (unsigned char) *((*gargv++)+1);
1.1 deraadt 452:
453: errno = 0;
454: val = strtoul (*gargv, &ep, 0);
455: check_conversion(*gargv++, ep);
456: return val;
457: }
458:
459: static double
1.10 deraadt 460: getdouble(void)
1.1 deraadt 461: {
462: double val;
463: char *ep;
464:
465: if (!*gargv)
466: return(0.0);
467:
468: if (**gargv == '\"' || **gargv == '\'')
1.25 tedu 469: return (unsigned char) *((*gargv++)+1);
1.1 deraadt 470:
471: errno = 0;
472: val = strtod (*gargv, &ep);
473: check_conversion(*gargv++, ep);
474: return val;
475: }
476:
477: static void
1.10 deraadt 478: check_conversion(const char *s, const char *ep)
1.1 deraadt 479: {
480: if (*ep) {
481: if (ep == s)
482: warnx ("%s: expected numeric value", s);
483: else
484: warnx ("%s: not completely converted", s);
485: rval = 1;
486: } else if (errno == ERANGE) {
1.21 guenther 487: warnc(ERANGE, "%s", s);
1.1 deraadt 488: rval = 1;
489: }
490: }
491:
1.26 schwarze 492: static void __dead
1.10 deraadt 493: usage(void)
1.1 deraadt 494: {
1.22 jmc 495: (void)fprintf(stderr, "usage: printf format [argument ...]\n");
1.26 schwarze 496: exit(1);
1.1 deraadt 497: }