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