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