Home | History | Annotate | Line # | Download | only in printf
printf.c revision 1.13
      1 /*
      2  * Copyright (c) 1989 The Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 #if !defined(SHELL) && !defined(BUILTIN)
     36 char copyright[] =
     37 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
     38  All rights reserved.\n";
     39 #endif
     40 #endif /* not lint */
     41 
     42 #ifndef lint
     43 /*static char sccsid[] = "from: @(#)printf.c	5.9 (Berkeley) 6/1/90";*/
     44 static char rcsid[] = "$Id: printf.c,v 1.13 1994/02/03 01:10:49 jtc Exp $";
     45 #endif /* not lint */
     46 
     47 #include <ctype.h>
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <limits.h>
     52 #include <locale.h>
     53 #include <errno.h>
     54 #include <err.h>
     55 
     56 static int	 print_escape_str __P((const char *));
     57 static int	 print_escape __P((const char *));
     58 
     59 static int	 getchr __P((void));
     60 static double	 getdouble __P((void));
     61 static int	 getint __P((void));
     62 static long	 getlong __P((void));
     63 static unsigned long getulong __P ((void));
     64 static char	*getstr __P((void));
     65 static char	*mklong __P((const char *, int));
     66 static void      check_conversion __P((const char *, const char *));
     67 static void	 usage __P((void));
     68 
     69 static int	rval;
     70 static char  **gargv;
     71 
     72 #define isodigit(c)	((c) >= '0' && (c) <= '7')
     73 #define octtobin(c)	((c) - '0')
     74 #define hextobin(c)	((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
     75 
     76 #ifdef SHELL
     77 #define main printfcmd
     78 #include "../../bin/sh/bltin/bltin.h"
     79 
     80 #ifdef __STDC__
     81 #include <stdarg.h>
     82 #else
     83 #include <vararg.h>
     84 #endif
     85 
     86 static void
     87 #ifdef __STDC__
     88 warnx(const char *fmt, ...)
     89 #else
     90 warnx(fmt, va_alist)
     91 	const char *fmt;
     92 	va_dcl
     93 #endif
     94 {
     95 
     96 	char buf[64];
     97 	va_list ap;
     98 
     99 #ifdef __STDC__
    100 	va_start(ap, fmt);
    101 #else
    102 	va_start(ap);
    103 #endif
    104 	vsprintf(buf, fmt, ap);
    105 	va_end(ap);
    106 
    107 	error(buf);
    108 }
    109 #endif /* SHELL */
    110 
    111 #define PF(f, func) { \
    112 	if (fieldwidth) \
    113 		if (precision) \
    114 			(void)printf(f, fieldwidth, precision, func); \
    115 		else \
    116 			(void)printf(f, fieldwidth, func); \
    117 	else if (precision) \
    118 		(void)printf(f, precision, func); \
    119 	else \
    120 		(void)printf(f, func); \
    121 }
    122 
    123 int
    124 #ifdef BUILTIN
    125 progprintf(argc, argv)
    126 #else
    127 main(argc, argv)
    128 #endif
    129 	int argc;
    130 	char **argv;
    131 {
    132 	register char *fmt, *start;
    133 	register int fieldwidth, precision;
    134 	char convch, nextch;
    135 	char *format;
    136 	int ch;
    137 
    138 #if !defined(SHELL) && !defined(BUILTIN)
    139 	setlocale (LC_ALL, "");
    140 #endif
    141 
    142 	while ((ch = getopt(argc, argv, "")) != -1) {
    143 		switch (ch) {
    144 		case '?':
    145 		default:
    146 			usage();
    147 			return (1);
    148 		}
    149 	}
    150 	argc -= optind;
    151 	argv += optind;
    152 
    153 	if (argc < 1) {
    154 		usage();
    155 		return (1);
    156 	}
    157 
    158 	format = *argv;
    159 	gargv = ++argv;
    160 
    161 #define SKIP1	"#-+ 0"
    162 #define SKIP2	"*0123456789"
    163 	do {
    164 		/*
    165 		 * Basic algorithm is to scan the format string for conversion
    166 		 * specifications -- once one is found, find out if the field
    167 		 * width or precision is a '*'; if it is, gather up value.
    168 		 * Note, format strings are reused as necessary to use up the
    169 		 * provided arguments, arguments of zero/null string are
    170 		 * provided to use up the format string.
    171 		 */
    172 
    173 		/* find next format specification */
    174 		for (fmt = format; *fmt; fmt++) {
    175 			switch (*fmt) {
    176 			case '%':
    177 				start = fmt++;
    178 
    179 				if (*fmt == '%') {
    180 					putchar ('%');
    181 					break;
    182 				} else if (*fmt == 'b') {
    183 					char *p = getstr();
    184 					if (print_escape_str(p)) {
    185 						return (rval);
    186 					}
    187 					break;
    188 				}
    189 
    190 				/* skip to field width */
    191 				for (; index(SKIP1, *fmt); ++fmt) ;
    192 				fieldwidth = *fmt == '*' ? getint() : 0;
    193 
    194 				/* skip to possible '.', get following precision */
    195 				for (; index(SKIP2, *fmt); ++fmt) ;
    196 				if (*fmt == '.')
    197 					++fmt;
    198 				precision = *fmt == '*' ? getint() : 0;
    199 
    200 				for (; index(SKIP2, *fmt); ++fmt) ;
    201 				if (!*fmt) {
    202 					warnx ("missing format character");
    203 					return(1);
    204 				}
    205 
    206 				convch = *fmt;
    207 				nextch = *(fmt + 1);
    208 				*(fmt + 1) = '\0';
    209 				switch(convch) {
    210 				case 'c': {
    211 					char p = getchr();
    212 					PF(start, p);
    213 					break;
    214 				}
    215 				case 's': {
    216 					char *p = getstr();
    217 					PF(start, p);
    218 					break;
    219 				}
    220 				case 'd':
    221 				case 'i': {
    222 					char *f = mklong(start, convch);
    223 					long p = getlong();
    224 					PF(f, p);
    225 					break;
    226 				}
    227 				case 'o':
    228 				case 'u':
    229 				case 'x':
    230 				case 'X': {
    231 					char *f = mklong(start, convch);
    232 					unsigned long p = getulong();
    233 					PF(f, p);
    234 					break;
    235 				}
    236 				case 'e':
    237 				case 'E':
    238 				case 'f':
    239 				case 'g':
    240 				case 'G': {
    241 					double p = getdouble();
    242 					PF(start, p);
    243 					break;
    244 				}
    245 				default:
    246 					warnx ("%s: invalid directive", start);
    247 					return(1);
    248 				}
    249 				*(fmt + 1) = nextch;
    250 				break;
    251 
    252 			case '\\':
    253 				fmt += print_escape(fmt);
    254 				break;
    255 
    256 			default:
    257 				putchar (*fmt);
    258 				break;
    259 			}
    260 		}
    261 	} while (gargv > argv && *gargv);
    262 
    263 	return (rval);
    264 }
    265 
    266 
    267 /*
    268  * Print SysV echo(1) style escape string
    269  *	Halts processing string and returns 1 if a \c escape is encountered.
    270  */
    271 static int
    272 print_escape_str(str)
    273 	register 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 				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 			putchar (*str);
    302 		}
    303 		str++;
    304 	}
    305 
    306 	return 0;
    307 }
    308 
    309 /*
    310  * Print "standard" escape characters
    311  */
    312 static int
    313 print_escape(str)
    314 	register const char *str;
    315 {
    316 	const char *start = str;
    317 	int value;
    318 	int c;
    319 
    320 	str++;
    321 
    322 	switch (*str) {
    323 	case '0': case '1': case '2': case '3':
    324 	case '4': case '5': case '6': case '7':
    325 		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
    326 			value <<= 3;
    327 			value += octtobin(*str);
    328 		}
    329 		putchar(value);
    330 		return str - start - 1;
    331 		/* NOTREACHED */
    332 
    333 	case 'x':
    334 		str++;
    335 		for (value = 0; isxdigit(*str); str++) {
    336 			value <<= 4;
    337 			value += hextobin(*str);
    338 		}
    339 		if (value > UCHAR_MAX) {
    340 			warnx ("escape sequence out of range for character");
    341 			rval = 1;
    342 		}
    343 		putchar (value);
    344 		return str - start - 1;
    345 		/* NOTREACHED */
    346 
    347 	case '\\':			/* backslash */
    348 		putchar('\\');
    349 		break;
    350 
    351 	case '\'':			/* single quote */
    352 		putchar('\'');
    353 		break;
    354 
    355 	case '"':			/* double quote */
    356 		putchar('"');
    357 		break;
    358 
    359 	case 'a':			/* alert */
    360 #ifdef __STDC__
    361 		putchar('\a');
    362 #else
    363 		putchar(007);
    364 #endif
    365 		break;
    366 
    367 	case 'b':			/* backspace */
    368 		putchar('\b');
    369 		break;
    370 
    371 	case 'e':			/* escape */
    372 #ifdef __GNUC__
    373 		putchar('\e');
    374 #else
    375 		putchar(033);
    376 #endif
    377 		break;
    378 
    379 	case 'f':			/* form-feed */
    380 		putchar('\f');
    381 		break;
    382 
    383 	case 'n':			/* newline */
    384 		putchar('\n');
    385 		break;
    386 
    387 	case 'r':			/* carriage-return */
    388 		putchar('\r');
    389 		break;
    390 
    391 	case 't':			/* tab */
    392 		putchar('\t');
    393 		break;
    394 
    395 	case 'v':			/* vertical-tab */
    396 		putchar('\v');
    397 		break;
    398 
    399 	default:
    400 		putchar(*str);
    401 		warnx("unknown escape sequence `\\%c'", *str);
    402 		rval = 1;
    403 	}
    404 
    405 	return 1;
    406 }
    407 
    408 static char *
    409 mklong(str, ch)
    410 	const char *str;
    411 	char ch;
    412 {
    413 	static char copy[64];
    414 	int len;
    415 
    416 	len = strlen(str) + 2;
    417 	(void) memmove(copy, str, len - 3);
    418 	copy[len - 3] = 'l';
    419 	copy[len - 2] = ch;
    420 	copy[len - 1] = '\0';
    421 	return (copy);
    422 }
    423 
    424 static int
    425 getchr()
    426 {
    427 	if (!*gargv)
    428 		return((int)'\0');
    429 	return((int)**gargv++);
    430 }
    431 
    432 static char *
    433 getstr()
    434 {
    435 	if (!*gargv)
    436 		return("");
    437 	return(*gargv++);
    438 }
    439 
    440 static char *number = "+-.0123456789";
    441 static int
    442 getint()
    443 {
    444 	if (!*gargv)
    445 		return(0);
    446 
    447 	if (index(number, **gargv))
    448 		return(atoi(*gargv++));
    449 
    450 	return 0;
    451 }
    452 
    453 static long
    454 getlong()
    455 {
    456 	long val;
    457 	char *ep;
    458 
    459 	if (!*gargv)
    460 		return(0L);
    461 
    462 	if (**gargv == '\"' || **gargv == '\'')
    463 		return (long) *((*gargv++)+1);
    464 
    465 	errno = 0;
    466 	val = strtol (*gargv, &ep, 0);
    467 	check_conversion(*gargv++, ep);
    468 	return val;
    469 }
    470 
    471 static unsigned long
    472 getulong()
    473 {
    474 	unsigned long val;
    475 	char *ep;
    476 
    477 	if (!*gargv)
    478 		return(0UL);
    479 
    480 	if (**gargv == '\"' || **gargv == '\'')
    481 		return (unsigned long) *((*gargv++)+1);
    482 
    483 	errno = 0;
    484 	val = strtoul (*gargv, &ep, 0);
    485 	check_conversion(*gargv++, ep);
    486 	return val;
    487 }
    488 
    489 static double
    490 getdouble()
    491 {
    492 	double val;
    493 	char *ep;
    494 
    495 	if (!*gargv)
    496 		return(0.0);
    497 
    498 	if (**gargv == '\"' || **gargv == '\'')
    499 		return (double) *((*gargv++)+1);
    500 
    501 	errno = 0;
    502 	val = strtod (*gargv, &ep);
    503 	check_conversion(*gargv++, ep);
    504 	return val;
    505 }
    506 
    507 static void
    508 check_conversion(s, ep)
    509 	const char *s;
    510 	const char *ep;
    511 {
    512 	if (*ep) {
    513 		if (ep == s)
    514 			warnx ("%s: expected numeric value", s);
    515 		else
    516 			warnx ("%s: not completely converted", s);
    517 		rval = 1;
    518 	} else if (errno == ERANGE) {
    519 		warnx ("%s: %s", s, strerror(ERANGE));
    520 		rval = 1;
    521 	}
    522 }
    523 
    524 static void
    525 usage()
    526 {
    527 	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
    528 }
    529