printf.c revision 1.7 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.7 1993/11/19 21:05:37 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;
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 free(f);
228 break;
229 }
230 case 'e':
231 case 'E':
232 case 'f':
233 case 'g':
234 case 'G': {
235 double p = getdouble();
236 PF(start, p);
237 break;
238 }
239 default:
240 warnx ("%s: invalid directive", start);
241 rval = 1;
242 }
243 *(fmt + 1) = nextch;
244 break;
245
246 case '\\':
247 fmt += print_escape(fmt);
248 break;
249
250 default:
251 putchar (*fmt);
252 break;
253 }
254 }
255 } while (gargv > argv && *gargv);
256
257 return (rval);
258 }
259
260
261 /*
262 * Print SysV echo(1) style escape string
263 * Halts processing string and returns 1 if a \c escape is encountered.
264 */
265 static int
266 print_escape_str(str)
267 register const char *str;
268 {
269 int value;
270 int c;
271
272 while (*str) {
273 if (*str == '\\') {
274 str++;
275 /*
276 * %b string octal constants are not like those in C.
277 * They start with a \0, and are followed by 0, 1, 2,
278 * or 3 octal digits.
279 */
280 if (*str == '0') {
281 str++;
282 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
283 value <<= 3;
284 value += octtobin(*str);
285 }
286 putchar (value);
287 str--;
288 } else if (*str == 'c') {
289 return 1;
290 } else {
291 str--;
292 str += print_escape(str);
293 }
294 } else {
295 putchar (*str);
296 }
297 str++;
298 }
299
300 return 0;
301 }
302
303 /*
304 * Print "standard" escape characters
305 */
306 static int
307 print_escape(str)
308 register const char *str;
309 {
310 const char *start = str;
311 int c;
312 int value;
313
314 str++;
315
316 switch (*str) {
317 case '0': case '1': case '2': case '3':
318 case '4': case '5': case '6': case '7':
319 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
320 value <<= 3;
321 value += octtobin(*str);
322 }
323 putchar(value);
324 return str - start - 1;
325 /* NOTREACHED */
326
327 case 'x':
328 str++;
329 for (value = 0; isxdigit(*str); str++) {
330 value <<= 4;
331 value += hextobin(*str);
332 }
333 if (value > UCHAR_MAX) {
334 warnx ("escape sequence out of range for character");
335 rval = 1;
336 }
337 putchar (value);
338 return str - start - 1;
339 /* NOTREACHED */
340
341 case '\\': /* backslash */
342 putchar('\\');
343 break;
344
345 case '\'': /* single quote */
346 putchar('\'');
347 break;
348
349 case '"': /* double quote */
350 putchar('"');
351 break;
352
353 case 'a': /* alert */
354 #ifdef __STDC__
355 putchar('\a');
356 #else
357 putchar(007);
358 #endif
359 break;
360
361 case 'b': /* backspace */
362 putchar('\b');
363 break;
364
365 case 'e': /* escape */
366 #ifdef __GNUC__
367 putchar('\e');
368 #else
369 putchar(033);
370 #endif
371 break;
372
373 case 'f': /* form-feed */
374 putchar('\f');
375 break;
376
377 case 'n': /* newline */
378 putchar('\n');
379 break;
380
381 case 'r': /* carriage-return */
382 putchar('\r');
383 break;
384
385 case 't': /* tab */
386 putchar('\t');
387 break;
388
389 case 'v': /* vertical-tab */
390 putchar('\v');
391 break;
392
393 default:
394 putchar(*str);
395 warnx("unknown escape sequence `\\%c'", *str);
396 rval = 1;
397 }
398
399 return 1;
400 }
401
402 static char *
403 mklong(str, ch)
404 char *str, ch;
405 {
406 static char copy[64];
407 int len;
408
409 len = strlen(str) + 2;
410 memmove(copy, str, len - 3);
411 copy[len - 3] = 'l';
412 copy[len - 2] = ch;
413 copy[len - 1] = '\0';
414 return (copy);
415 }
416
417 static int
418 getchr()
419 {
420 if (!*gargv)
421 return((int)'\0');
422 return((int)**gargv++);
423 }
424
425 static char *
426 getstr()
427 {
428 if (!*gargv)
429 return("");
430 return(*gargv++);
431 }
432
433 static char *number = "+-.0123456789";
434 static int
435 getint()
436 {
437 if (!*gargv)
438 return(0);
439
440 if (index(number, **gargv))
441 return(atoi(*gargv++));
442
443 return 0;
444 }
445
446 static long
447 getlong()
448 {
449 long val;
450 char *ep;
451
452 if (!*gargv)
453 return(0L);
454
455 if (**gargv == '\"' || **gargv == '\'') {
456 val = gargv[0][1];
457 gargv++;
458 return val;
459 }
460
461 val = strtol (*gargv, &ep, 0);
462 if (*ep) {
463 warnx ("incompletely converted argument: %s", *gargv);
464 rval = 1;
465 }
466
467 gargv++;
468 return val;
469 }
470
471 static double
472 getdouble()
473 {
474 double val;
475 char *ep;
476
477 if (!*gargv)
478 return(0.0);
479
480 if (**gargv == '\"' || **gargv == '\'') {
481 val = gargv[0][1];
482 gargv++;
483 return val;
484 }
485
486 val = strtod (*gargv, &ep);
487 if (*ep) {
488 warnx ("incompletely converted argument: %s", *gargv);
489 rval = 1;
490 }
491
492 gargv++;
493 return val;
494 }
495
496 static void
497 usage()
498 {
499 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
500 }
501