Home | History | Annotate | Line # | Download | only in libterminfo
tparm.c revision 1.7.4.3
      1 /* $NetBSD: tparm.c,v 1.7.4.3 2013/03/14 15:48:29 riz Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2009, 2011, 2013 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.7.4.3 2013/03/14 15:48:29 riz Exp $");
     32 
     33 #include <assert.h>
     34 #include <ctype.h>
     35 #include <errno.h>
     36 #include <stdarg.h>
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <term_private.h>
     41 #include <term.h>
     42 
     43 #define LONG_STR_MAX ((CHAR_BIT * sizeof(long)) / 3)
     44 #define BUFINC 128	/* Size to increament the terminal buffer by */
     45 
     46 #define VA_LONG_LONG	1
     47 #define VA_CHAR_INT	2
     48 //#define VA_CHAR_LONG	3
     49 
     50 static TERMINAL *dumbterm; /* For non thread safe functions */
     51 
     52 typedef struct {
     53 	long nums[20];
     54 	char *strings[20];
     55 	size_t offset;
     56 } TPSTACK;
     57 
     58 typedef struct {
     59 	long num;
     60 	char *string;
     61 } TPVAR;
     62 
     63 static int
     64 push(long num, char *string, TPSTACK *stack)
     65 {
     66 	if (stack->offset >= sizeof(stack->nums)) {
     67 		errno = E2BIG;
     68 		return -1;
     69 	}
     70 	stack->nums[stack->offset] = num;
     71 	stack->strings[stack->offset] = string;
     72 	stack->offset++;
     73 	return 0;
     74 }
     75 
     76 static int
     77 pop(long *num, char **string, TPSTACK *stack)
     78 {
     79 	if (stack->offset == 0) {
     80 		if (num)
     81 			*num = 0;
     82 		if (string)
     83 			*string = NULL;
     84 		errno = E2BIG;
     85 		return -1;
     86 	}
     87 	stack->offset--;
     88 	if (num)
     89 		*num = stack->nums[stack->offset];
     90 	if (string)
     91 		*string = stack->strings[stack->offset];
     92 	return 0;
     93 }
     94 
     95 static char *
     96 checkbuf(TERMINAL *term, size_t len)
     97 {
     98 	char *buf;
     99 
    100 	if (term->_bufpos + len >= term->_buflen) {
    101 		len = term->_buflen + BUFSIZ;
    102 		buf = realloc(term->_buf, len);
    103 		if (buf == NULL)
    104 			return 0;
    105 		term->_buf = buf;
    106 		term->_buflen = len;
    107 	}
    108 	return term->_buf;
    109 }
    110 
    111 static size_t
    112 ochar(TERMINAL *term, int c)
    113 {
    114 	if (c == 0)
    115 		c = 0200;
    116 	/* Check we have space and a terminator */
    117 	if (checkbuf(term, 2) == NULL)
    118 		return 0;
    119 	term->_buf[term->_bufpos++] = (char)c;
    120 	return 1;
    121 }
    122 
    123 static size_t
    124 onum(TERMINAL *term, const char *fmt, int num, unsigned int len)
    125 {
    126 	size_t l;
    127 
    128 	if (len < LONG_STR_MAX)
    129 		len = LONG_STR_MAX;
    130 	if (checkbuf(term, len + 2) == NULL)
    131 		return 0;
    132 	l = sprintf(term->_buf + term->_bufpos, fmt, num);
    133 	term->_bufpos += l;
    134 	return l;
    135 }
    136 
    137 /*
    138   Make a pass through the string so we can work out
    139   which parameters are ints and which are char *.
    140   Basically we only use char * if %p[1-9] is followed by %l or %s.
    141 */
    142 int
    143 _ti_parm_analyse(const char *str, int *piss, int piss_len)
    144 {
    145 	int nparm, lpop;
    146 	char c;
    147 
    148 	nparm = 0;
    149 	lpop = -1;
    150 	while ((c = *str++) != '\0') {
    151 		if (c != '%')
    152 			continue;
    153 		c = *str++;
    154 		switch (c) {
    155 			case 'l': /* FALLTHROUGH */
    156 			case 's':
    157 				if (lpop > 0) {
    158 					if (lpop <= piss_len)
    159 						piss[lpop - 1] = 1;
    160 					else if (piss)
    161 						errno = E2BIG;
    162 				}
    163 				break;
    164 			case 'p':
    165 				c = *str++;
    166 				if (c < '1' || c > '9') {
    167 					errno = EINVAL;
    168 					continue;
    169 				} else {
    170 					lpop = c - '0';
    171 					if (lpop > nparm)
    172 						nparm = lpop;
    173 				}
    174 				break;
    175 			default:
    176 				lpop = -1;
    177 		}
    178 	}
    179 
    180 	return nparm;
    181 }
    182 
    183 static char *
    184 _ti_tiparm(TERMINAL *term, const char *str, int va_type, va_list parms)
    185 {
    186 	char c, fmt[64], *fp, *ostr;
    187 	long val, val2;
    188 	long dnums[26]; /* dynamic variables a-z, not preserved */
    189 	size_t l, max;
    190 	TPSTACK stack;
    191 	TPVAR params[TPARM_MAX];
    192 	unsigned int done, dot, minus, width, precision, olen;
    193 	int piss[TPARM_MAX]; /* Parameter IS String - piss ;) */
    194 
    195 	if (str == NULL)
    196 		return NULL;
    197 
    198 	/*
    199 	  If not passed a terminal, malloc a dummy one.
    200 	  This means we can preserve buffers and variables per terminal and
    201 	  still work with non thread safe functions (which sadly are still the
    202 	  norm and standard).
    203 	*/
    204 	if (term == NULL) {
    205 		if (dumbterm == NULL) {
    206 			dumbterm = malloc(sizeof(*dumbterm));
    207 			if (dumbterm == NULL)
    208 				return NULL;
    209 			dumbterm->_buflen = 0;
    210 		}
    211 		term = dumbterm;
    212 	}
    213 
    214 	term->_bufpos = 0;
    215 	/* Ensure we have an initial buffer */
    216 	if (term->_buflen == 0) {
    217 		term->_buf = malloc(BUFINC);
    218 		if (term->_buf == NULL)
    219 			return NULL;
    220 		term->_buflen = BUFINC;
    221 	}
    222 
    223 	memset(&piss, 0, sizeof(piss));
    224 	max = _ti_parm_analyse(str, piss, TPARM_MAX);
    225 
    226 	/* Put our parameters into variables */
    227 	memset(&params, 0, sizeof(params));
    228 	for (l = 0; l < max; l++) {
    229 		if (piss[l]) {
    230 			if (va_type == VA_LONG_LONG) {
    231 				/* This only works if char * fits into a long
    232 				 * on this platform. */
    233 				if (sizeof(char *) <= sizeof(long)/*CONSTCOND*/)
    234 					params[l].string =
    235 					    (char *)va_arg(parms, long);
    236 				else {
    237 					errno = ENOTSUP;
    238 					return NULL;
    239 				}
    240 			} else
    241 				params[l].string = va_arg(parms, char *);
    242 		} else {
    243 			if (va_type == VA_CHAR_INT)
    244 				params[l].num = (long)va_arg(parms, int);
    245 			else
    246 				params[l].num = va_arg(parms, long);
    247 		}
    248 	}
    249 
    250 	memset(&stack, 0, sizeof(stack));
    251 	while ((c = *str++) != '\0') {
    252 		if (c != '%' || (c = *str++) == '%') {
    253 			if (c == '\0')
    254 				break;
    255 			if (ochar(term, c) == 0)
    256 				return NULL;
    257 			continue;
    258 		}
    259 
    260 		/* Handle formatting. */
    261 		fp = fmt;
    262 		*fp++ = '%';
    263 		done = dot = minus = width = precision = 0;
    264 		val = 0;
    265 		while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) {
    266 			switch (c) {
    267 			case 'c': /* FALLTHROUGH */
    268 			case 's':
    269 				*fp++ = c;
    270 				done = 1;
    271 				break;
    272 			case 'd': /* FALLTHROUGH */
    273 			case 'o': /* FALLTHROUGH */
    274 			case 'x': /* FALLTHROUGH */
    275 			case 'X': /* FALLTHROUGH */
    276 				*fp++ = 'l';
    277 				*fp++ = c;
    278 				done = 1;
    279 				break;
    280 			case '#': /* FALLTHROUGH */
    281 			case ' ':
    282 				*fp++ = c;
    283 				break;
    284 			case '.':
    285 				*fp++ = c;
    286 				if (dot == 0) {
    287 					dot = 1;
    288 					width = val;
    289 				} else
    290 					done = 2;
    291 				val = 0;
    292 				break;
    293 			case ':':
    294 				minus = 1;
    295 				break;
    296 			case '-':
    297 				if (minus)
    298 					*fp++ = c;
    299 				else
    300 					done = 1;
    301 				break;
    302 			default:
    303 				if (isdigit((unsigned char)c)) {
    304 					val = (val * 10) + (c - '0');
    305 					if (val > 10000)
    306 						done = 2;
    307 					else
    308 						*fp++ = c;
    309 				} else
    310 					done = 1;
    311 			}
    312 			if (done == 0)
    313 				c = *str++;
    314 		}
    315 		if (done == 2) {
    316 			/* Found an error in the format */
    317 			fp = fmt + 1;
    318 			*fp = *str;
    319 			olen = 0;
    320 		} else {
    321 			if (dot == 0)
    322 				width = val;
    323 			else
    324 				precision = val;
    325 			olen = MAX(width, precision);
    326 		}
    327 		*fp++ = '\0';
    328 
    329 		/* Handle commands */
    330 		switch (c) {
    331 		case 'c':
    332 			pop(&val, NULL, &stack);
    333 			if (ochar(term, (unsigned char)val) == 0)
    334 				return NULL;
    335 			break;
    336 		case 's':
    337 			pop(NULL, &ostr, &stack);
    338 			if (ostr != NULL) {
    339 				l = strlen(ostr);
    340 				if (l < (size_t)olen)
    341 					l = olen;
    342 				if (checkbuf(term, (size_t)(l + 1)) == NULL)
    343 					return NULL;
    344 				l = sprintf(term->_buf + term->_bufpos,
    345 				    fmt, ostr);
    346 				term->_bufpos += l;
    347 			}
    348 			break;
    349 		case 'l':
    350 			pop(NULL, &ostr, &stack);
    351 			if (ostr == NULL)
    352 				l = 0;
    353 			else
    354 				l = strlen(ostr);
    355 #ifdef NCURSES_COMPAT_57
    356 			if (onum(term, "%ld", (long)l, 0) == 0)
    357 				return NULL;
    358 #else
    359 			push((long)l, NULL, &stack);
    360 #endif
    361 			break;
    362 		case 'd': /* FALLTHROUGH */
    363 		case 'o': /* FALLTHROUGH */
    364 		case 'x': /* FALLTHROUGH */
    365 		case 'X':
    366 			pop(&val, NULL, &stack);
    367 			if (onum(term, fmt, (int)val, olen) == 0)
    368 				return NULL;
    369 			break;
    370 		case 'p':
    371 			if (*str < '1' || *str > '9')
    372 				break;
    373 			l = *str++ - '1';
    374 			if (push(params[l].num, params[l].string, &stack))
    375 				return NULL;
    376 			break;
    377 		case 'P':
    378 			pop(&val, NULL, &stack);
    379 			if (*str >= 'a' && *str <= 'z')
    380 				dnums[*str - 'a'] = val;
    381 			else if (*str >= 'A' && *str <= 'Z')
    382 				term->_snums[*str - 'A'] = val;
    383 			break;
    384 		case 'g':
    385 			if (*str >= 'a' && *str <= 'z') {
    386 				if (push(dnums[*str - 'a'], NULL, &stack))
    387 					return NULL;
    388 			} else if (*str >= 'A' && *str <= 'Z') {
    389 				if (push(term->_snums[*str - 'A'],
    390 					NULL, &stack))
    391 					return NULL;
    392 			}
    393 			break;
    394 		case 'i':
    395 			if (piss[0] == 0)
    396 				params[0].num++;
    397 			if (piss[1] == 0)
    398 				params[1].num++;
    399 			break;
    400 		case '\'':
    401 			if (push((long)(unsigned char)*str++, NULL, &stack))
    402 				return NULL;
    403 			while (*str != '\0' && *str != '\'')
    404 				str++;
    405 			if (*str == '\'')
    406 				str++;
    407 			break;
    408 		case '{':
    409 			val = 0;
    410 			for (; isdigit((unsigned char)*str);  str++)
    411 				val = (val * 10) + (*str - '0');
    412 			if (push(val, NULL, &stack))
    413 				return NULL;
    414 			while (*str != '\0' && *str != '}')
    415 				str++;
    416 			if (*str == '}')
    417 				str++;
    418 			break;
    419 		case '+': /* FALLTHROUGH */
    420 		case '-': /* FALLTHROUGH */
    421 		case '*': /* FALLTHROUGH */
    422 		case '/': /* FALLTHROUGH */
    423 		case 'm': /* FALLTHROUGH */
    424 		case 'A': /* FALLTHROUGH */
    425 		case 'O': /* FALLTHROUGH */
    426 		case '&': /* FALLTHROUGH */
    427 		case '|': /* FALLTHROUGH */
    428 		case '^': /* FALLTHROUGH */
    429 		case '=': /* FALLTHROUGH */
    430 		case '<': /* FALLTHROUGH */
    431 		case '>':
    432 			pop(&val, NULL, &stack);
    433 			pop(&val2, NULL, &stack);
    434 			switch (c) {
    435 			case '+':
    436 				val = val + val2;
    437 				break;
    438 			case '-':
    439 				val = val2 - val;
    440 				break;
    441 			case '*':
    442 				val = val * val2;
    443 				break;
    444 			case '/':
    445 				val = val ? val2 / val : 0;
    446 				break;
    447 			case 'm':
    448 				val = val ? val2 % val : 0;
    449 				break;
    450 			case 'A':
    451 				val = val && val2;
    452 				break;
    453 			case 'O':
    454 				val = val || val2;
    455 				break;
    456 			case '&':
    457 				val = val & val2;
    458 				break;
    459 			case '|':
    460 				val = val | val2;
    461 				break;
    462 			case '^':
    463 				val = val ^ val2;
    464 				break;
    465 			case '=':
    466 				val = val == val2;
    467 				break;
    468 			case '<':
    469 				val = val2 < val;
    470 				break;
    471 			case '>':
    472 				val = val2 > val;
    473 				break;
    474 			}
    475 			if (push(val, NULL, &stack))
    476 				return NULL;
    477 			break;
    478 		case '!':
    479 		case '~':
    480 			pop(&val, NULL, &stack);
    481 			switch (c) {
    482 			case '!':
    483 				val = !val;
    484 				break;
    485 			case '~':
    486 				val = ~val;
    487 				break;
    488 			}
    489 			if (push(val, NULL, &stack))
    490 				return NULL;
    491 			break;
    492 		case '?': /* if */
    493 			break;
    494 		case 't': /* then */
    495 			pop(&val, NULL, &stack);
    496 			if (val == 0) {
    497 				l = 0;
    498 				for (; *str != '\0'; str++) {
    499 					if (*str != '%')
    500 						continue;
    501 					str++;
    502 					if (*str == '?')
    503 						l++;
    504 					else if (*str == ';') {
    505 						if (l > 0)
    506 							l--;
    507 						else {
    508 							str++;
    509 							break;
    510 						}
    511 					} else if (*str == 'e' && l == 0) {
    512 						str++;
    513 						break;
    514 					}
    515 				}
    516 			}
    517 			break;
    518 		case 'e': /* else */
    519 			l = 0;
    520 			for (; *str != '\0'; str++) {
    521 				if (*str != '%')
    522 					continue;
    523 				str++;
    524 				if (*str == '?')
    525 					l++;
    526 				else if (*str == ';') {
    527 					if (l > 0)
    528 						l--;
    529 					else {
    530 						str++;
    531 						break;
    532 					}
    533 				}
    534 			}
    535 			break;
    536 		case ';': /* fi */
    537 			break;
    538 		}
    539 	}
    540 	term->_buf[term->_bufpos] = '\0';
    541 	return term->_buf;
    542 }
    543 
    544 char *
    545 ti_tiparm(TERMINAL *term, const char *str, ...)
    546 {
    547 	va_list va;
    548 	char *ret;
    549 
    550 	_DIAGASSERT(term != NULL);
    551 	_DIAGASSERT(str != NULL);
    552 
    553 	va_start(va, str);
    554 	ret = _ti_tiparm(term, str, VA_CHAR_INT, va);
    555 	va_end(va);
    556 	return ret;
    557 }
    558 
    559 char *
    560 tiparm(const char *str, ...)
    561 {
    562 	va_list va;
    563 	char *ret;
    564 
    565 	_DIAGASSERT(str != NULL);
    566 
    567 	va_start(va, str);
    568 	ret = _ti_tiparm(NULL, str, VA_CHAR_INT, va);
    569 	va_end(va);
    570 	return ret;
    571 }
    572 
    573 #ifdef VA_CHAR_LONG
    574 char *
    575 ti_tlparm(TERMINAL *term, const char *str, ...)
    576 {
    577 	va_list va;
    578 	char *ret;
    579 
    580 	_DIAGASSERT(term != NULL);
    581 	_DIAGASSERT(str != NULL);
    582 
    583 	va_start(va, str);
    584 	ret = _ti_tiparm(term, str, VA_CHAR_LONG, va);
    585 	va_end(va);
    586 	return ret;
    587 }
    588 
    589 char *
    590 tlparm(const char *str, ...)
    591 {
    592 	va_list va;
    593 	char *ret;
    594 
    595 	_DIAGASSERT(str != NULL);
    596 
    597 	va_start(va, str);
    598 	ret = _ti_tiparm(NULL, str, VA_CHAR_LONG, va);
    599 	va_end(va);
    600 	return ret;
    601 }
    602 #endif
    603 
    604 static char *
    605 _tparm(const char *str, ...)
    606 {
    607 	va_list va;
    608 	char *ret;
    609 
    610 	_DIAGASSERT(str != NULL);
    611 
    612 	va_start(va, str);
    613 	ret = _ti_tiparm(NULL, str, VA_LONG_LONG, va);
    614 	va_end(va);
    615 	return ret;
    616 }
    617 
    618 char *
    619 tparm(const char *str,
    620     long p1, long p2, long p3, long p4, long p5,
    621     long p6, long p7, long p8, long p9)
    622 {
    623 
    624 	return _tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
    625 }
    626