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