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