1 /*	$OpenBSD: printf.c,v 1.17 2009/10/27 23:59:41 deraadt 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifdef MKSH_PRINTF_BUILTIN
33 /* MirBSD Korn Shell */
34 #include "sh.h"
35 #else
36 /* stand-alone executable */
37 #include <sys/types.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <locale.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 #define vstrchr strchr
48 #endif
49 
50 __RCSID("$MirOS: src/usr.bin/printf/printf.c,v 1.20 2012/12/22 22:19:27 tg Exp $");
51 
52 static int	 print_escape_str(const char *);
53 static int	 print_escape(const char *);
54 
55 static int	 getchr(void);
56 #ifndef MKSH_PRINTF_BUILTIN
57 static double	 getdouble(void);
58 #endif
59 static int	 getinteger(void);
60 static long	 getlong(void);
61 static unsigned long getulong(void);
62 static const char *getstr(void);
63 static char	*mklong(const char *, int);
64 static void      check_conversion(const char *, const char *);
65 static int	 usage(void);
66 static int	 real_main(char *, const char *[]);
67 
68 static int	rval;
69 static const char **gargv;
70 
71 #ifdef MKSH_PRINTF_BUILTIN
72 static int s_get(void);
73 static void s_put(int);
74 static void s_prt(int);
75 
76 static const char *s_ptr;
77 
78 #define isxdigit(c)	(((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
79 #endif
80 
81 #define isodigit(c)	((c) >= '0' && (c) <= '7')
82 #define octtobin(c)	((c) - '0')
83 #define hextobin(c)	((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
84 
85 #define PF(f, func)	do {						\
86 	if (fieldwidth)							\
87 		if (precision)						\
88 			uprintf(f, fieldwidth, precision, func);	\
89 		else							\
90 			uprintf(f, fieldwidth, func);			\
91 	else if (precision)						\
92 		uprintf(f, precision, func);				\
93 	else								\
94 		uprintf(f, func);					\
95 } while (/* CONSTCOND */ 0)
96 
97 #ifndef vstrchr
98 #define vstrchr		strchr
99 #endif
100 
101 #ifndef cstrerror
102 #define cstrerror(e)	((const char *)strerror(e))
103 #endif
104 
105 #ifdef MKSH_PRINTF_BUILTIN
106 #define ufprintf	shf_fprintf
107 #define uprintf		shprintf
108 #define uputc(c)	shf_putchar((c), shl_stdout)
109 #define ustderr		shl_out
110 #define uwarnx		warningf
111 #define UWARNX		false,
112 
113 int
c_printf(const char ** wp)114 c_printf(const char **wp)
115 {
116 	int rv;
117 	const char *old_kshname;
118 	char *fmt;
119 	uint8_t old_utfmode;
120 
121 	old_kshname = kshname;
122 	kshname = wp[0];
123 	++wp;
124 	if (wp[0] && !strcmp(wp[0], "--"))
125 		++wp;
126 	if (wp[0]) {
127 		strdupx(fmt, wp[0], ATEMP);
128 		old_utfmode = UTFMODE;
129 		UTFMODE = 0;
130 		rv = real_main(fmt, wp);
131 		UTFMODE = old_utfmode;
132 		afree(fmt, ATEMP);
133 	} else
134 		rv = usage();
135 	kshname = old_kshname;
136 	return (rv);
137 }
138 #else
139 #define ufprintf	fprintf
140 #define uprintf		printf
141 #define uputc		putchar
142 #define ustderr		stderr
143 #define uwarnx		warnx
144 #define UWARNX		/* nothing */
145 
146 int
main(int argc,char * argv[])147 main(int argc, char *argv[])
148 {
149 	setlocale(LC_ALL, "");
150 
151 	if (argc > 1 && !strcmp(argv[1], "--")) {
152 		--argc;
153 		++argv;
154 	}
155 
156 	++argv;
157 	return (argc < 2 ? usage() : real_main(argv[0], (const char **)argv));
158 }
159 #endif
160 
161 static int
real_main(char * format,const char * argv[])162 real_main(char *format, const char *argv[])
163 {
164 	char *fmt, *start;
165 	int fieldwidth, precision;
166 	char convch, nextch;
167 
168 	gargv = ++argv;
169 
170 #define SKIP1	"#-+ 0"
171 #define SKIP2	"0123456789"
172 	do {
173 		/*
174 		 * Basic algorithm is to scan the format string for conversion
175 		 * specifications -- once one is found, find out if the field
176 		 * width or precision is a '*'; if it is, gather up value.
177 		 * Note, format strings are reused as necessary to use up the
178 		 * provided arguments, arguments of zero/null string are
179 		 * provided to use up the format string.
180 		 */
181 
182 		/* find next format specification */
183 		for (fmt = format; *fmt; fmt++) {
184 			switch (*fmt) {
185 			case '%':
186 				start = fmt++;
187 
188 				if (*fmt == '%') {
189 					uputc('%');
190 					break;
191 				} else if (*fmt == 'b') {
192 					const char *p = getstr();
193 					if (print_escape_str(p)) {
194 						return (rval);
195 					}
196 					break;
197 				} else if (!*fmt) {
198 					goto synerr;
199 				}
200 
201 				/* skip to field width */
202 				for (; vstrchr(SKIP1, *fmt); ++fmt)
203 					;
204 				if (*fmt == '*') {
205 					++fmt;
206 					fieldwidth = getinteger();
207 				} else if (!*fmt) {
208 					goto synerr;
209 				} else
210 					fieldwidth = 0;
211 
212 				/* skip to field precision */
213 				for (; vstrchr(SKIP2, *fmt); ++fmt)
214 					;
215 				precision = 0;
216 				if (*fmt == '.') {
217 					++fmt;
218 					if (*fmt == '*') {
219 						++fmt;
220 						precision = getinteger();
221 					}
222 					if (!*fmt)
223 						goto synerr;
224 					for (; vstrchr(SKIP2, *fmt); ++fmt)
225 						;
226 				}
227 
228 				if (!*fmt) {
229  synerr:
230 					uwarnx(UWARNX "missing format character");
231 					return (1);
232 				}
233 
234 				convch = *fmt;
235 				nextch = *(fmt + 1);
236 				*(fmt + 1) = '\0';
237 				switch(convch) {
238 				case 'c': {
239 					char p = getchr();
240 					PF(start, p);
241 					break;
242 				}
243 				case 's': {
244 					const char *p = getstr();
245 					PF(start, p);
246 					break;
247 				}
248 				case 'd':
249 				case 'i': {
250 					long p;
251 					char *f = mklong(start, convch);
252 					if (!f) {
253 						uwarnx(UWARNX "out of memory");
254 						return (1);
255 					}
256 					p = getlong();
257 					PF(f, p);
258 					break;
259 				}
260 				case 'o':
261 				case 'u':
262 				case 'x':
263 				case 'X': {
264 					unsigned long p;
265 					char *f = mklong(start, convch);
266 					if (!f) {
267 						uwarnx(UWARNX "out of memory");
268 						return (1);
269 					}
270 					p = getulong();
271 					PF(f, p);
272 					break;
273 				}
274 #ifndef MKSH_PRINTF_BUILTIN
275 #if 0
276 				case 'a':
277 				case 'A':
278 #endif
279 				case 'e':
280 				case 'E':
281 				case 'f':
282 				case 'F':
283 				case 'g':
284 				case 'G': {
285 					double p = getdouble();
286 					PF(start, p);
287 					break;
288 				}
289 #endif
290 
291 				default:
292 					uwarnx(UWARNX "%s: invalid directive", start);
293 					return (1);
294 				}
295 				*(fmt + 1) = nextch;
296 				break;
297 
298 			case '\\':
299 				fmt += print_escape(fmt);
300 				break;
301 
302 			default:
303 				uputc(*fmt);
304 				break;
305 			}
306 		}
307 	} while (gargv > argv && *gargv);
308 
309 	return (rval);
310 }
311 
312 
313 /*
314  * Print SysV echo(1) style escape string
315  *	Halts processing string and returns 1 if a \c escape is encountered.
316  */
317 static int
print_escape_str(const char * str)318 print_escape_str(const char *str)
319 {
320 #ifndef MKSH_PRINTF_BUILTIN
321 	int value;
322 #endif
323 	int c;
324 
325 	while (*str) {
326 		if (*str == '\\') {
327 #ifdef MKSH_PRINTF_BUILTIN
328 			s_ptr = str + 1;
329 			c = unbksl(false, s_get, s_put);
330 			str = s_ptr;
331 			if (c == -1) {
332 				if ((c = *str++) == 'c')
333 					return (1);
334 				else if (!c)
335 					--str;
336 				uputc(c);
337 				uwarnx(UWARNX
338 				    "unknown escape sequence `\\%c'", c);
339 				rval = 1;
340 			} else
341 				s_prt(c);
342 			continue;
343 #else
344 			str++;
345 			/*
346 			 * %b string octal constants are not like those in C.
347 			 * They start with a \0, and are followed by 0, 1, 2,
348 			 * or 3 octal digits.
349 			 */
350 			if (*str == '0') {
351 				str++;
352 				for (c = 3, value = 0; c-- && isodigit(*str); str++) {
353 					value <<= 3;
354 					value += octtobin(*str);
355 				}
356 				uputc(value);
357 				str--;
358 			} else if (*str == 'c') {
359 				return (1);
360 			} else {
361 				str--;
362 				str += print_escape(str);
363 			}
364 #endif
365 		} else {
366 			uputc(*str);
367 		}
368 		str++;
369 	}
370 
371 	return (0);
372 }
373 
374 /*
375  * Print "standard" escape characters
376  */
377 static int
print_escape(const char * str)378 print_escape(const char *str)
379 {
380 #ifdef MKSH_PRINTF_BUILTIN
381 	int c;
382 
383 	s_ptr = str + 1;
384 	c = unbksl(true, s_get, s_put);
385 	if (c == -1) {
386 		s_ptr = str + 1;
387 		switch ((c = *s_ptr++)) {
388 		case '\\':
389 		case '\'':
390 		case '"':
391 			break;
392 		case '\0':
393 			--s_ptr;
394 			/* FALLTHROUGH */
395 		default:
396 			uwarnx(UWARNX "unknown escape sequence `\\%c'", c);
397 			rval = 1;
398 		}
399 	}
400 	s_prt(c);
401 	return (s_ptr - str - 1);
402 #else
403 	const char *start = str;
404 	int value;
405 	int c;
406 
407 	str++;
408 
409 	switch (*str) {
410 	case '0': case '1': case '2': case '3':
411 	case '4': case '5': case '6': case '7':
412 		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
413 			value <<= 3;
414 			value += octtobin(*str);
415 		}
416 		uputc(value);
417 		return (str - start - 1);
418 		/* NOTREACHED */
419 
420 	case 'x':
421 		str++;
422 		for (value = 0; isxdigit(*str); str++) {
423 			value <<= 4;
424 			value += hextobin(*str);
425 		}
426 		if (value > UCHAR_MAX) {
427 			uwarnx(UWARNX "escape sequence out of range for character");
428 			rval = 1;
429 		}
430 		uputc(value);
431 		return (str - start - 1);
432 		/* NOTREACHED */
433 
434 	case '\\':			/* backslash */
435 		uputc('\\');
436 		break;
437 
438 	case '\'':			/* single quote */
439 		uputc('\'');
440 		break;
441 
442 	case '"':			/* double quote */
443 		uputc('"');
444 		break;
445 
446 	case 'a':			/* alert */
447 		uputc('\a');
448 		break;
449 
450 	case 'b':			/* backspace */
451 		uputc('\b');
452 		break;
453 
454 	case 'e':			/* escape */
455 		uputc(033);
456 		break;
457 
458 	case 'f':			/* form-feed */
459 		uputc('\f');
460 		break;
461 
462 	case 'n':			/* newline */
463 		uputc('\n');
464 		break;
465 
466 	case 'r':			/* carriage-return */
467 		uputc('\r');
468 		break;
469 
470 	case 't':			/* tab */
471 		uputc('\t');
472 		break;
473 
474 	case 'v':			/* vertical-tab */
475 		uputc('\v');
476 		break;
477 
478 	default:
479 		uputc(*str);
480 		uwarnx(UWARNX "unknown escape sequence `\\%c'", *str);
481 		rval = 1;
482 	}
483 
484 	return (1);
485 #endif
486 }
487 
488 static char *
mklong(const char * str,int ch)489 mklong(const char *str, int ch)
490 {
491 	static char *copy;
492 	static size_t copysize;
493 	size_t len;
494 
495 	len = strlen(str) + 2;
496 	if (copysize < len) {
497 		char *newcopy;
498 
499 		copysize = len + 256;
500 
501 		newcopy = realloc(copy, copysize);
502 		if (newcopy == NULL) {
503 			copysize = 0;
504 			free(copy);
505 			copy = NULL;
506 			return (NULL);
507 		}
508 		copy = newcopy;
509 	}
510 	(void) memmove(copy, str, len - 3);
511 	copy[len - 3] = 'l';
512 	copy[len - 2] = ch;
513 	copy[len - 1] = '\0';
514 	return (copy);
515 }
516 
517 static int
getchr(void)518 getchr(void)
519 {
520 	if (!*gargv)
521 		return ((int)'\0');
522 	return ((int)**gargv++);
523 }
524 
525 static const char *
getstr(void)526 getstr(void)
527 {
528 	if (!*gargv)
529 		return ("");
530 	return (*gargv++);
531 }
532 
533 static const char *number = "+-.0123456789";
534 static int
getinteger(void)535 getinteger(void)
536 {
537 	if (!*gargv)
538 		return (0);
539 
540 	if (**gargv && vstrchr(number, **gargv))
541 		return (atoi(*gargv++));
542 
543 	return (0);
544 }
545 
546 static long
getlong(void)547 getlong(void)
548 {
549 	long val;
550 	char *ep;
551 
552 	if (!*gargv)
553 		return (0L);
554 
555 	if (**gargv == '\"' || **gargv == '\'')
556 		return ((long)*((*gargv++)+1));
557 
558 	errno = 0;
559 	val = strtol(*gargv, &ep, 0);
560 	check_conversion(*gargv++, ep);
561 	return (val);
562 }
563 
564 static unsigned long
getulong(void)565 getulong(void)
566 {
567 	unsigned long val;
568 	char *ep;
569 
570 	if (!*gargv)
571 		return (0UL);
572 
573 	if (**gargv == '\"' || **gargv == '\'')
574 		return ((unsigned long) *((*gargv++)+1));
575 
576 	errno = 0;
577 	val = strtoul(*gargv, &ep, 0);
578 	check_conversion(*gargv++, ep);
579 	return (val);
580 }
581 
582 #ifndef MKSH_PRINTF_BUILTIN
583 static double
getdouble(void)584 getdouble(void)
585 {
586 	double val;
587 	char *ep;
588 
589 	if (!*gargv)
590 		return (0.0);
591 
592 	if (**gargv == '\"' || **gargv == '\'')
593 		return ((double)*((*gargv++)+1));
594 
595 	errno = 0;
596 	val = strtod(*gargv, &ep);
597 	check_conversion(*gargv++, ep);
598 	return (val);
599 }
600 #endif
601 
602 static void
check_conversion(const char * s,const char * ep)603 check_conversion(const char *s, const char *ep)
604 {
605 	if (*ep) {
606 		if (ep == s)
607 			uwarnx(UWARNX "%s: expected numeric value", s);
608 		else
609 			uwarnx(UWARNX "%s: not completely converted", s);
610 		rval = 1;
611 	} else if (errno == ERANGE) {
612 		uwarnx(UWARNX "%s: %s", s, cstrerror(ERANGE));
613 		rval = 1;
614 	}
615 }
616 
617 static int
usage(void)618 usage(void)
619 {
620 	ufprintf(ustderr, "usage: printf format [arg ...]\n");
621 	return (1);
622 }
623 
624 #ifdef MKSH_PRINTF_BUILTIN
625 static int
s_get(void)626 s_get(void)
627 {
628 	return (*s_ptr++);
629 }
630 
631 static void
s_put(int c MKSH_A_UNUSED)632 s_put(int c MKSH_A_UNUSED)
633 {
634 	--s_ptr;
635 }
636 
637 static void
s_prt(int c)638 s_prt(int c)
639 {
640 	char ts[4];
641 
642 	if ((unsigned int)c < 0x100) {
643 		ts[0] = c;
644 		c = 1;
645 	} else
646 		c = (int)utf_wctomb(ts, c - 0x100);
647 
648 	shf_write(ts, c, shl_stdout);
649 }
650 #endif
651