morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base.git
Log | Files | Refs

printf.c (9744B)


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