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