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