xref: /freebsd/usr.bin/printf/printf.c (revision 12f110aa1ad3c9d0ead55bf80f2f994b4b845ffb)
1 /*-
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  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  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30  * Important: This file is used both as a standalone program /usr/bin/printf
31  * and as a builtin for /bin/sh (#define SHELL).
32  */
33 
34 #ifndef SHELL
35 #ifndef lint
36 static char const copyright[] =
37 "@(#) Copyright (c) 1989, 1993\n\
38 	The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40 #endif
41 
42 #ifndef lint
43 #if 0
44 static char const sccsid[] = "@(#)printf.c	8.1 (Berkeley) 7/20/93";
45 #endif
46 static const char rcsid[] =
47   "$FreeBSD$";
48 #endif /* not lint */
49 
50 #include <sys/types.h>
51 
52 #include <err.h>
53 #include <errno.h>
54 #include <inttypes.h>
55 #include <limits.h>
56 #include <locale.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61 #include <wchar.h>
62 
63 #ifdef SHELL
64 #define main printfcmd
65 #include "bltin/bltin.h"
66 #include "error.h"
67 #include "options.h"
68 #endif
69 
70 #define PF(f, func) do {						\
71 	char *b = NULL;							\
72 	if (havewidth)							\
73 		if (haveprec)						\
74 			(void)asprintf(&b, f, fieldwidth, precision, func); \
75 		else							\
76 			(void)asprintf(&b, f, fieldwidth, func);	\
77 	else if (haveprec)						\
78 		(void)asprintf(&b, f, precision, func);			\
79 	else								\
80 		(void)asprintf(&b, f, func);				\
81 	if (b) {							\
82 		(void)fputs(b, stdout);					\
83 		free(b);						\
84 	}								\
85 } while (0)
86 
87 static int	 asciicode(void);
88 static char	*printf_doformat(char *, int *);
89 static int	 escape(char *, int, size_t *);
90 static int	 getchr(void);
91 static int	 getfloating(long double *, int);
92 static int	 getint(int *);
93 static int	 getnum(intmax_t *, uintmax_t *, int);
94 static const char
95 		*getstr(void);
96 static char	*mknum(char *, char);
97 static void	 usage(void);
98 
99 static char **gargv;
100 
101 int
102 main(int argc, char *argv[])
103 {
104 	size_t len;
105 	int chopped, end, rval;
106 	char *format, *fmt, *start;
107 #ifndef SHELL
108 	int ch;
109 
110 	(void) setlocale(LC_ALL, "");
111 #endif
112 
113 #ifdef SHELL
114 	nextopt("");
115 	argc -= argptr - argv;
116 	argv = argptr;
117 #else
118 	while ((ch = getopt(argc, argv, "")) != -1)
119 		switch (ch) {
120 		case '?':
121 		default:
122 			usage();
123 			return (1);
124 		}
125 	argc -= optind;
126 	argv += optind;
127 #endif
128 
129 	if (argc < 1) {
130 		usage();
131 		return (1);
132 	}
133 
134 #ifdef SHELL
135 	INTOFF;
136 #endif
137 	/*
138 	 * Basic algorithm is to scan the format string for conversion
139 	 * specifications -- once one is found, find out if the field
140 	 * width or precision is a '*'; if it is, gather up value.  Note,
141 	 * format strings are reused as necessary to use up the provided
142 	 * arguments, arguments of zero/null string are provided to use
143 	 * up the format string.
144 	 */
145 	fmt = format = *argv;
146 	chopped = escape(fmt, 1, &len);		/* backslash interpretation */
147 	rval = end = 0;
148 	gargv = ++argv;
149 	for (;;) {
150 		start = fmt;
151 		while (fmt < format + len) {
152 			if (fmt[0] == '%') {
153 				fwrite(start, 1, fmt - start, stdout);
154 				if (fmt[1] == '%') {
155 					/* %% prints a % */
156 					putchar('%');
157 					fmt += 2;
158 				} else {
159 					fmt = printf_doformat(fmt, &rval);
160 					if (fmt == NULL) {
161 #ifdef SHELL
162 						INTON;
163 #endif
164 						return (1);
165 					}
166 					end = 0;
167 				}
168 				start = fmt;
169 			} else
170 				fmt++;
171 		}
172 
173 		if (end == 1) {
174 			warnx("missing format character");
175 #ifdef SHELL
176 			INTON;
177 #endif
178 			return (1);
179 		}
180 		fwrite(start, 1, fmt - start, stdout);
181 		if (chopped || !*gargv) {
182 #ifdef SHELL
183 			INTON;
184 #endif
185 			return (rval);
186 		}
187 		/* Restart at the beginning of the format string. */
188 		fmt = format;
189 		end = 1;
190 	}
191 	/* NOTREACHED */
192 }
193 
194 
195 static char *
196 printf_doformat(char *start, int *rval)
197 {
198 	static const char skip1[] = "#'-+ 0";
199 	static const char skip2[] = "0123456789";
200 	char *fmt;
201 	int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
202 	char convch, nextch;
203 
204 	fmt = start + 1;
205 	/* skip to field width */
206 	fmt += strspn(fmt, skip1);
207 	if (*fmt == '*') {
208 		if (getint(&fieldwidth))
209 			return (NULL);
210 		havewidth = 1;
211 		++fmt;
212 	} else {
213 		havewidth = 0;
214 
215 		/* skip to possible '.', get following precision */
216 		fmt += strspn(fmt, skip2);
217 	}
218 	if (*fmt == '.') {
219 		/* precision present? */
220 		++fmt;
221 		if (*fmt == '*') {
222 			if (getint(&precision))
223 				return (NULL);
224 			haveprec = 1;
225 			++fmt;
226 		} else {
227 			haveprec = 0;
228 
229 			/* skip to conversion char */
230 			fmt += strspn(fmt, skip2);
231 		}
232 	} else
233 		haveprec = 0;
234 	if (!*fmt) {
235 		warnx("missing format character");
236 		return (NULL);
237 	}
238 
239 	/*
240 	 * Look for a length modifier.  POSIX doesn't have these, so
241 	 * we only support them for floating-point conversions, which
242 	 * are extensions.  This is useful because the L modifier can
243 	 * be used to gain extra range and precision, while omitting
244 	 * it is more likely to produce consistent results on different
245 	 * architectures.  This is not so important for integers
246 	 * because overflow is the only bad thing that can happen to
247 	 * them, but consider the command  printf %a 1.1
248 	 */
249 	if (*fmt == 'L') {
250 		mod_ldbl = 1;
251 		fmt++;
252 		if (!strchr("aAeEfFgG", *fmt)) {
253 			warnx("bad modifier L for %%%c", *fmt);
254 			return (NULL);
255 		}
256 	} else {
257 		mod_ldbl = 0;
258 	}
259 
260 	convch = *fmt;
261 	nextch = *++fmt;
262 	*fmt = '\0';
263 	switch (convch) {
264 	case 'b': {
265 		size_t len;
266 		char *p;
267 		int getout;
268 
269 		p = strdup(getstr());
270 		if (p == NULL) {
271 			warnx("%s", strerror(ENOMEM));
272 			return (NULL);
273 		}
274 		getout = escape(p, 0, &len);
275 		*(fmt - 1) = 's';
276 		PF(start, p);
277 		*(fmt - 1) = 'b';
278 		free(p);
279 		if (getout)
280 			return (fmt);
281 		break;
282 	}
283 	case 'c': {
284 		char p;
285 
286 		p = getchr();
287 		PF(start, p);
288 		break;
289 	}
290 	case 's': {
291 		const char *p;
292 
293 		p = getstr();
294 		PF(start, p);
295 		break;
296 	}
297 	case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
298 		char *f;
299 		intmax_t val;
300 		uintmax_t uval;
301 		int signedconv;
302 
303 		signedconv = (convch == 'd' || convch == 'i');
304 		if ((f = mknum(start, convch)) == NULL)
305 			return (NULL);
306 		if (getnum(&val, &uval, signedconv))
307 			*rval = 1;
308 		if (signedconv)
309 			PF(f, val);
310 		else
311 			PF(f, uval);
312 		break;
313 	}
314 	case 'e': case 'E':
315 	case 'f': case 'F':
316 	case 'g': case 'G':
317 	case 'a': case 'A': {
318 		long double p;
319 
320 		if (getfloating(&p, mod_ldbl))
321 			*rval = 1;
322 		if (mod_ldbl)
323 			PF(start, p);
324 		else
325 			PF(start, (double)p);
326 		break;
327 	}
328 	default:
329 		warnx("illegal format character %c", convch);
330 		return (NULL);
331 	}
332 	*fmt = nextch;
333 	return (fmt);
334 }
335 
336 static char *
337 mknum(char *str, char ch)
338 {
339 	static char *copy;
340 	static size_t copy_size;
341 	char *newcopy;
342 	size_t len, newlen;
343 
344 	len = strlen(str) + 2;
345 	if (len > copy_size) {
346 		newlen = ((len + 1023) >> 10) << 10;
347 		if ((newcopy = realloc(copy, newlen)) == NULL)
348 		{
349 			warnx("%s", strerror(ENOMEM));
350 			return (NULL);
351 		}
352 		copy = newcopy;
353 		copy_size = newlen;
354 	}
355 
356 	memmove(copy, str, len - 3);
357 	copy[len - 3] = 'j';
358 	copy[len - 2] = ch;
359 	copy[len - 1] = '\0';
360 	return (copy);
361 }
362 
363 static int
364 escape(char *fmt, int percent, size_t *len)
365 {
366 	char *save, *store, c;
367 	int value;
368 
369 	for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
370 		if (c != '\\') {
371 			*store = c;
372 			continue;
373 		}
374 		switch (*++fmt) {
375 		case '\0':		/* EOS, user error */
376 			*store = '\\';
377 			*++store = '\0';
378 			*len = store - save;
379 			return (0);
380 		case '\\':		/* backslash */
381 		case '\'':		/* single quote */
382 			*store = *fmt;
383 			break;
384 		case 'a':		/* bell/alert */
385 			*store = '\a';
386 			break;
387 		case 'b':		/* backspace */
388 			*store = '\b';
389 			break;
390 		case 'c':
391 			*store = '\0';
392 			*len = store - save;
393 			return (1);
394 		case 'f':		/* form-feed */
395 			*store = '\f';
396 			break;
397 		case 'n':		/* newline */
398 			*store = '\n';
399 			break;
400 		case 'r':		/* carriage-return */
401 			*store = '\r';
402 			break;
403 		case 't':		/* horizontal tab */
404 			*store = '\t';
405 			break;
406 		case 'v':		/* vertical tab */
407 			*store = '\v';
408 			break;
409 					/* octal constant */
410 		case '0': case '1': case '2': case '3':
411 		case '4': case '5': case '6': case '7':
412 			c = (!percent && *fmt == '0') ? 4 : 3;
413 			for (value = 0;
414 			    c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
415 				value <<= 3;
416 				value += *fmt - '0';
417 			}
418 			--fmt;
419 			if (percent && value == '%') {
420 				*store++ = '%';
421 				*store = '%';
422 			} else
423 				*store = (char)value;
424 			break;
425 		default:
426 			*store = *fmt;
427 			break;
428 		}
429 	}
430 	*store = '\0';
431 	*len = store - save;
432 	return (0);
433 }
434 
435 static int
436 getchr(void)
437 {
438 	if (!*gargv)
439 		return ('\0');
440 	return ((int)**gargv++);
441 }
442 
443 static const char *
444 getstr(void)
445 {
446 	if (!*gargv)
447 		return ("");
448 	return (*gargv++);
449 }
450 
451 static int
452 getint(int *ip)
453 {
454 	intmax_t val;
455 	uintmax_t uval;
456 	int rval;
457 
458 	if (getnum(&val, &uval, 1))
459 		return (1);
460 	rval = 0;
461 	if (val < INT_MIN || val > INT_MAX) {
462 		warnx("%s: %s", *gargv, strerror(ERANGE));
463 		rval = 1;
464 	}
465 	*ip = (int)val;
466 	return (rval);
467 }
468 
469 static int
470 getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
471 {
472 	char *ep;
473 	int rval;
474 
475 	if (!*gargv) {
476 		*ip = *uip = 0;
477 		return (0);
478 	}
479 	if (**gargv == '"' || **gargv == '\'') {
480 		if (signedconv)
481 			*ip = asciicode();
482 		else
483 			*uip = asciicode();
484 		return (0);
485 	}
486 	rval = 0;
487 	errno = 0;
488 	if (signedconv)
489 		*ip = strtoimax(*gargv, &ep, 0);
490 	else
491 		*uip = strtoumax(*gargv, &ep, 0);
492 	if (ep == *gargv) {
493 		warnx("%s: expected numeric value", *gargv);
494 		rval = 1;
495 	}
496 	else if (*ep != '\0') {
497 		warnx("%s: not completely converted", *gargv);
498 		rval = 1;
499 	}
500 	if (errno == ERANGE) {
501 		warnx("%s: %s", *gargv, strerror(ERANGE));
502 		rval = 1;
503 	}
504 	++gargv;
505 	return (rval);
506 }
507 
508 static int
509 getfloating(long double *dp, int mod_ldbl)
510 {
511 	char *ep;
512 	int rval;
513 
514 	if (!*gargv) {
515 		*dp = 0.0;
516 		return (0);
517 	}
518 	if (**gargv == '"' || **gargv == '\'') {
519 		*dp = asciicode();
520 		return (0);
521 	}
522 	rval = 0;
523 	errno = 0;
524 	if (mod_ldbl)
525 		*dp = strtold(*gargv, &ep);
526 	else
527 		*dp = strtod(*gargv, &ep);
528 	if (ep == *gargv) {
529 		warnx("%s: expected numeric value", *gargv);
530 		rval = 1;
531 	} else if (*ep != '\0') {
532 		warnx("%s: not completely converted", *gargv);
533 		rval = 1;
534 	}
535 	if (errno == ERANGE) {
536 		warnx("%s: %s", *gargv, strerror(ERANGE));
537 		rval = 1;
538 	}
539 	++gargv;
540 	return (rval);
541 }
542 
543 static int
544 asciicode(void)
545 {
546 	int ch;
547 	wchar_t wch;
548 	mbstate_t mbs;
549 
550 	ch = (unsigned char)**gargv;
551 	if (ch == '\'' || ch == '"') {
552 		memset(&mbs, 0, sizeof(mbs));
553 		switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
554 		case (size_t)-2:
555 		case (size_t)-1:
556 			wch = (unsigned char)gargv[0][1];
557 			break;
558 		case 0:
559 			wch = 0;
560 			break;
561 		}
562 		ch = wch;
563 	}
564 	++gargv;
565 	return (ch);
566 }
567 
568 static void
569 usage(void)
570 {
571 	(void)fprintf(stderr, "usage: printf format [arguments ...]\n");
572 }
573