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