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