xref: /netbsd-src/lib/libterminfo/term.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /* $NetBSD: term.c,v 1.20 2016/11/24 17:09:55 christos Exp $ */
2 
3 /*
4  * Copyright (c) 2009, 2010, 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: term.c,v 1.20 2016/11/24 17:09:55 christos Exp $");
32 
33 #include <sys/stat.h>
34 
35 #include <assert.h>
36 #include <cdbr.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <term_private.h>
45 #include <term.h>
46 
47 #define _PATH_TERMINFO		"/usr/share/misc/terminfo"
48 
49 static char database[PATH_MAX];
50 static char pathbuf[PATH_MAX];
51 const char *_ti_database;
52 
53 /* Include a generated list of pre-compiled terminfo descriptions. */
54 #include "compiled_terms.c"
55 
56 static int
57 allocset(void *pp, int init, size_t nelem, size_t elemsize)
58 {
59 	void **p = pp;
60 	if (*p) {
61 		memset(*p, init, nelem * elemsize);
62 		return 0;
63 	}
64 
65 	if ((*p = calloc(nelem, elemsize)) == NULL)
66 		return -1;
67 
68 	if (init != 0)
69 		memset(*p, init, nelem * elemsize);
70 	return 0;
71 }
72 
73 static int
74 _ti_readterm(TERMINAL *term, const char *cap, size_t caplen, int flags)
75 {
76 	uint8_t ver;
77 	uint16_t ind, num;
78 	size_t len;
79 	TERMUSERDEF *ud;
80 
81 	if (caplen == 0)
82 		goto out;
83 	ver = *cap++;
84 	caplen--;
85 	/* Only read version 1 structures */
86 	if (ver != 1)
87 		goto out;
88 
89 
90 	if (allocset(&term->flags, 0, TIFLAGMAX + 1, sizeof(*term->flags)) == -1)
91 		return -1;
92 
93 	if (allocset(&term->nums, -1, TINUMMAX + 1, sizeof(*term->nums)) == -1)
94 		return -1;
95 
96 	if (allocset(&term->strs, 0, TISTRMAX + 1, sizeof(*term->strs)) == -1)
97 		return -1;
98 
99 	if (term->_arealen != caplen) {
100 		term->_arealen = caplen;
101 		term->_area = realloc(term->_area, term->_arealen);
102 		if (term->_area == NULL)
103 			return -1;
104 	}
105 	memcpy(term->_area, cap, term->_arealen);
106 
107 	cap = term->_area;
108 	len = le16dec(cap);
109 	cap += sizeof(uint16_t);
110 	term->name = cap;
111 	cap += len;
112 	len = le16dec(cap);
113 	cap += sizeof(uint16_t);
114 	if (len == 0)
115 		term->_alias = NULL;
116 	else {
117 		term->_alias = cap;
118 		cap += len;
119 	}
120 	len = le16dec(cap);
121 	cap += sizeof(uint16_t);
122 	if (len == 0)
123 		term->desc = NULL;
124 	else {
125 		term->desc = cap;
126 		cap += len;
127 	}
128 
129 	num = le16dec(cap);
130 	cap += sizeof(uint16_t);
131 	if (num != 0) {
132 		num = le16dec(cap);
133 		cap += sizeof(uint16_t);
134 		for (; num != 0; num--) {
135 			ind = le16dec(cap);
136 			cap += sizeof(uint16_t);
137 			term->flags[ind] = *cap++;
138 			if (flags == 0 && !VALID_BOOLEAN(term->flags[ind]))
139 				term->flags[ind] = 0;
140 		}
141 	}
142 
143 	num = le16dec(cap);
144 	cap += sizeof(uint16_t);
145 	if (num != 0) {
146 		num = le16dec(cap);
147 		cap += sizeof(uint16_t);
148 		for (; num != 0; num--) {
149 			ind = le16dec(cap);
150 			cap += sizeof(uint16_t);
151 			term->nums[ind] = le16dec(cap);
152 			if (flags == 0 && !VALID_NUMERIC(term->nums[ind]))
153 				term->nums[ind] = ABSENT_NUMERIC;
154 			cap += sizeof(uint16_t);
155 		}
156 	}
157 
158 	num = le16dec(cap);
159 	cap += sizeof(uint16_t);
160 	if (num != 0) {
161 		num = le16dec(cap);
162 		cap += sizeof(uint16_t);
163 		for (; num != 0; num--) {
164 			ind = le16dec(cap);
165 			cap += sizeof(uint16_t);
166 			len = le16dec(cap);
167 			cap += sizeof(uint16_t);
168 			if (len > 0)
169 				term->strs[ind] = cap;
170 			else if (flags == 0)
171 				term->strs[ind] = ABSENT_STRING;
172 			else
173 				term->strs[ind] = CANCELLED_STRING;
174 			cap += len;
175 		}
176 	}
177 
178 	num = le16dec(cap);
179 	cap += sizeof(uint16_t);
180 	if (num != 0) {
181 		num = le16dec(cap);
182 		cap += sizeof(uint16_t);
183 		if (num != term->_nuserdefs) {
184 			free(term->_userdefs);
185 			term->_userdefs = NULL;
186 			term->_nuserdefs = num;
187 		}
188 		if (allocset(&term->_userdefs, 0, term->_nuserdefs,
189 		    sizeof(*term->_userdefs)) == -1)
190 			return -1;
191 		for (num = 0; num < term->_nuserdefs; num++) {
192 			ud = &term->_userdefs[num];
193 			len = le16dec(cap);
194 			cap += sizeof(uint16_t);
195 			ud->id = cap;
196 			cap += len;
197 			ud->type = *cap++;
198 			switch (ud->type) {
199 			case 'f':
200 				ud->flag = *cap++;
201 				if (flags == 0 &&
202 				    !VALID_BOOLEAN(ud->flag))
203 					ud->flag = 0;
204 				ud->num = ABSENT_NUMERIC;
205 				ud->str = ABSENT_STRING;
206 				break;
207 			case 'n':
208 				ud->flag = ABSENT_BOOLEAN;
209 				ud->num = le16dec(cap);
210 				if (flags == 0 &&
211 				    !VALID_NUMERIC(ud->num))
212 					ud->num = ABSENT_NUMERIC;
213 				ud->str = ABSENT_STRING;
214 				cap += sizeof(uint16_t);
215 				break;
216 			case 's':
217 				ud->flag = ABSENT_BOOLEAN;
218 				ud->num = ABSENT_NUMERIC;
219 				len = le16dec(cap);
220 				cap += sizeof(uint16_t);
221 				if (len > 0)
222 					ud->str = cap;
223 				else if (flags == 0)
224 					ud->str = ABSENT_STRING;
225 				else
226 					ud->str = CANCELLED_STRING;
227 				cap += len;
228 				break;
229 			default:
230 				goto out;
231 			}
232 		}
233 	} else {
234 		term->_nuserdefs = 0;
235 		if (term->_userdefs) {
236 			free(term->_userdefs);
237 			term->_userdefs = NULL;
238 		}
239 	}
240 
241 	return 1;
242 out:
243 	errno = EINVAL;
244 	return -1;
245 }
246 
247 static int
248 _ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags)
249 {
250 	struct cdbr *db;
251 	const void *data;
252 	char *db_name;
253 	const uint8_t *data8;
254 	size_t len, klen;
255 	int r;
256 
257 	if (asprintf(&db_name, "%s.cdb", path) < 0)
258 		return -1;
259 
260 	db = cdbr_open(db_name, CDBR_DEFAULT);
261 	free(db_name);
262 	if (db == NULL)
263 		return -1;
264 
265 	klen = strlen(name) + 1;
266 	if (cdbr_find(db, name, klen, &data, &len) == -1)
267 		goto fail;
268 	data8 = data;
269 	if (len == 0)
270 		goto fail;
271 	/* Check for alias first, fall through to processing normal entries. */
272 	if (data8[0] == 2) {
273 		if (klen + 7 > len || le16dec(data8 + 5) != klen)
274 			goto fail;
275 		if (memcmp(data8 + 7, name, klen))
276 			goto fail;
277 		if (cdbr_get(db, le32dec(data8 + 1), &data, &len))
278 			goto fail;
279 		data8 = data;
280 		if (data8[0] != 1)
281 			goto fail;
282 	} else if (data8[0] != 1)
283 		goto fail;
284 	else if (klen + 3 >= len || le16dec(data8 + 1) != klen)
285 		goto fail;
286 	else if (memcmp(data8 + 3, name, klen))
287 		goto fail;
288 
289 	strlcpy(database, path, sizeof(database));
290 	_ti_database = database;
291 
292 	r = _ti_readterm(term, data, len, flags);
293 	cdbr_close(db);
294 	return r;
295 
296  fail:
297 	cdbr_close(db);
298 	return 0;
299 }
300 
301 static int
302 _ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags)
303 {
304 	const char *p;
305 	size_t l;
306 	int r, e;
307 
308 	e = -1;
309 	r = 0;
310 	do {
311 		for (p = path; *path != '\0' && *path != ':'; path++)
312 			continue;
313 		l = path - p;
314 		if (l != 0 && l + 1 < sizeof(pathbuf)) {
315 			memcpy(pathbuf, p, l);
316 			pathbuf[l] = '\0';
317 			r = _ti_dbgetterm(term, pathbuf, name, flags);
318 			if (r == 1)
319 				return 1;
320 			if (r == 0)
321 				e = 0;
322 		}
323 	} while (*path++ == ':');
324 	return e;
325 }
326 
327 static int
328 ticcmp(const TIC *tic, const char *name)
329 {
330 	char *alias, *s;
331 	size_t len, l;
332 
333 	if (strcmp(tic->name, name) == 0)
334 		return 0;
335 	if (tic->alias == NULL)
336 		return -1;
337 
338 	len = strlen(name);
339 	alias = tic->alias;
340 	while (*alias != '\0') {
341 		s = strchr(alias, '|');
342 		if (s == NULL)
343 			l = strlen(alias);
344 		else
345 			l = s - alias;
346 		if (len == l && memcmp(alias, name, l) == 0)
347 			return 0;
348 		if (s == NULL)
349 			break;
350 		alias = s + 1;
351 	}
352 	return 1;
353 }
354 
355 static int
356 _ti_findterm(TERMINAL *term, const char *name, int flags)
357 {
358 	int r;
359 	char *c, *e, h[PATH_MAX];
360 	TIC *tic;
361 	uint8_t *f;
362 	ssize_t len;
363 
364 	_DIAGASSERT(term != NULL);
365 	_DIAGASSERT(name != NULL);
366 
367 	database[0] = '\0';
368 	_ti_database = NULL;
369 	r = 0;
370 
371 	if ((e = getenv("TERMINFO")) != NULL && *e != '\0')
372 		if (e[0] == '/')
373 			return _ti_dbgetterm(term, e, name, flags);
374 
375 	c = NULL;
376 	if (e == NULL && (c = getenv("TERMCAP")) != NULL) {
377 		if (*c != '\0' && *c != '/') {
378 			c = strdup(c);
379 			if (c != NULL) {
380 				e = captoinfo(c);
381 				free(c);
382 			}
383 		}
384 	}
385 
386 	if (e != NULL) {
387 		if (c == NULL)
388 			e = strdup(e); /* So we don't destroy env */
389 		if (e == NULL)
390 			tic = NULL;
391 		else {
392 			tic = _ti_compile(e, TIC_WARNING |
393 			    TIC_ALIAS | TIC_DESCRIPTION | TIC_EXTRA);
394 			free(e);
395 		}
396 		if (tic != NULL && ticcmp(tic, name) == 0) {
397 			len = _ti_flatten(&f, tic);
398 			if (len != -1) {
399 				r = _ti_readterm(term, (char *)f, (size_t)len,
400 				    flags);
401 				free(f);
402 			}
403 		}
404 		_ti_freetic(tic);
405 		if (r == 1) {
406 			if (c == NULL)
407 				_ti_database = "$TERMINFO";
408 			else
409 				_ti_database = "$TERMCAP";
410 			return r;
411 		}
412 	}
413 
414 	if ((e = getenv("TERMINFO_DIRS")) != NULL)
415 		return _ti_dbgettermp(term, e, name, flags);
416 
417 	if ((e = getenv("HOME")) != NULL) {
418 		snprintf(h, sizeof(h), "%s/.terminfo", e);
419 		r = _ti_dbgetterm(term, h, name, flags);
420 	}
421 	if (r != 1)
422 		r = _ti_dbgettermp(term, _PATH_TERMINFO, name, flags);
423 
424 	return r;
425 
426 }
427 
428 int
429 _ti_getterm(TERMINAL *term, const char *name, int flags)
430 {
431 	int r;
432 	size_t i;
433 	const struct compiled_term *t;
434 
435 	r = _ti_findterm(term, name, flags);
436 	if (r == 1)
437 		return r;
438 
439 	for (i = 0; i < __arraycount(compiled_terms); i++) {
440 		t = &compiled_terms[i];
441 		if (strcmp(name, t->name) == 0) {
442 			r = _ti_readterm(term, t->cap, t->caplen, flags);
443 			break;
444 		}
445 	}
446 
447 	return r;
448 }
449