Home | History | Annotate | Line # | Download | only in printf
printf.c revision 1.6
      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.6 1993/11/19 20:50:27 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 void	 print_escape_str();
     56 static int	 print_escape();
     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 	static char *skip1, *skip2;
    130 	register char *fmt, *start;
    131 	register int fieldwidth, precision;
    132 	char convch, nextch;
    133 	char *format;
    134 	int ch;
    135 
    136 #if !defined(SHELL) && !defined(BUILTIN)
    137 	setlocale (LC_ALL, "");
    138 #endif
    139 
    140 	while ((ch = getopt(argc, argv, "")) != -1) {
    141 		switch (ch) {
    142 		case '?':
    143 		default:
    144 			usage();
    145 			return (1);
    146 		}
    147 	}
    148 	argc -= optind;
    149 	argv += optind;
    150 
    151 	if (argc < 1) {
    152 		usage();
    153 		return (1);
    154 	}
    155 
    156 	format = *argv;
    157 	gargv = ++argv;
    158 
    159 	do {
    160 		/*
    161 		 * Basic algorithm is to scan the format string for conversion
    162 		 * specifications -- once one is found, find out if the field
    163 		 * width or precision is a '*'; if it is, gather up value.
    164 		 * Note, format strings are reused as necessary to use up the
    165 		 * provided arguments, arguments of zero/null string are
    166 		 * provided to use up the format string.
    167 		 */
    168 		skip1 = "#-+ 0";
    169 		skip2 = "*0123456789";
    170 
    171 		/* find next format specification */
    172 		for (fmt = format; *fmt; fmt++) {
    173 			switch (*fmt) {
    174 			case '%':
    175 				start = fmt++;
    176 
    177 				if (*fmt == '%') {
    178 					putchar ('%');
    179 					break;
    180 				} else if (*fmt == 'b') {
    181 					char *p = getstr();
    182 					print_escape_str(p);
    183 					break;
    184 				}
    185 
    186 				/* skip to field width */
    187 				for (; index(skip1, *fmt); ++fmt);
    188 				fieldwidth = *fmt == '*' ? getint() : 0;
    189 
    190 				/* skip to possible '.', get following precision */
    191 				for (; index(skip2, *fmt); ++fmt);
    192 				if (*fmt == '.')
    193 					++fmt;
    194 				precision = *fmt == '*' ? getint() : 0;
    195 
    196 				for (; index(skip2, *fmt); ++fmt);
    197 				if (!*fmt) {
    198 					warnx ("missing format character");
    199 					rval = 1;
    200 					return;
    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': case 'i': case 'o': case 'u': case 'x': case 'X': {
    218 					char *f = mklong(start, convch);
    219 					long p = getlong();
    220 					PF(f, p);
    221 					free(f);
    222 					break;
    223 				}
    224 				case 'e': case 'E': case 'f': case 'g': case 'G': {
    225 					double p = getdouble();
    226 					PF(start, p);
    227 					break;
    228 				}
    229 				default:
    230 					warnx ("%s: invalid directive", start);
    231 					rval = 1;
    232 				}
    233 				*(fmt + 1) = nextch;
    234 				break;
    235 
    236 			case '\\':
    237 				fmt += print_escape(fmt);
    238 				break;
    239 
    240 			default:
    241 				putchar (*fmt);
    242 				break;
    243 			}
    244 		}
    245 	} while (gargv > argv && *gargv);
    246 
    247 	return (rval);
    248 }
    249 
    250 
    251 /*
    252  * Print SysV echo(1) style escape string
    253  */
    254 static void
    255 print_escape_str(str)
    256 	register char *str;
    257 {
    258 	int value;
    259 	int c;
    260 
    261 	while (*str) {
    262 		if (*str == '\\') {
    263 			str++;
    264 			/*
    265 			 * %b string octal constants are not like those in C.
    266 			 * They start with a \0, and are followed by 0, 1, 2,
    267 			 * or 3 octal digits.
    268 			 */
    269 			if (*str == '0') {
    270 				str++;
    271 				for (c = 3, value = 0; c-- && isodigit(*str); str++) {
    272 					value <<= 3;
    273 					value += octtobin(*str);
    274 				}
    275 				putchar (value);
    276 				str--;
    277 			} else if (*str == 'c') {
    278 				exit (rval);
    279 			} else {
    280 				str--;
    281 				str += print_escape(str);
    282 			}
    283 		} else {
    284 			putchar (*str);
    285 		}
    286 		str++;
    287 	}
    288 }
    289 
    290 /*
    291  * Print "standard" escape characters
    292  */
    293 static int
    294 print_escape(str)
    295 	register char *str;
    296 {
    297 	char *start = str;
    298 	int c;
    299 	int value;
    300 
    301 	str++;
    302 
    303 	switch (*str) {
    304 	case '0': case '1': case '2': case '3':
    305 	case '4': case '5': case '6': case '7':
    306 		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
    307 			value <<= 3;
    308 			value += octtobin(*str);
    309 		}
    310 		putchar(value);
    311 		return str - start - 1;
    312 		/* NOTREACHED */
    313 
    314 	case 'x':
    315 		str++;
    316 		for (value = 0; isxdigit(*str); str++) {
    317 			value <<= 4;
    318 			value += hextobin(*str);
    319 		}
    320 		if (value > UCHAR_MAX) {
    321 			warnx ("escape sequence out of range for character");
    322 			rval = 1;
    323 		}
    324 		putchar (value);
    325 		return str - start - 1;
    326 		/* NOTREACHED */
    327 
    328 	case '\\':			/* backslash */
    329 		putchar('\\');
    330 		break;
    331 
    332 	case '\'':			/* single quote */
    333 		putchar('\'');
    334 		break;
    335 
    336 	case '"':			/* double quote */
    337 		putchar('"');
    338 		break;
    339 
    340 	case 'a':			/* alert */
    341 #ifdef __STDC__
    342 		putchar('\a');
    343 #else
    344 		putchar(007);
    345 #endif
    346 		break;
    347 
    348 	case 'b':			/* backspace */
    349 		putchar('\b');
    350 		break;
    351 
    352 	case 'e':			/* escape */
    353 #ifdef __GNUC__
    354 		putchar('\e');
    355 #else
    356 		putchar(033);
    357 #endif
    358 		break;
    359 
    360 	case 'f':			/* form-feed */
    361 		putchar('\f');
    362 		break;
    363 
    364 	case 'n':			/* newline */
    365 		putchar('\n');
    366 		break;
    367 
    368 	case 'r':			/* carriage-return */
    369 		putchar('\r');
    370 		break;
    371 
    372 	case 't':			/* tab */
    373 		putchar('\t');
    374 		break;
    375 
    376 	case 'v':			/* vertical-tab */
    377 		putchar('\v');
    378 		break;
    379 
    380 	default:
    381 		putchar(*str);
    382 		warnx("unknown escape sequence `\\%c'", *str);
    383 		rval = 1;
    384 	}
    385 
    386 	return 1;
    387 }
    388 
    389 static char *
    390 mklong(str, ch)
    391 	char *str, ch;
    392 {
    393 	static char copy[64];
    394 	int len;
    395 
    396 	len = strlen(str) + 2;
    397 	memmove(copy, str, len - 3);
    398 	copy[len - 3] = 'l';
    399 	copy[len - 2] = ch;
    400 	copy[len - 1] = '\0';
    401 	return (copy);
    402 }
    403 
    404 static int
    405 getchr()
    406 {
    407 	if (!*gargv)
    408 		return((int)'\0');
    409 	return((int)**gargv++);
    410 }
    411 
    412 static char *
    413 getstr()
    414 {
    415 	if (!*gargv)
    416 		return("");
    417 	return(*gargv++);
    418 }
    419 
    420 static char *number = "+-.0123456789";
    421 static int
    422 getint()
    423 {
    424 	if (!*gargv)
    425 		return(0);
    426 
    427 	if (index(number, **gargv))
    428 		return(atoi(*gargv++));
    429 
    430 	return 0;
    431 }
    432 
    433 static long
    434 getlong()
    435 {
    436 	long val;
    437 	char *ep;
    438 
    439 	if (!*gargv)
    440 		return(0L);
    441 
    442 	if (**gargv == '\"' || **gargv == '\'') {
    443 		val = gargv[0][1];
    444 		gargv++;
    445 		return val;
    446 	}
    447 
    448 	val = strtol (*gargv, &ep, 0);
    449 	if (*ep) {
    450 		warnx ("incompletely converted argument: %s", *gargv);
    451 		rval = 1;
    452 	}
    453 
    454 	gargv++;
    455 	return val;
    456 }
    457 
    458 static double
    459 getdouble()
    460 {
    461 	double val;
    462 	char *ep;
    463 
    464 	if (!*gargv)
    465 		return(0.0);
    466 
    467 	if (**gargv == '\"' || **gargv == '\'') {
    468 		val = gargv[0][1];
    469 		gargv++;
    470 		return val;
    471 	}
    472 
    473 	val = strtod (*gargv, &ep);
    474 	if (*ep) {
    475 		warnx ("incompletely converted argument: %s", *gargv);
    476 		rval = 1;
    477 	}
    478 
    479 	gargv++;
    480 	return val;
    481 }
    482 
    483 static void
    484 usage()
    485 {
    486 	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
    487 }
    488