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