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