xref: /netbsd-src/usr.bin/printf/printf.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: printf.c,v 1.21 1998/12/19 20:21:44 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if !defined(BUILTIN) && !defined(SHELL)
39 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n");
41 #endif
42 #endif
43 
44 #ifndef lint
45 #if 0
46 static char sccsid[] = "@(#)printf.c	8.2 (Berkeley) 3/22/95";
47 #else
48 __RCSID("$NetBSD: printf.c,v 1.21 1998/12/19 20:21:44 christos Exp $");
49 #endif
50 #endif /* not lint */
51 
52 #include <sys/types.h>
53 
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <limits.h>
58 #include <locale.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 static int	 print_escape_str __P((const char *));
66 static size_t	 print_escape __P((const char *));
67 
68 static int	 getchr __P((void));
69 static double	 getdouble __P((void));
70 static int	 getint __P((void));
71 static long	 getlong __P((void));
72 static unsigned long getulong __P ((void));
73 static char	*getstr __P((void));
74 static char	*mklong __P((const char *, int));
75 static void      check_conversion __P((const char *, const char *));
76 static void	 usage __P((void));
77 
78 static int	rval;
79 static char  **gargv;
80 
81 #ifdef BUILTIN
82 int progprintf __P((int, char **));
83 #else
84 int main __P((int, char **));
85 #endif
86 
87 #define isodigit(c)	((c) >= '0' && (c) <= '7')
88 #define octtobin(c)	((c) - '0')
89 #define hextobin(c)	((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
90 
91 #ifdef SHELL
92 #define main printfcmd
93 #include "../../bin/sh/bltin/bltin.h"
94 
95 #ifdef __STDC__
96 #include <stdarg.h>
97 #else
98 #include <vararg.h>
99 #endif
100 
101 static void warnx __P((const char *fmt, ...));
102 
103 static void
104 #ifdef __STDC__
105 warnx(const char *fmt, ...)
106 #else
107 warnx(fmt, va_alist)
108 	const char *fmt;
109 	va_dcl
110 #endif
111 {
112 
113 	char buf[64];
114 	va_list ap;
115 
116 #ifdef __STDC__
117 	va_start(ap, fmt);
118 #else
119 	va_start(ap);
120 #endif
121 	vsprintf(buf, fmt, ap);
122 	va_end(ap);
123 
124 	error(buf);
125 }
126 #endif /* SHELL */
127 
128 #define PF(f, func) { \
129 	if (fieldwidth) { \
130 		if (precision) \
131 			(void)printf(f, fieldwidth, precision, func); \
132 		else \
133 			(void)printf(f, fieldwidth, func); \
134 	} else if (precision) \
135 		(void)printf(f, precision, func); \
136 	else \
137 		(void)printf(f, func); \
138 }
139 
140 int
141 #ifdef BUILTIN
142 progprintf(argc, argv)
143 #else
144 main(argc, argv)
145 #endif
146 	int argc;
147 	char *argv[];
148 {
149 	char *fmt, *start;
150 	int fieldwidth, precision;
151 	char convch, nextch;
152 	char *format;
153 	int ch;
154 
155 #if !defined(SHELL) && !defined(BUILTIN)
156 	(void)setlocale (LC_ALL, "");
157 #endif
158 
159 	while ((ch = getopt(argc, argv, "")) != -1) {
160 		switch (ch) {
161 		case '?':
162 		default:
163 			usage();
164 			return (1);
165 		}
166 	}
167 	argc -= optind;
168 	argv += optind;
169 
170 	if (argc < 1) {
171 		usage();
172 		return (1);
173 	}
174 
175 	format = *argv;
176 	gargv = ++argv;
177 
178 #define SKIP1	"#-+ 0"
179 #define SKIP2	"*0123456789"
180 	do {
181 		/*
182 		 * Basic algorithm is to scan the format string for conversion
183 		 * specifications -- once one is found, find out if the field
184 		 * width or precision is a '*'; if it is, gather up value.
185 		 * Note, format strings are reused as necessary to use up the
186 		 * provided arguments, arguments of zero/null string are
187 		 * provided to use up the format string.
188 		 */
189 
190 		/* find next format specification */
191 		for (fmt = format; *fmt; fmt++) {
192 			switch (*fmt) {
193 			case '%':
194 				start = fmt++;
195 
196 				if (*fmt == '%') {
197 					(void)putchar('%');
198 					break;
199 				} else if (*fmt == 'b') {
200 					char *p = getstr();
201 					if (print_escape_str(p)) {
202 						return (rval);
203 					}
204 					break;
205 				}
206 
207 				/* skip to field width */
208 				for (; strchr(SKIP1, *fmt); ++fmt) ;
209 				fieldwidth = *fmt == '*' ? getint() : 0;
210 
211 				/* skip to possible '.', get following precision */
212 				for (; strchr(SKIP2, *fmt); ++fmt) ;
213 				if (*fmt == '.')
214 					++fmt;
215 				precision = *fmt == '*' ? getint() : 0;
216 
217 				for (; strchr(SKIP2, *fmt); ++fmt) ;
218 				if (!*fmt) {
219 					warnx ("missing format character");
220 					return(1);
221 				}
222 
223 				convch = *fmt;
224 				nextch = *(fmt + 1);
225 				*(fmt + 1) = '\0';
226 				switch(convch) {
227 				case 'c': {
228 					char p = getchr();
229 					PF(start, p);
230 					break;
231 				}
232 				case 's': {
233 					char *p = getstr();
234 					PF(start, p);
235 					break;
236 				}
237 				case 'd':
238 				case 'i': {
239 					char *f = mklong(start, convch);
240 					long p = getlong();
241 					PF(f, p);
242 					break;
243 				}
244 				case 'o':
245 				case 'u':
246 				case 'x':
247 				case 'X': {
248 					char *f = mklong(start, convch);
249 					unsigned long p = getulong();
250 					PF(f, p);
251 					break;
252 				}
253 				case 'e':
254 				case 'E':
255 				case 'f':
256 				case 'g':
257 				case 'G': {
258 					double p = getdouble();
259 					PF(start, p);
260 					break;
261 				}
262 				default:
263 					warnx ("%s: invalid directive", start);
264 					return(1);
265 				}
266 				*(fmt + 1) = nextch;
267 				break;
268 
269 			case '\\':
270 				fmt += print_escape(fmt);
271 				break;
272 
273 			default:
274 				(void)putchar(*fmt);
275 				break;
276 			}
277 		}
278 	} while (gargv > argv && *gargv);
279 
280 	return (rval);
281 }
282 
283 
284 /*
285  * Print SysV echo(1) style escape string
286  *	Halts processing string and returns 1 if a \c escape is encountered.
287  */
288 static int
289 print_escape_str(str)
290 	const char *str;
291 {
292 	int value;
293 	int c;
294 
295 	while (*str) {
296 		if (*str == '\\') {
297 			str++;
298 			/*
299 			 * %b string octal constants are not like those in C.
300 			 * They start with a \0, and are followed by 0, 1, 2,
301 			 * or 3 octal digits.
302 			 */
303 			if (*str == '0') {
304 				str++;
305 				for (c = 3, value = 0; c-- && isodigit(*str); str++) {
306 					value <<= 3;
307 					value += octtobin(*str);
308 				}
309 				(void)putchar(value);
310 				str--;
311 			} else if (*str == 'c') {
312 				return 1;
313 			} else {
314 				str--;
315 				str += print_escape(str);
316 			}
317 		} else {
318 			(void)putchar(*str);
319 		}
320 		str++;
321 	}
322 
323 	return 0;
324 }
325 
326 /*
327  * Print "standard" escape characters
328  */
329 static size_t
330 print_escape(str)
331 	const char *str;
332 {
333 	const char *start = str;
334 	int value;
335 	int c;
336 
337 	str++;
338 
339 	switch (*str) {
340 	case '0': case '1': case '2': case '3':
341 	case '4': case '5': case '6': case '7':
342 		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
343 			value <<= 3;
344 			value += octtobin(*str);
345 		}
346 		(void)putchar(value);
347 		return str - start - 1;
348 		/* NOTREACHED */
349 
350 	case 'x':
351 		str++;
352 		for (value = 0; isxdigit((unsigned char)*str); str++) {
353 			value <<= 4;
354 			value += hextobin(*str);
355 		}
356 		if (value > UCHAR_MAX) {
357 			warnx ("escape sequence out of range for character");
358 			rval = 1;
359 		}
360 		(void)putchar (value);
361 		return str - start - 1;
362 		/* NOTREACHED */
363 
364 	case '\\':			/* backslash */
365 		(void)putchar('\\');
366 		break;
367 
368 	case '\'':			/* single quote */
369 		(void)putchar('\'');
370 		break;
371 
372 	case '"':			/* double quote */
373 		(void)putchar('"');
374 		break;
375 
376 	case 'a':			/* alert */
377 #ifdef __STDC__
378 		(void)putchar('\a');
379 #else
380 		(void)putchar(007);
381 #endif
382 		break;
383 
384 	case 'b':			/* backspace */
385 		(void)putchar('\b');
386 		break;
387 
388 	case 'e':			/* escape */
389 #ifdef __GNUC__
390 		(void)putchar('\e');
391 #else
392 		(void)putchar(033);
393 #endif
394 		break;
395 
396 	case 'f':			/* form-feed */
397 		(void)putchar('\f');
398 		break;
399 
400 	case 'n':			/* newline */
401 		(void)putchar('\n');
402 		break;
403 
404 	case 'r':			/* carriage-return */
405 		(void)putchar('\r');
406 		break;
407 
408 	case 't':			/* tab */
409 		(void)putchar('\t');
410 		break;
411 
412 	case 'v':			/* vertical-tab */
413 		(void)putchar('\v');
414 		break;
415 
416 	default:
417 		(void)putchar(*str);
418 		warnx("unknown escape sequence `\\%c'", *str);
419 		rval = 1;
420 		break;
421 	}
422 
423 	return 1;
424 }
425 
426 static char *
427 mklong(str, ch)
428 	const char *str;
429 	char ch;
430 {
431 	static char copy[64];
432 	size_t len;
433 
434 	len = strlen(str) + 2;
435 	(void)memmove(copy, str, len - 3);
436 	copy[len - 3] = 'l';
437 	copy[len - 2] = ch;
438 	copy[len - 1] = '\0';
439 	return (copy);
440 }
441 
442 static int
443 getchr()
444 {
445 	if (!*gargv)
446 		return ('\0');
447 	return ((int)**gargv++);
448 }
449 
450 static char *
451 getstr()
452 {
453 	if (!*gargv)
454 		return ("");
455 	return (*gargv++);
456 }
457 
458 static char *Number = "+-.0123456789";
459 static int
460 getint()
461 {
462 	if (!*gargv)
463 		return(0);
464 
465 	if (strchr(Number, **gargv))
466 		return(atoi(*gargv++));
467 
468 	return 0;
469 }
470 
471 static long
472 getlong()
473 {
474 	long val;
475 	char *ep;
476 
477 	if (!*gargv)
478 		return(0L);
479 
480 	if (**gargv == '\"' || **gargv == '\'')
481 		return (long) *((*gargv++)+1);
482 
483 	errno = 0;
484 	val = strtol (*gargv, &ep, 0);
485 	check_conversion(*gargv++, ep);
486 	return val;
487 }
488 
489 static unsigned long
490 getulong()
491 {
492 	unsigned long val;
493 	char *ep;
494 
495 	if (!*gargv)
496 		return(0UL);
497 
498 	if (**gargv == '\"' || **gargv == '\'')
499 		return (unsigned long) *((*gargv++)+1);
500 
501 	errno = 0;
502 	val = strtoul (*gargv, &ep, 0);
503 	check_conversion(*gargv++, ep);
504 	return val;
505 }
506 
507 static double
508 getdouble()
509 {
510 	double val;
511 	char *ep;
512 
513 	if (!*gargv)
514 		return(0.0);
515 
516 	if (**gargv == '\"' || **gargv == '\'')
517 		return (double) *((*gargv++)+1);
518 
519 	errno = 0;
520 	val = strtod (*gargv, &ep);
521 	check_conversion(*gargv++, ep);
522 	return val;
523 }
524 
525 static void
526 check_conversion(s, ep)
527 	const char *s;
528 	const char *ep;
529 {
530 	if (*ep) {
531 		if (ep == s)
532 			warnx ("%s: expected numeric value", s);
533 		else
534 			warnx ("%s: not completely converted", s);
535 		rval = 1;
536 	} else if (errno == ERANGE) {
537 		warnx ("%s: %s", s, strerror(ERANGE));
538 		rval = 1;
539 	}
540 }
541 
542 static void
543 usage()
544 {
545 	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
546 }
547