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