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