Home | History | Annotate | Line # | Download | only in printf
printf.c revision 1.10
      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.10 1993/12/31 01:46:16 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 					return(1);
    201 				}
    202 
    203 				convch = *fmt;
    204 				nextch = *(fmt + 1);
    205 				*(fmt + 1) = '\0';
    206 				switch(convch) {
    207 				case 'c': {
    208 					char p = getchr();
    209 					PF(start, p);
    210 					break;
    211 				}
    212 				case 's': {
    213 					char *p = getstr();
    214 					PF(start, p);
    215 					break;
    216 				}
    217 				case 'd':
    218 				case 'i':
    219 				case 'o':
    220 				case 'u':
    221 				case 'x':
    222 				case 'X': {
    223 					char *f = mklong(start, convch);
    224 					long p = getlong();
    225 					PF(f, p);
    226 					break;
    227 				}
    228 				case 'e':
    229 				case 'E':
    230 				case 'f':
    231 				case 'g':
    232 				case 'G': {
    233 					double p = getdouble();
    234 					PF(start, p);
    235 					break;
    236 				}
    237 				default:
    238 					warnx ("%s: invalid directive", start);
    239 					return(1);
    240 				}
    241 				*(fmt + 1) = nextch;
    242 				break;
    243 
    244 			case '\\':
    245 				fmt += print_escape(fmt);
    246 				break;
    247 
    248 			default:
    249 				putchar (*fmt);
    250 				break;
    251 			}
    252 		}
    253 	} while (gargv > argv && *gargv);
    254 
    255 	return (rval);
    256 }
    257 
    258 
    259 /*
    260  * Print SysV echo(1) style escape string
    261  *	Halts processing string and returns 1 if a \c escape is encountered.
    262  */
    263 static int
    264 print_escape_str(str)
    265 	register const char *str;
    266 {
    267 	int value;
    268 	int c;
    269 
    270 	while (*str) {
    271 		if (*str == '\\') {
    272 			str++;
    273 			/*
    274 			 * %b string octal constants are not like those in C.
    275 			 * They start with a \0, and are followed by 0, 1, 2,
    276 			 * or 3 octal digits.
    277 			 */
    278 			if (*str == '0') {
    279 				str++;
    280 				for (c = 3, value = 0; c-- && isodigit(*str); str++) {
    281 					value <<= 3;
    282 					value += octtobin(*str);
    283 				}
    284 				putchar (value);
    285 				str--;
    286 			} else if (*str == 'c') {
    287 				return 1;
    288 			} else {
    289 				str--;
    290 				str += print_escape(str);
    291 			}
    292 		} else {
    293 			putchar (*str);
    294 		}
    295 		str++;
    296 	}
    297 
    298 	return 0;
    299 }
    300 
    301 /*
    302  * Print "standard" escape characters
    303  */
    304 static int
    305 print_escape(str)
    306 	register const char *str;
    307 {
    308 	const char *start = str;
    309 	int value;
    310 	int c;
    311 
    312 	str++;
    313 
    314 	switch (*str) {
    315 	case '0': case '1': case '2': case '3':
    316 	case '4': case '5': case '6': case '7':
    317 		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
    318 			value <<= 3;
    319 			value += octtobin(*str);
    320 		}
    321 		putchar(value);
    322 		return str - start - 1;
    323 		/* NOTREACHED */
    324 
    325 	case 'x':
    326 		str++;
    327 		for (value = 0; isxdigit(*str); str++) {
    328 			value <<= 4;
    329 			value += hextobin(*str);
    330 		}
    331 		if (value > UCHAR_MAX) {
    332 			warnx ("escape sequence out of range for character");
    333 			rval = 1;
    334 		}
    335 		putchar (value);
    336 		return str - start - 1;
    337 		/* NOTREACHED */
    338 
    339 	case '\\':			/* backslash */
    340 		putchar('\\');
    341 		break;
    342 
    343 	case '\'':			/* single quote */
    344 		putchar('\'');
    345 		break;
    346 
    347 	case '"':			/* double quote */
    348 		putchar('"');
    349 		break;
    350 
    351 	case 'a':			/* alert */
    352 #ifdef __STDC__
    353 		putchar('\a');
    354 #else
    355 		putchar(007);
    356 #endif
    357 		break;
    358 
    359 	case 'b':			/* backspace */
    360 		putchar('\b');
    361 		break;
    362 
    363 	case 'e':			/* escape */
    364 #ifdef __GNUC__
    365 		putchar('\e');
    366 #else
    367 		putchar(033);
    368 #endif
    369 		break;
    370 
    371 	case 'f':			/* form-feed */
    372 		putchar('\f');
    373 		break;
    374 
    375 	case 'n':			/* newline */
    376 		putchar('\n');
    377 		break;
    378 
    379 	case 'r':			/* carriage-return */
    380 		putchar('\r');
    381 		break;
    382 
    383 	case 't':			/* tab */
    384 		putchar('\t');
    385 		break;
    386 
    387 	case 'v':			/* vertical-tab */
    388 		putchar('\v');
    389 		break;
    390 
    391 	default:
    392 		putchar(*str);
    393 		warnx("unknown escape sequence `\\%c'", *str);
    394 		rval = 1;
    395 	}
    396 
    397 	return 1;
    398 }
    399 
    400 static char *
    401 mklong(str, ch)
    402 	char *str, ch;
    403 {
    404 	static char copy[64];
    405 	int len;
    406 
    407 	len = strlen(str) + 2;
    408 	(void) memmove(copy, str, len - 3);
    409 	copy[len - 3] = 'l';
    410 	copy[len - 2] = ch;
    411 	copy[len - 1] = '\0';
    412 	return (copy);
    413 }
    414 
    415 static int
    416 getchr()
    417 {
    418 	if (!*gargv)
    419 		return((int)'\0');
    420 	return((int)**gargv++);
    421 }
    422 
    423 static char *
    424 getstr()
    425 {
    426 	if (!*gargv)
    427 		return("");
    428 	return(*gargv++);
    429 }
    430 
    431 static char *number = "+-.0123456789";
    432 static int
    433 getint()
    434 {
    435 	if (!*gargv)
    436 		return(0);
    437 
    438 	if (index(number, **gargv))
    439 		return(atoi(*gargv++));
    440 
    441 	return 0;
    442 }
    443 
    444 static long
    445 getlong()
    446 {
    447 	long val;
    448 	char *ep;
    449 
    450 	if (!*gargv)
    451 		return(0L);
    452 
    453 	if (**gargv == '\"' || **gargv == '\'') {
    454 		val = gargv[0][1];
    455 		gargv++;
    456 		return val;
    457 	}
    458 
    459 	val = strtol (*gargv, &ep, 0);
    460 	if (*ep) {
    461 		warnx ("incompletely converted argument: %s", *gargv);
    462 		rval = 1;
    463 	}
    464 
    465 	gargv++;
    466 	return val;
    467 }
    468 
    469 static double
    470 getdouble()
    471 {
    472 	double val;
    473 	char *ep;
    474 
    475 	if (!*gargv)
    476 		return(0.0);
    477 
    478 	if (**gargv == '\"' || **gargv == '\'') {
    479 		val = gargv[0][1];
    480 		gargv++;
    481 		return val;
    482 	}
    483 
    484 	val = strtod (*gargv, &ep);
    485 	if (*ep) {
    486 		warnx ("incompletely converted argument: %s", *gargv);
    487 		rval = 1;
    488 	}
    489 
    490 	gargv++;
    491 	return val;
    492 }
    493 
    494 static void
    495 usage()
    496 {
    497 	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
    498 }
    499