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