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