xref: /netbsd-src/lib/libterminfo/termcap.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /* $NetBSD: termcap.c,v 1.19 2016/04/01 19:59:08 christos 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: termcap.c,v 1.19 2016/04/01 19:59:08 christos Exp $");
32 
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdint.h>
37 #include <string.h>
38 #include <term_private.h>
39 #include <term.h>
40 #include <termcap.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 
44 #include "termcap_map.c"
45 #include "termcap_hash.c"
46 
47 char *UP;
48 char *BC;
49 
50 /* ARGSUSED */
51 int
52 tgetent(__unused char *bp, const char *name)
53 {
54 	int errret;
55 	static TERMINAL *last = NULL;
56 
57 	_DIAGASSERT(name != NULL);
58 
59 	/* Free the old term */
60 	if (cur_term != NULL) {
61 		if (last != NULL && cur_term != last)
62 			del_curterm(last);
63 		last = cur_term;
64 	}
65 	errret = -1;
66 	if (setupterm(name, STDOUT_FILENO, &errret) != 0)
67 		return errret;
68 
69 	if (last == NULL)
70 		last = cur_term;
71 
72 	if (pad_char != NULL)
73 		PC = pad_char[0];
74 	UP = __UNCONST(cursor_up);
75 	BC = __UNCONST(cursor_left);
76 	return 1;
77 }
78 
79 int
80 tgetflag(const char *id2)
81 {
82 	uint32_t ind;
83 	size_t i;
84 	TERMUSERDEF *ud;
85 	const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
86 
87 	if (cur_term == NULL)
88 		return 0;
89 
90 	ind = _t_flaghash((const unsigned char *)id, strlen(id));
91 	if (ind <= __arraycount(_ti_cap_flagids)) {
92 		if (strcmp(id, _ti_cap_flagids[ind].id) == 0)
93 			return cur_term->flags[_ti_cap_flagids[ind].ti];
94 	}
95 	for (i = 0; i < cur_term->_nuserdefs; i++) {
96 		ud = &cur_term->_userdefs[i];
97 		if (ud->type == 'f' && strcmp(ud->id, id) == 0)
98 			return ud->flag;
99 	}
100 	return 0;
101 }
102 
103 int
104 tgetnum(const char *id2)
105 {
106 	uint32_t ind;
107 	size_t i;
108 	TERMUSERDEF *ud;
109 	const TENTRY *te;
110 	const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
111 
112 	if (cur_term == NULL)
113 		return -1;
114 
115 	ind = _t_numhash((const unsigned char *)id, strlen(id));
116 	if (ind <= __arraycount(_ti_cap_numids)) {
117 		te = &_ti_cap_numids[ind];
118 		if (strcmp(id, te->id) == 0) {
119 			if (!VALID_NUMERIC(cur_term->nums[te->ti]))
120 				return ABSENT_NUMERIC;
121 			return cur_term->nums[te->ti];
122 		}
123 	}
124 	for (i = 0; i < cur_term->_nuserdefs; i++) {
125 		ud = &cur_term->_userdefs[i];
126 		if (ud->type == 'n' && strcmp(ud->id, id) == 0) {
127 			if (!VALID_NUMERIC(ud->num))
128 				return ABSENT_NUMERIC;
129 			return ud->num;
130 		}
131 	}
132 	return -1;
133 }
134 
135 char *
136 tgetstr(const char *id2, char **area)
137 {
138 	uint32_t ind;
139 	size_t i;
140 	TERMUSERDEF *ud;
141 	const char *str;
142 	const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
143 
144 	if (cur_term == NULL)
145 		return NULL;
146 
147 	str = NULL;
148 	ind = _t_strhash((const unsigned char *)id, strlen(id));
149 	if (ind <= __arraycount(_ti_cap_strids)) {
150 		if (strcmp(id, _ti_cap_strids[ind].id) == 0) {
151 			str = cur_term->strs[_ti_cap_strids[ind].ti];
152 			if (str == NULL)
153 				return NULL;
154 		}
155 	}
156 	if (str != NULL)
157 		for (i = 0; i < cur_term->_nuserdefs; i++) {
158 			ud = &cur_term->_userdefs[i];
159 			if (ud->type == 's' && strcmp(ud->id, id) == 0)
160 				str = ud->str;
161 		}
162 
163 	/* XXX: FXIXME
164 	 * We should fix sgr0(me) as it has a slightly different meaning
165 	 * for termcap. */
166 
167 	if (str != NULL && area != NULL && *area != NULL) {
168 		char *s;
169 		s = *area;
170 		strcpy(*area, str);
171 		*area += strlen(*area) + 1;
172 		return s;
173 	}
174 
175 	return __UNCONST(str);
176 }
177 
178 char *
179 tgoto(const char *cm, int destcol, int destline)
180 {
181 	_DIAGASSERT(cm != NULL);
182 	return tiparm(cm, destline, destcol);
183 }
184 
185 static const char *
186 flagname(const char *key)
187 {
188 	uint32_t idx;
189 
190 	idx = _t_flaghash((const unsigned char *)key, strlen(key));
191 	if (idx <= __arraycount(_ti_cap_flagids) &&
192 	    strcmp(key, _ti_cap_flagids[idx].id) == 0)
193 		return _ti_flagid(_ti_cap_flagids[idx].ti);
194 	return key;
195 }
196 
197 static const char *
198 numname(const char *key)
199 {
200 	uint32_t idx;
201 
202 	idx = _t_numhash((const unsigned char *)key, strlen(key));
203 	if (idx <= __arraycount(_ti_cap_numids) &&
204 	    strcmp(key, _ti_cap_numids[idx].id) == 0)
205 		return _ti_numid(_ti_cap_numids[idx].ti);
206 	return key;
207 }
208 
209 static const char *
210 strname(const char *key)
211 {
212 	uint32_t idx;
213 
214 	idx = _t_strhash((const unsigned char *)key, strlen(key));
215 	if (idx <= __arraycount(_ti_cap_strids) &&
216 	    strcmp(key, _ti_cap_strids[idx].id) == 0)
217 		return _ti_strid(_ti_cap_strids[idx].ti);
218 
219 	if (strcmp(key, "tc") == 0)
220 		return "use";
221 
222 	return key;
223 }
224 
225 /* Print a parameter if needed */
226 static int
227 printparam(char **dst, char p, int *nop)
228 {
229 	if (*nop != 0) {
230 		*nop = 0;
231 		return 0;
232 	}
233 
234 	*(*dst)++ = '%';
235 	*(*dst)++ = 'p';
236 	*(*dst)++ = '0' + p;
237 	return 3;
238 }
239 
240 /* Convert a termcap character into terminfo equivalents */
241 static int
242 printchar(char **dst, const char **src)
243 {
244 	unsigned char v;
245 	int l;
246 
247 	l = 4;
248 	v = (unsigned char) *++(*src);
249 	if (v == '\\') {
250 		v = (unsigned char) *++(*src);
251 		switch (v) {
252 		case '0':
253 		case '1':
254 		case '2':
255 		case '3':
256 			v = 0;
257 			while (isdigit((unsigned char) **src))
258 				v = 8 * v + ((unsigned char) *(*src)++ - '0');
259 			(*src)--;
260 			break;
261 		case '\0':
262 			v = '\\';
263 			break;
264 		}
265 	} else if (v == '^')
266 		v = (unsigned char) (*++(*src) & 0x1f);
267 	*(*dst)++ = '%';
268 	if (isgraph(v) && v != ',' && v != '\'' && v != '\\' && v != ':') {
269 		*(*dst)++ = '\'';
270 		*(*dst)++ = v;
271 		*(*dst)++ = '\'';
272 	} else {
273 		*(*dst)++ = '{';
274 		if (v > 99) {
275 			*(*dst)++ = '0'+ v / 100;
276 			l++;
277 		}
278 		if (v > 9) {
279 			*(*dst)++ = '0' + ((int) (v / 10)) % 10;
280 			l++;
281 		}
282 		*(*dst)++ = '0' + v % 10;
283 		*(*dst)++ = '}';
284 	}
285 	return l;
286 }
287 
288 /* Convert termcap commands into terminfo commands */
289 static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+";
290 static const char fmtD[] = "%p0%p0%{2}%*%-";
291 static const char fmtIf[] = "%p0%p0%?";
292 static const char fmtThen[] = "%>%t";
293 static const char fmtElse[] = "%+%;";
294 
295 static char *
296 strval(const char *val)
297 {
298 	char *info, *ip, c;
299 	const char *ps, *pe;
300 	int p, nop;
301 	size_t len, l;
302 
303 	len = 1024; /* no single string should be bigger */
304 	info = ip = malloc(len);
305 	if (info == NULL)
306 		return 0;
307 
308 	/* Move the = */
309 	*ip++ = *val++;
310 
311 	/* Set ps and pe to point to the start and end of the padding */
312 	if (isdigit((unsigned char)*val)) {
313 		for (ps = pe = val;
314 		     isdigit((unsigned char)*val) || *val == '.';
315 		     val++)
316 			pe++;
317 		if (*val == '*') {
318 			val++;
319 			pe++;
320 		}
321 	} else
322 		ps = pe  = NULL;
323 
324 	l = nop = 0;
325 	p = 1;
326 	for (; *val != '\0'; val++) {
327 		if (l + 2 > len)
328 			goto elen;
329 		if (*val != '%') {
330 			if (*val == ',') {
331 				if (l + 3 > len)
332 					goto elen;
333 				*ip++ = '\\';
334 				l++;
335 			}
336 			*ip++ = *val;
337 			l++;
338 			continue;
339 		}
340 		switch (c = *++(val)) {
341 		case 'B':
342 			if (l + sizeof(fmtB) > len)
343 				goto elen;
344 			memcpy(ip, fmtB, sizeof(fmtB) - 1);
345 			/* Replace the embedded parameters with real ones */
346 			ip[2] += p;
347 			ip[19] += p;
348 			ip += sizeof(fmtB) - 1;
349 			l += sizeof(fmtB) - 1;
350 			nop = 1;
351 			continue;
352 		case 'D':
353 			if (l + sizeof(fmtD) > len)
354 				goto elen;
355 			memcpy(ip, fmtD, sizeof(fmtD) - 1);
356 			/* Replace the embedded parameters with real ones */
357 			ip[2] += p;
358 			ip[5] += p;
359 			ip += sizeof(fmtD) - 1;
360 			l += sizeof(fmtD) - 1;
361 			nop = 1;
362 			continue;
363 		case 'r':
364 			/* non op as switched below */
365 			break;
366 		case '2': /* FALLTHROUGH */
367 		case '3': /* FALLTHROUGH */
368 		case 'd':
369 			if (l + 7 > len)
370 				goto elen;
371 			l += printparam(&ip, p, &nop);
372 			*ip++ = '%';
373 			if (c != 'd') {
374 				*ip++ = c;
375 				l++;
376 			}
377 			*ip++ = 'd';
378 			l += 2;
379 			break;
380 		case '+':
381 			if (l + 13 > len)
382 				goto elen;
383 			l += printparam(&ip, p, &nop);
384 			l += printchar(&ip, &val);
385 			*ip++ = '%';
386 			*ip++ = c;
387 			*ip++ = '%';
388 			*ip++ = 'c';
389 			l += 7;
390 			break;
391 		case '>':
392 			if (l + sizeof(fmtIf) + sizeof(fmtThen) +
393 			    sizeof(fmtElse) + (6 * 2) > len)
394 				goto elen;
395 
396 			memcpy(ip, fmtIf, sizeof(fmtIf) - 1);
397 			/* Replace the embedded parameters with real ones */
398 			ip[2] += p;
399 			ip[5] += p;
400 			ip += sizeof(fmtIf) - 1;
401 			l += sizeof(fmtIf) - 1;
402 			l += printchar(&ip, &val);
403 			memcpy(ip, fmtThen, sizeof(fmtThen) - 1);
404 			ip += sizeof(fmtThen) - 1;
405 			l += sizeof(fmtThen) - 1;
406 			l += printchar(&ip, &val);
407 			memcpy(ip, fmtElse, sizeof(fmtElse) - 1);
408 			ip += sizeof(fmtElse) - 1;
409 			l += sizeof(fmtElse) - 1;
410 			l += 16;
411 			nop = 1;
412 			continue;
413 		case '.':
414 			if (l + 6 > len)
415 				goto elen;
416 			l += printparam(&ip, p, &nop);
417 			*ip++ = '%';
418 			*ip++ = 'c';
419 			l += 2;
420 			break;
421 		default:
422 			/* Hope it matches a terminfo command. */
423 			*ip++ = '%';
424 			*ip++ = c;
425 			l += 2;
426 			if (c == 'i')
427 				continue;
428 			break;
429 		}
430 		/* Swap p1 and p2 */
431 		p = 3 - p;
432 	}
433 
434 	/* \E\ is valid termcap.
435 	 * We need to escape the final \ for terminfo. */
436 	if (l > 2 && info[l - 1] == '\\' &&
437 	    (info[l - 2] != '\\' && info[l - 2] != '^'))
438 	{
439 		if (l + 1 > len)
440 			goto elen;
441 		*ip++ = '\\';
442 	}
443 
444 	/* Add our padding at the end. */
445 	if (ps != NULL) {
446 		size_t n = pe - ps;
447 		if (l + n + 4 > len)
448 			goto elen;
449 		*ip++ = '$';
450 		*ip++ = '<';
451 		strncpy(ip, ps, n);
452 		ip += n;
453 		*ip++ = '/';
454 		*ip++ = '>';
455 	}
456 
457 	*ip = '\0';
458 	return info;
459 
460 elen:
461 	free(info);
462 	errno = ENOMEM;
463 	return NULL;
464 }
465 
466 typedef struct {
467 	const char *name;
468 	const char *cap;
469 } DEF_INFO;
470 
471 static DEF_INFO def_infos[] = {
472 	{ "bel",	"^G" },
473 	{ "cr",		"^M" },
474 	{ "cud1",	"^J" },
475 	{ "ht",		"^I" },
476 	{ "ind",	"^J" },
477 	{ "kbs",	"^H" },
478 	{ "kcub1",	"^H" },
479 	{ "kcud1",	"^J" },
480 	{ "nel",	"^M^J" }
481 };
482 
483 char *
484 captoinfo(char *cap)
485 {
486 	char *info, *ip, *token, *val, *p, tok[3];
487 	const char *name;
488 	size_t len, lp, nl, vl, rl;
489 	int defs[__arraycount(def_infos)], fv;
490 
491 	_DIAGASSERT(cap != NULL);
492 
493 	len = strlen(cap) * 2;
494 	len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */
495 	info = ip = malloc(len);
496 	if (info == NULL)
497 		return NULL;
498 
499 	memset(defs, 0, sizeof(defs));
500 	lp = 0;
501 	tok[2] = '\0';
502 	for (token = _ti_get_token(&cap, ':');
503 	     token != NULL;
504 	     token = _ti_get_token(&cap, ':'))
505 	{
506 		if (token[0] == '\0')
507 			continue;
508 		name = token;
509 		val = p = NULL;
510 		fv = nl = 0;
511 		if (token[1] != '\0') {
512 			tok[0] = token[0];
513 			tok[1] = token[1];
514 			nl = 1;
515 			if (token[2] == '\0') {
516 				name = flagname(tok);
517 				val = NULL;
518 			} else if (token[2] == '#') {
519 				name = numname(tok);
520 				val = token + 2;
521 			} else if (token[2] == '=') {
522 				name = strname(tok);
523 				val = strval(token + 2);
524 				fv = 1;
525 			} else
526 				nl = 0;
527 		}
528 		/* If not matched we may need to convert padding still. */
529 		if (nl == 0) {
530 			p = strchr(name, '=');
531 			if (p != NULL) {
532 				val = strval(p);
533 				*p = '\0';
534 				fv = 1;
535 			}
536 		}
537 
538 		/* See if this sets a default. */
539 		for (nl = 0; nl < __arraycount(def_infos); nl++) {
540 			if (strcmp(name, def_infos[nl].name) == 0) {
541 				defs[nl] = 1;
542 				break;
543 			}
544 		}
545 
546 		nl = strlen(name);
547 		if (val == NULL)
548 			vl = 0;
549 		else
550 			vl = strlen(val);
551 		rl = nl + vl + 3; /* , \0 */
552 
553 		if (lp + rl > len) {
554 			if (rl < 256)
555 				len += 256;
556 			else
557 				len += rl;
558 			p = realloc(info, len);
559 			if (p == NULL) {
560 				if (fv == 1)
561 					free(val);
562 				return NULL;
563 			}
564 			info = p;
565 		}
566 
567 		if (ip != info) {
568 			*ip++ = ',';
569 			*ip++ = ' ';
570 		}
571 
572 		strcpy(ip, name);
573 		ip += nl;
574 		if (val != NULL) {
575 			strcpy(ip, val);
576 			ip += vl;
577 			if (fv == 1)
578 				free(val);
579 		}
580 	}
581 
582 	/* Add any defaults not set above. */
583 	for (nl = 0; nl < __arraycount(def_infos); nl++) {
584 		if (defs[nl] == 0) {
585 			*ip++ = ',';
586 			*ip++ = ' ';
587 			strcpy(ip, def_infos[nl].name);
588 			ip += strlen(def_infos[nl].name);
589 			*ip++ = '=';
590 			strcpy(ip, def_infos[nl].cap);
591 			ip += strlen(def_infos[nl].cap);
592 		}
593 	}
594 
595 	*ip = '\0';
596 	return info;
597 }
598 
599