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