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