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