xref: /netbsd-src/lib/libterminfo/term.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
1 /* $NetBSD: term.c,v 1.16 2012/06/06 13:36:58 joerg 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.16 2012/06/06 13:36:58 joerg 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 _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 
211 static 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 
265 static 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 
291 static int
292 ticcmp(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 
319 static 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 
392 int
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