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