printf.c revision 1.6 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.6 1993/11/19 20:50:27 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 void print_escape_str();
56 static int print_escape();
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 static char *skip1, *skip2;
130 register char *fmt, *start;
131 register int fieldwidth, precision;
132 char convch, nextch;
133 char *format;
134 int ch;
135
136 #if !defined(SHELL) && !defined(BUILTIN)
137 setlocale (LC_ALL, "");
138 #endif
139
140 while ((ch = getopt(argc, argv, "")) != -1) {
141 switch (ch) {
142 case '?':
143 default:
144 usage();
145 return (1);
146 }
147 }
148 argc -= optind;
149 argv += optind;
150
151 if (argc < 1) {
152 usage();
153 return (1);
154 }
155
156 format = *argv;
157 gargv = ++argv;
158
159 do {
160 /*
161 * Basic algorithm is to scan the format string for conversion
162 * specifications -- once one is found, find out if the field
163 * width or precision is a '*'; if it is, gather up value.
164 * Note, format strings are reused as necessary to use up the
165 * provided arguments, arguments of zero/null string are
166 * provided to use up the format string.
167 */
168 skip1 = "#-+ 0";
169 skip2 = "*0123456789";
170
171 /* find next format specification */
172 for (fmt = format; *fmt; fmt++) {
173 switch (*fmt) {
174 case '%':
175 start = fmt++;
176
177 if (*fmt == '%') {
178 putchar ('%');
179 break;
180 } else if (*fmt == 'b') {
181 char *p = getstr();
182 print_escape_str(p);
183 break;
184 }
185
186 /* skip to field width */
187 for (; index(skip1, *fmt); ++fmt);
188 fieldwidth = *fmt == '*' ? getint() : 0;
189
190 /* skip to possible '.', get following precision */
191 for (; index(skip2, *fmt); ++fmt);
192 if (*fmt == '.')
193 ++fmt;
194 precision = *fmt == '*' ? getint() : 0;
195
196 for (; index(skip2, *fmt); ++fmt);
197 if (!*fmt) {
198 warnx ("missing format character");
199 rval = 1;
200 return;
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': case 'i': case 'o': case 'u': case 'x': case 'X': {
218 char *f = mklong(start, convch);
219 long p = getlong();
220 PF(f, p);
221 free(f);
222 break;
223 }
224 case 'e': case 'E': case 'f': case 'g': case 'G': {
225 double p = getdouble();
226 PF(start, p);
227 break;
228 }
229 default:
230 warnx ("%s: invalid directive", start);
231 rval = 1;
232 }
233 *(fmt + 1) = nextch;
234 break;
235
236 case '\\':
237 fmt += print_escape(fmt);
238 break;
239
240 default:
241 putchar (*fmt);
242 break;
243 }
244 }
245 } while (gargv > argv && *gargv);
246
247 return (rval);
248 }
249
250
251 /*
252 * Print SysV echo(1) style escape string
253 */
254 static void
255 print_escape_str(str)
256 register char *str;
257 {
258 int value;
259 int c;
260
261 while (*str) {
262 if (*str == '\\') {
263 str++;
264 /*
265 * %b string octal constants are not like those in C.
266 * They start with a \0, and are followed by 0, 1, 2,
267 * or 3 octal digits.
268 */
269 if (*str == '0') {
270 str++;
271 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
272 value <<= 3;
273 value += octtobin(*str);
274 }
275 putchar (value);
276 str--;
277 } else if (*str == 'c') {
278 exit (rval);
279 } else {
280 str--;
281 str += print_escape(str);
282 }
283 } else {
284 putchar (*str);
285 }
286 str++;
287 }
288 }
289
290 /*
291 * Print "standard" escape characters
292 */
293 static int
294 print_escape(str)
295 register char *str;
296 {
297 char *start = str;
298 int c;
299 int value;
300
301 str++;
302
303 switch (*str) {
304 case '0': case '1': case '2': case '3':
305 case '4': case '5': case '6': case '7':
306 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
307 value <<= 3;
308 value += octtobin(*str);
309 }
310 putchar(value);
311 return str - start - 1;
312 /* NOTREACHED */
313
314 case 'x':
315 str++;
316 for (value = 0; isxdigit(*str); str++) {
317 value <<= 4;
318 value += hextobin(*str);
319 }
320 if (value > UCHAR_MAX) {
321 warnx ("escape sequence out of range for character");
322 rval = 1;
323 }
324 putchar (value);
325 return str - start - 1;
326 /* NOTREACHED */
327
328 case '\\': /* backslash */
329 putchar('\\');
330 break;
331
332 case '\'': /* single quote */
333 putchar('\'');
334 break;
335
336 case '"': /* double quote */
337 putchar('"');
338 break;
339
340 case 'a': /* alert */
341 #ifdef __STDC__
342 putchar('\a');
343 #else
344 putchar(007);
345 #endif
346 break;
347
348 case 'b': /* backspace */
349 putchar('\b');
350 break;
351
352 case 'e': /* escape */
353 #ifdef __GNUC__
354 putchar('\e');
355 #else
356 putchar(033);
357 #endif
358 break;
359
360 case 'f': /* form-feed */
361 putchar('\f');
362 break;
363
364 case 'n': /* newline */
365 putchar('\n');
366 break;
367
368 case 'r': /* carriage-return */
369 putchar('\r');
370 break;
371
372 case 't': /* tab */
373 putchar('\t');
374 break;
375
376 case 'v': /* vertical-tab */
377 putchar('\v');
378 break;
379
380 default:
381 putchar(*str);
382 warnx("unknown escape sequence `\\%c'", *str);
383 rval = 1;
384 }
385
386 return 1;
387 }
388
389 static char *
390 mklong(str, ch)
391 char *str, ch;
392 {
393 static char copy[64];
394 int len;
395
396 len = strlen(str) + 2;
397 memmove(copy, str, len - 3);
398 copy[len - 3] = 'l';
399 copy[len - 2] = ch;
400 copy[len - 1] = '\0';
401 return (copy);
402 }
403
404 static int
405 getchr()
406 {
407 if (!*gargv)
408 return((int)'\0');
409 return((int)**gargv++);
410 }
411
412 static char *
413 getstr()
414 {
415 if (!*gargv)
416 return("");
417 return(*gargv++);
418 }
419
420 static char *number = "+-.0123456789";
421 static int
422 getint()
423 {
424 if (!*gargv)
425 return(0);
426
427 if (index(number, **gargv))
428 return(atoi(*gargv++));
429
430 return 0;
431 }
432
433 static long
434 getlong()
435 {
436 long val;
437 char *ep;
438
439 if (!*gargv)
440 return(0L);
441
442 if (**gargv == '\"' || **gargv == '\'') {
443 val = gargv[0][1];
444 gargv++;
445 return val;
446 }
447
448 val = strtol (*gargv, &ep, 0);
449 if (*ep) {
450 warnx ("incompletely converted argument: %s", *gargv);
451 rval = 1;
452 }
453
454 gargv++;
455 return val;
456 }
457
458 static double
459 getdouble()
460 {
461 double val;
462 char *ep;
463
464 if (!*gargv)
465 return(0.0);
466
467 if (**gargv == '\"' || **gargv == '\'') {
468 val = gargv[0][1];
469 gargv++;
470 return val;
471 }
472
473 val = strtod (*gargv, &ep);
474 if (*ep) {
475 warnx ("incompletely converted argument: %s", *gargv);
476 rval = 1;
477 }
478
479 gargv++;
480 return val;
481 }
482
483 static void
484 usage()
485 {
486 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
487 }
488