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