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