xref: /netbsd-src/lib/libterminfo/term.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
1 /* $NetBSD: term.c,v 1.4 2010/02/05 19:21:02 roy Exp $ */
2 
3 /*
4  * Copyright (c) 2009 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.4 2010/02/05 19:21:02 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 #define TERMINFO_DIRS		"/usr/share/misc/terminfo"
48 #define TERMINFO_RESCUE		"/rescue/terminfo"
49 
50 static char database[PATH_MAX];
51 static char pathbuf[PATH_MAX];
52 const char *_ti_database;
53 
54 static int
55 _ti_readterm(TERMINAL *term, char *cap, size_t caplen, int flags)
56 {
57 	uint8_t ver;
58 	uint16_t ind, num;
59 	size_t len;
60 	TERMUSERDEF *ud;
61 
62 	ver = *cap++;
63 	/* Only read version 1 and 2 structures */
64 	if (ver != 1 && ver != 2) {
65 		errno = EINVAL;
66 		return -1;
67 	}
68 
69 	term->flags = calloc(TIFLAGMAX + 1, sizeof(char));
70 	if (term->flags == NULL)
71 		goto err;
72 	term->nums = malloc((TINUMMAX + 1) * sizeof(short));
73 	if (term->nums == NULL)
74 		goto err;
75 	memset(term->nums, (short)-1, (TINUMMAX + 1) * sizeof(short));
76 	term->strs = calloc(TISTRMAX + 1, sizeof(char *));
77 	if (term->strs == NULL)
78 		goto err;
79 	term->_area = malloc(caplen);
80 	if (term->_area == NULL)
81 		goto err;
82  	memcpy(term->_area, cap, caplen);
83 
84 	cap = term->_area;
85 	len = le16dec(cap);
86 	cap += sizeof(uint16_t);
87 	term->name = cap;
88 	cap += len;
89 	if (ver == 1)
90 		term->_alias = NULL;
91 	else {
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 	}
101 	len = le16dec(cap);
102 	cap += sizeof(uint16_t);
103 	term->desc = cap;
104 	cap += len;
105 
106 	num = le16dec(cap);
107 	cap += sizeof(uint16_t);
108 	if (num != 0) {
109 		num = le16dec(cap);
110 		cap += sizeof(uint16_t);
111 		for (; num != 0; num--) {
112 			ind = le16dec(cap);
113 			cap += sizeof(uint16_t);
114 			term->flags[ind] = *cap++;
115 			if (flags == 0 && !VALID_BOOLEAN(term->flags[ind]))
116 				term->flags[ind] = 0;
117 		}
118 	}
119 
120 	num = le16dec(cap);
121 	cap += sizeof(uint16_t);
122 	if (num != 0) {
123 		num = le16dec(cap);
124 		cap += sizeof(uint16_t);
125 		for (; num != 0; num--) {
126 			ind = le16dec(cap);
127 			cap += sizeof(uint16_t);
128 			term->nums[ind] = le16dec(cap);
129 			if (flags == 0 && !VALID_NUMERIC(term->nums[ind]))
130 				term->nums[ind] = ABSENT_NUMERIC;
131 			cap += sizeof(uint16_t);
132 		}
133 	}
134 
135 	num = le16dec(cap);
136 	cap += sizeof(uint16_t);
137 	if (num != 0) {
138 		num = le16dec(cap);
139 		cap += sizeof(uint16_t);
140 		for (; num != 0; num--) {
141 			ind = le16dec(cap);
142 			cap += sizeof(uint16_t);
143 			len = le16dec(cap);
144 			cap += sizeof(uint16_t);
145 			if (len > 0)
146 				term->strs[ind] = cap;
147 			else if (flags == 0)
148 				term->strs[ind] = ABSENT_STRING;
149 			else
150 				term->strs[ind] = CANCELLED_STRING;
151 			cap += len;
152 		}
153 	}
154 
155 	num = le16dec(cap);
156 	cap += sizeof(uint16_t);
157 	if (num != 0) {
158 		term->_nuserdefs = le16dec(cap);
159 		term->_userdefs = malloc(sizeof(*term->_userdefs) * num);
160 		cap += sizeof(uint16_t);
161 		for (num = 0; num < term->_nuserdefs; num++) {
162 			ud = &term->_userdefs[num];
163 			len = le16dec(cap);
164 			cap += sizeof(uint16_t);
165 			ud->id = cap;
166 			cap += len;
167 			ud->type = *cap++;
168 			switch (ud->type) {
169 			case 'f':
170 				ud->flag = *cap++;
171 				if (flags == 0 &&
172 				    !VALID_BOOLEAN(ud->flag))
173 					ud->flag = 0;
174 				ud->num = ABSENT_NUMERIC;
175 				ud->str = ABSENT_STRING;
176 				break;
177 			case 'n':
178 				ud->flag = ABSENT_BOOLEAN;
179 				ud->num = le16dec(cap);
180 				if (flags == 0 &&
181 				    !VALID_NUMERIC(ud->num))
182 					ud->num = ABSENT_NUMERIC;
183 				ud->str = ABSENT_STRING;
184 				cap += sizeof(uint16_t);
185 				break;
186 			case 's':
187 				ud->flag = ABSENT_BOOLEAN;
188 				ud->num = ABSENT_NUMERIC;
189 				len = le16dec(cap);
190 				cap += sizeof(uint16_t);
191 				if (len > 0)
192 					ud->str = cap;
193 				else if (flags == 0)
194 					ud->str = ABSENT_STRING;
195 				else
196 					ud->str = CANCELLED_STRING;
197 				cap += len;
198 				break;
199 			default:
200 				errno = EINVAL;
201 				goto err;
202 			}
203 		}
204 	}
205 	return 1;
206 
207 err:
208 	_ti_freeterm(term);
209 	return -1;
210 }
211 
212 static int
213 _ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags)
214 {
215 	DBM *db;
216 	datum dt;
217 	char *p;
218 	int r;
219 
220 	db = dbm_open(path, O_RDONLY, 0644);
221 	if (db == NULL)
222 		return -1;
223 	strlcpy(database, path, sizeof(database));
224 	_ti_database = database;
225 	dt.dptr = (void *)__UNCONST(name);
226 	dt.dsize = strlen(name);
227 	dt = dbm_fetch(db, dt);
228 	if (dt.dptr == NULL) {
229 		dbm_close(db);
230 		return 0;
231 	}
232 
233 	for (;;) {
234 		p = (char *)dt.dptr;
235 		if (*p++ != 0) /* not alias */
236 			break;
237 		dt.dsize = le16dec(p) - 1;
238 		p += sizeof(uint16_t);
239 		dt.dptr = p;
240 		dt = dbm_fetch(db, dt);
241 		if (dt.dptr == NULL) {
242 			dbm_close(db);
243 			return 0;
244 		}
245 	}
246 
247 	r = _ti_readterm(term, (char *)dt.dptr, dt.dsize, flags);
248 	dbm_close(db);
249 	return r;
250 }
251 
252 static int
253 _ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags)
254 {
255 	const char *p;
256 	size_t l;
257 	int r, e;
258 
259 	e = -1;
260 	r = 0;
261 	do {
262 		for (p = path; *path != '\0' && *path != ':'; path++)
263 			continue;
264 		l = path - p;
265 		if (l != 0 && l + 1 < sizeof(pathbuf)) {
266 			memcpy(pathbuf, p, l);
267 			pathbuf[l] = '\0';
268 			r = _ti_dbgetterm(term, pathbuf, name, flags);
269 			if (r == 1)
270 				return 1;
271 			if (r == 0)
272 				e = 0;
273 		}
274 	} while (*path++ == ':');
275 	return e;
276 }
277 
278 int
279 _ti_getterm(TERMINAL *term, const char *name, int flags)
280 {
281 	int r;
282 	char *e, h[PATH_MAX];
283 
284 	_DIAGASSERT(term != NULL);
285 	_DIAGASSERT(name != NULL);
286 
287 	database[0] = '\0';
288 	_ti_database = NULL;
289 	e = getenv("TERMINFO");
290 	if (e != NULL)
291 		return _ti_dbgetterm(term, e, name, flags);
292 
293 	e = getenv("HOME");
294 	if (e != NULL) {
295 		snprintf(h, sizeof(h), "%s/.terminfo", e);
296 		r = _ti_dbgetterm(term, h, name, flags);
297 		if (r == 1)
298 			return 1;
299 	}
300 
301 	r = _ti_dbgettermp(term, TERMINFO_DIRS, name, flags);
302 	if (r == 1)
303 		return 1;
304 
305 	/* If we don't find the term in the rescue db and there is
306 	 * no error, then report the last database accessed. */
307 	strlcpy(h, database, sizeof(h));
308 	r = _ti_dbgetterm(term, TERMINFO_RESCUE, name, flags);
309 	if (r == 0 && h[0] != '\0')
310 		strlcpy(database, h, sizeof(h));
311 	return r;
312 }
313 
314 void
315 _ti_freeterm(TERMINAL *term)
316 {
317 
318 	_DIAGASSERT(term != NULL);
319 
320 	free(term->_area);
321 	term->_area = NULL;
322 	free(term->strs);
323 	term->strs = NULL;
324 	free(term->nums);
325 	term->nums = NULL;
326 	free(term->flags);
327 	term->flags = NULL;
328 	free(term->_userdefs);
329 	term->_userdefs = NULL;
330 }
331