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