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