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