Home | History | Annotate | Line # | Download | only in libterminfo
tparm.c revision 1.11
      1 /* $NetBSD: tparm.c,v 1.11 2013/01/24 10:28:28 roy Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc.
      5  *
      6  * This code is derived from software contributed to The NetBSD Foundation
      7  * by Roy Marples.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __RCSID("$NetBSD: tparm.c,v 1.11 2013/01/24 10:28:28 roy Exp $");
     32 #include <sys/param.h>
     33 
     34 #include <assert.h>
     35 #include <ctype.h>
     36 #include <errno.h>
     37 #include <stdarg.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <term_private.h>
     42 #include <term.h>
     43 
     44 #define LONG_STR_MAX ((CHAR_BIT * sizeof(long)) / 3)
     45 #define BUFINC 128 /* Size to increament the terminal buffer by */
     46 
     47 static TERMINAL *dumbterm; /* For non thread safe functions */
     48 
     49 typedef struct {
     50 	long nums[20];
     51 	char *strings[20];
     52 	size_t offset;
     53 } TPSTACK;
     54 
     55 typedef struct {
     56 	long num;
     57 	char *string;
     58 } TPVAR;
     59 
     60 static int
     61 push(long num, char *string, TPSTACK *stack)
     62 {
     63 	if (stack->offset >= sizeof(stack->nums)) {
     64 		errno = E2BIG;
     65 		return -1;
     66 	}
     67 	stack->nums[stack->offset] = num;
     68 	stack->strings[stack->offset] = string;
     69 	stack->offset++;
     70 	return 0;
     71 }
     72 
     73 static int
     74 pop(long *num, char **string, TPSTACK *stack)
     75 {
     76 	if (stack->offset == 0) {
     77 		if (num)
     78 			*num = 0;
     79 		if (string)
     80 			*string = NULL;
     81 		errno = E2BIG;
     82 		return -1;
     83 	}
     84 	stack->offset--;
     85 	if (num)
     86 		*num = stack->nums[stack->offset];
     87 	if (string)
     88 		*string = stack->strings[stack->offset];
     89 	return 0;
     90 }
     91 
     92 static char *
     93 checkbuf(TERMINAL *term, size_t len)
     94 {
     95 	char *buf;
     96 
     97 	if (term->_bufpos + len >= term->_buflen) {
     98 		len = term->_buflen + MAX(len, BUFINC);
     99 		buf = realloc(term->_buf, len);
    100 		if (buf == NULL)
    101 			return NULL;
    102 		term->_buf = buf;
    103 		term->_buflen = len;
    104 	}
    105 	return term->_buf;
    106 }
    107 
    108 static size_t
    109 ochar(TERMINAL *term, int c)
    110 {
    111 	if (c == 0)
    112 		c = 0200;
    113 	/* Check we have space and a terminator */
    114 	if (checkbuf(term, 2) == NULL)
    115 		return 0;
    116 	term->_buf[term->_bufpos++] = (char)c;
    117 	return 1;
    118 }
    119 
    120 static size_t
    121 onum(TERMINAL *term, const char *fmt, int num, unsigned int len)
    122 {
    123 	size_t l;
    124 
    125 	if (len < LONG_STR_MAX)
    126 		len = LONG_STR_MAX;
    127 	if (checkbuf(term, len + 2) == NULL)
    128 		return 0;
    129 	l = sprintf(term->_buf + term->_bufpos, fmt, num);
    130 	term->_bufpos += l;
    131 	return l;
    132 }
    133 
    134 static char *
    135 _ti_tiparm(TERMINAL *term, const char *str, int va_long, va_list parms)
    136 {
    137 	const char *sp;
    138 	char c, fmt[64], *fp, *ostr;
    139 	long val, val2;
    140 	long dnums[26]; /* dynamic variables a-z, not preserved */
    141 	size_t l, max;
    142 	TPSTACK stack;
    143 	TPVAR params[9];
    144 	unsigned int done, dot, minus, width, precision, olen;
    145 	int piss[9]; /* Parameter IS String - piss ;) */
    146 
    147 	if (str == NULL)
    148 		return NULL;
    149 
    150 	/*
    151 	  If not passed a terminal, malloc a dummy one.
    152 	  This means we can preserve buffers and variables per terminal and
    153 	  still work with non thread safe functions (which sadly are still the
    154 	  norm and standard).
    155 	*/
    156 	if (term == NULL) {
    157 		if (dumbterm == NULL) {
    158 			dumbterm = malloc(sizeof(*dumbterm));
    159 			if (dumbterm == NULL)
    160 				return NULL;
    161 			dumbterm->_buflen = 0;
    162 		}
    163 		term = dumbterm;
    164 	}
    165 
    166 	term->_bufpos = 0;
    167 	/* Ensure we have an initial buffer */
    168 	if (term->_buflen == 0) {
    169 		term->_buf = malloc(BUFINC);
    170 		if (term->_buf == NULL)
    171 			return NULL;
    172 		term->_buflen = BUFINC;
    173 	}
    174 
    175 	/*
    176 	  Make a first pass through the string so we can work out
    177 	  which parameters are ints and which are char *.
    178 	  Basically we only use char * if %p[1-9] is followed by %l or %s.
    179 	*/
    180 	memset(&piss, 0, sizeof(piss));
    181 	max = 0;
    182 	sp = str;
    183 	while ((c = *sp++) != '\0') {
    184 		if (c != '%')
    185 			continue;
    186 		c = *sp++;
    187 		if (c == '\0')
    188 			break;
    189 		if (c != 'p')
    190 			continue;
    191 		c = *sp++;
    192 		if (c < '1' || c > '9') {
    193 			errno = EINVAL;
    194 			continue;
    195 		}
    196 		l = c - '0';
    197 		if (l > max)
    198 			max = l;
    199 		if (*sp != '%')
    200 			continue;
    201 		/* Skip formatting */
    202 		sp++;
    203 		while (*sp == '.' || *sp == '#' || *sp == ' ' || *sp == ':' ||
    204 		    *sp == '-' || isdigit((unsigned char)*sp))
    205 			sp++;
    206 		if (*sp == 'l' || *sp == 's')
    207 			piss[l - 1] = 1;
    208 	}
    209 
    210 	/* Put our parameters into variables */
    211 	memset(&params, 0, sizeof(params));
    212 	for (l = 0; l < max; l++) {
    213 		if (piss[l])
    214 			params[l].string = va_arg(parms, char *);
    215 		else if (va_long)
    216 			params[l].num = va_arg(parms, long);
    217 		else
    218 			params[l].num = (long)va_arg(parms, int);
    219 	}
    220 
    221 	memset(&stack, 0, sizeof(stack));
    222 	while ((c = *str++) != '\0') {
    223 		if (c != '%' || (c = *str++) == '%') {
    224 			if (c == '\0')
    225 				break;
    226 			if (ochar(term, c) == 0)
    227 				return NULL;
    228 			continue;
    229 		}
    230 
    231 		/* Handle formatting. */
    232 		fp = fmt;
    233 		*fp++ = '%';
    234 		done = dot = minus = width = precision = 0;
    235 		val = 0;
    236 		while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) {
    237 			switch (c) {
    238 			case 'c': /* FALLTHROUGH */
    239 			case 's':
    240 				*fp++ = c;
    241 				done = 1;
    242 				break;
    243 			case 'd': /* FALLTHROUGH */
    244 			case 'o': /* FALLTHROUGH */
    245 			case 'x': /* FALLTHROUGH */
    246 			case 'X': /* FALLTHROUGH */
    247 				*fp++ = 'l';
    248 				*fp++ = c;
    249 				done = 1;
    250 				break;
    251 			case '#': /* FALLTHROUGH */
    252 			case ' ':
    253 				*fp++ = c;
    254 				break;
    255 			case '.':
    256 				*fp++ = c;
    257 				if (dot == 0) {
    258 					dot = 1;
    259 					width = val;
    260 				} else
    261 					done = 2;
    262 				val = 0;
    263 				break;
    264 			case ':':
    265 				minus = 1;
    266 				break;
    267 			case '-':
    268 				if (minus)
    269 					*fp++ = c;
    270 				else
    271 					done = 1;
    272 				break;
    273 			default:
    274 				if (isdigit((unsigned char)c)) {
    275 					val = (val * 10) + (c - '0');
    276 					if (val > 10000)
    277 						done = 2;
    278 					else
    279 						*fp++ = c;
    280 				} else
    281 					done = 1;
    282 			}
    283 			if (done == 0)
    284 				c = *str++;
    285 		}
    286 		if (done == 2) {
    287 			/* Found an error in the format */
    288 			fp = fmt + 1;
    289 			*fp = *str;
    290 			olen = 0;
    291 		} else {
    292 			if (dot == 0)
    293 				width = val;
    294 			else
    295 				precision = val;
    296 			olen = MAX(width, precision);
    297 		}
    298 		*fp++ = '\0';
    299 
    300 		/* Handle commands */
    301 		switch (c) {
    302 		case 'c':
    303 			pop(&val, NULL, &stack);
    304 			if (ochar(term, (unsigned char)val) == 0)
    305 				return NULL;
    306 			break;
    307 		case 's':
    308 			pop(NULL, &ostr, &stack);
    309 			if (ostr != NULL) {
    310 				l = strlen(ostr);
    311 				if (l < (size_t)olen)
    312 					l = olen;
    313 				if (checkbuf(term, (size_t)(l + 1)) == NULL)
    314 					return NULL;
    315 				l = sprintf(term->_buf + term->_bufpos,
    316 				    fmt, ostr);
    317 				term->_bufpos += l;
    318 			}
    319 			break;
    320 		case 'l':
    321 			pop(NULL, &ostr, &stack);
    322 			if (ostr == NULL)
    323 				l = 0;
    324 			else
    325 				l = strlen(ostr);
    326 #ifdef NCURSES_COMPAT_57
    327 			if (onum(term, "%ld", (long)l, 0) == 0)
    328 				return NULL;
    329 #else
    330 			push((long)l, NULL, &stack);
    331 #endif
    332 			break;
    333 		case 'd': /* FALLTHROUGH */
    334 		case 'o': /* FALLTHROUGH */
    335 		case 'x': /* FALLTHROUGH */
    336 		case 'X':
    337 			pop(&val, NULL, &stack);
    338 			if (onum(term, fmt, (int)val, olen) == 0)
    339 				return NULL;
    340 			break;
    341 		case 'p':
    342 			if (*str < '1' || *str > '9')
    343 				break;
    344 			l = *str++ - '1';
    345 			if (push(params[l].num, params[l].string, &stack))
    346 				return NULL;
    347 			break;
    348 		case 'P':
    349 			pop(&val, NULL, &stack);
    350 			if (*str >= 'a' && *str <= 'z')
    351 				dnums[*str - 'a'] = val;
    352 			else if (*str >= 'A' && *str <= 'Z')
    353 				term->_snums[*str - 'A'] = val;
    354 			break;
    355 		case 'g':
    356 			if (*str >= 'a' && *str <= 'z') {
    357 				if (push(dnums[*str - 'a'], NULL, &stack))
    358 					return NULL;
    359 			} else if (*str >= 'A' && *str <= 'Z') {
    360 				if (push(term->_snums[*str - 'A'],
    361 					NULL, &stack))
    362 					return NULL;
    363 			}
    364 			break;
    365 		case 'i':
    366 			if (piss[0] == 0)
    367 				params[0].num++;
    368 			if (piss[1] == 0)
    369 				params[1].num++;
    370 			break;
    371 		case '\'':
    372 			if (push((long)(unsigned char)*str++, NULL, &stack))
    373 				return NULL;
    374 			while (*str != '\0' && *str != '\'')
    375 				str++;
    376 			if (*str == '\'')
    377 				str++;
    378 			break;
    379 		case '{':
    380 			val = 0;
    381 			for (; isdigit((unsigned char)*str);  str++)
    382 				val = (val * 10) + (*str - '0');
    383 			if (push(val, NULL, &stack))
    384 				return NULL;
    385 			while (*str != '\0' && *str != '}')
    386 				str++;
    387 			if (*str == '}')
    388 				str++;
    389 			break;
    390 		case '+': /* FALLTHROUGH */
    391 		case '-': /* FALLTHROUGH */
    392 		case '*': /* FALLTHROUGH */
    393 		case '/': /* FALLTHROUGH */
    394 		case 'm': /* FALLTHROUGH */
    395 		case 'A': /* FALLTHROUGH */
    396 		case 'O': /* FALLTHROUGH */
    397 		case '&': /* FALLTHROUGH */
    398 		case '|': /* FALLTHROUGH */
    399 		case '^': /* FALLTHROUGH */
    400 		case '=': /* FALLTHROUGH */
    401 		case '<': /* FALLTHROUGH */
    402 		case '>':
    403 			pop(&val, NULL, &stack);
    404 			pop(&val2, NULL, &stack);
    405 			switch (c) {
    406 			case '+':
    407 				val = val + val2;
    408 				break;
    409 			case '-':
    410 				val = val2 - val;
    411 				break;
    412 			case '*':
    413 				val = val * val2;
    414 				break;
    415 			case '/':
    416 				val = val ? val2 / val : 0;
    417 				break;
    418 			case 'm':
    419 				val = val ? val2 % val : 0;
    420 				break;
    421 			case 'A':
    422 				val = val && val2;
    423 				break;
    424 			case 'O':
    425 				val = val || val2;
    426 				break;
    427 			case '&':
    428 				val = val & val2;
    429 				break;
    430 			case '|':
    431 				val = val | val2;
    432 				break;
    433 			case '^':
    434 				val = val ^ val2;
    435 				break;
    436 			case '=':
    437 				val = val == val2;
    438 				break;
    439 			case '<':
    440 				val = val2 < val;
    441 				break;
    442 			case '>':
    443 				val = val2 > val;
    444 				break;
    445 			}
    446 			if (push(val, NULL, &stack))
    447 				return NULL;
    448 			break;
    449 		case '!':
    450 		case '~':
    451 			pop(&val, NULL, &stack);
    452 			switch (c) {
    453 			case '!':
    454 				val = !val;
    455 				break;
    456 			case '~':
    457 				val = ~val;
    458 				break;
    459 			}
    460 			if (push(val, NULL, &stack))
    461 				return NULL;
    462 			break;
    463 		case '?': /* if */
    464 			break;
    465 		case 't': /* then */
    466 			pop(&val, NULL, &stack);
    467 			if (val == 0) {
    468 				l = 0;
    469 				for (; *str != '\0'; str++) {
    470 					if (*str != '%')
    471 						continue;
    472 					str++;
    473 					if (*str == '?')
    474 						l++;
    475 					else if (*str == ';') {
    476 						if (l > 0)
    477 							l--;
    478 						else {
    479 							str++;
    480 							break;
    481 						}
    482 					} else if (*str == 'e' && l == 0) {
    483 						str++;
    484 						break;
    485 					}
    486 				}
    487 			}
    488 			break;
    489 		case 'e': /* else */
    490 			l = 0;
    491 			for (; *str != '\0'; str++) {
    492 				if (*str != '%')
    493 					continue;
    494 				str++;
    495 				if (*str == '?')
    496 					l++;
    497 				else if (*str == ';') {
    498 					if (l > 0)
    499 						l--;
    500 					else {
    501 						str++;
    502 						break;
    503 					}
    504 				}
    505 			}
    506 			break;
    507 		case ';': /* fi */
    508 			break;
    509 		}
    510 	}
    511 	term->_buf[term->_bufpos] = '\0';
    512 	return term->_buf;
    513 }
    514 
    515 char *
    516 ti_tiparm(TERMINAL *term, const char *str, ...)
    517 {
    518 	va_list va;
    519 	char *ret;
    520 
    521 	_DIAGASSERT(term != NULL);
    522 	_DIAGASSERT(str != NULL);
    523 
    524 	va_start(va, str);
    525 	ret = _ti_tiparm(term, str, 0, va);
    526 	va_end(va);
    527 	return ret;
    528 }
    529 
    530 char *
    531 tiparm(const char *str, ...)
    532 {
    533 	va_list va;
    534 	char *ret;
    535 
    536 	_DIAGASSERT(str != NULL);
    537 
    538 	va_start(va, str);
    539 	ret = _ti_tiparm(NULL, str, 0, va);
    540 	va_end(va);
    541 	return ret;
    542 }
    543 
    544 /* Same as tiparm, but accepts long instead of int for the numeric params.
    545  * Currently there is no need for this to be a public interface and is only
    546  * consumed by tparm. If we need this to be public, and I really cannot
    547  * imagine why, then we would need ti_tlparm() as well. */
    548 static char *
    549 tlparm(const char *str, ...)
    550 {
    551 	va_list va;
    552 	char *ret;
    553 
    554 	_DIAGASSERT(str != NULL);
    555 
    556 	va_start(va, str);
    557 	ret = _ti_tiparm(NULL, str, 1, va);
    558 	va_end(va);
    559 	return ret;
    560 }
    561 
    562 char *
    563 tparm(const char *str,
    564     long p1, long p2, long p3, long p4, long p5,
    565     long p6, long p7, long p8, long p9)
    566 {
    567 
    568 	return tlparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
    569 }
    570