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