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