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