read_entry.c revision 1.3
1/*	$OpenBSD: read_entry.c,v 1.3 1999/01/23 18:31:02 millert Exp $	*/
2
3/****************************************************************************
4 * Copyright (c) 1998 Free Software Foundation, Inc.                        *
5 *                                                                          *
6 * Permission is hereby granted, free of charge, to any person obtaining a  *
7 * copy of this software and associated documentation files (the            *
8 * "Software"), to deal in the Software without restriction, including      *
9 * without limitation the rights to use, copy, modify, merge, publish,      *
10 * distribute, distribute with modifications, sublicense, and/or sell       *
11 * copies of the Software, and to permit persons to whom the Software is    *
12 * furnished to do so, subject to the following conditions:                 *
13 *                                                                          *
14 * The above copyright notice and this permission notice shall be included  *
15 * in all copies or substantial portions of the Software.                   *
16 *                                                                          *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24 *                                                                          *
25 * Except as contained in this notice, the name(s) of the above copyright   *
26 * holders shall not be used in advertising or otherwise to promote the     *
27 * sale, use or other dealings in this Software without prior written       *
28 * authorization.                                                           *
29 ****************************************************************************/
30
31/****************************************************************************
32 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34 ****************************************************************************/
35
36
37
38/*
39 *	read_entry.c -- Routine for reading in a compiled terminfo file
40 *
41 */
42
43#include <curses.priv.h>
44
45#if HAVE_FCNTL_H
46#include <fcntl.h>
47#endif
48
49#include <term.h>
50#include <tic.h>
51#include <term_entry.h>
52
53MODULE_ID("$From: read_entry.c,v 1.47 1998/12/20 02:51:50 tom Exp $")
54
55#ifndef O_BINARY
56#define O_BINARY 0
57#endif
58
59/*
60 *	int
61 *	_nc_read_file_entry(filename, ptr)
62 *
63 *	Read the compiled terminfo entry in the given file into the
64 *	structure pointed to by ptr, allocating space for the string
65 *	table.
66 */
67
68#undef  BYTE
69#define BYTE(p,n)	(unsigned char)((p)[n])
70
71#define IS_NEG1(p)	((BYTE(p,0) == 0377) && (BYTE(p,1) == 0377))
72#define IS_NEG2(p)	((BYTE(p,0) == 0376) && (BYTE(p,1) == 0377))
73#define LOW_MSB(p)	(BYTE(p,0) + 256*BYTE(p,1))
74
75static bool have_tic_directory = FALSE;
76static bool keep_tic_directory = FALSE;
77
78/*
79 * Record the "official" location of the terminfo directory, according to
80 * the place where we're writing to, or the normal default, if not.
81 */
82const char *_nc_tic_dir(const char *path)
83{
84    static const char *result = TERMINFO;
85
86    if (!keep_tic_directory) {
87	if (path != 0) {
88	    result = path;
89	    have_tic_directory = TRUE;
90	} else if (!have_tic_directory) {
91	    char *envp;
92	    if (!issetugid() && (envp = getenv("TERMINFO")) != 0)
93		return _nc_tic_dir(envp);
94	}
95    }
96    return result;
97}
98
99/*
100 * Special fix to prevent the terminfo directory from being moved after tic
101 * has chdir'd to it.  If we let it be changed, then if $TERMINFO has a
102 * relative path, we'll lose track of the actual directory.
103 */
104void _nc_keep_tic_dir(const char *path)
105{
106    _nc_tic_dir(path);
107    keep_tic_directory = TRUE;
108}
109
110int _nc_read_file_entry(const char *const filename, TERMTYPE *ptr)
111/* return 1 if read, 0 if not found or garbled */
112{
113    int		name_size, bool_count, num_count, str_count, str_size;
114    int		i, fd, numread;
115    char	buf[MAX_ENTRY_SIZE];
116
117#ifdef __OpenBSD__
118    if (_nc_read_bsd_terminfo_file(filename, ptr) == 1)
119	return(1);
120#endif /* __OpenBSD__ */
121
122    if (_nc_access(filename, R_OK) < 0
123     || (fd = open(filename, O_RDONLY|O_BINARY)) < 0) {
124	T(("cannot open terminfo %s (errno=%d)", filename, errno));
125	return(0);
126    }
127
128    T(("read terminfo %s", filename));
129
130    /* grab the header */
131    (void) read(fd, buf, 12);
132    if (LOW_MSB(buf) != MAGIC)
133    {
134	close(fd);
135	return(0);
136    }
137    name_size  = LOW_MSB(buf + 2);
138    bool_count = LOW_MSB(buf + 4);
139    num_count  = LOW_MSB(buf + 6);
140    str_count  = LOW_MSB(buf + 8);
141    str_size   = LOW_MSB(buf + 10);
142
143    if (str_size)
144    {
145	/* try to allocate space for the string table */
146	ptr->str_table = malloc((unsigned)str_size);
147	if (ptr->str_table == 0)
148	{
149	    close(fd);
150	    return(0);
151	}
152    }
153    else
154	str_count = 0;
155
156    /* grab the name */
157    read(fd, buf, min(MAX_NAME_SIZE, (unsigned)name_size));
158    buf[MAX_NAME_SIZE] = '\0';
159    ptr->term_names = calloc(strlen(buf) + 1, sizeof(char));
160    if (ptr->term_names == NULL) {
161	if (str_size)
162	    free(ptr->str_table);
163	close(fd);
164	return(0);
165    }
166    (void) strcpy(ptr->term_names, buf);
167    if (name_size > MAX_NAME_SIZE)
168	lseek(fd, (off_t) (name_size - MAX_NAME_SIZE), 1);
169
170    /* grab the booleans */
171    read(fd, ptr->Booleans, min(BOOLCOUNT, (unsigned)bool_count));
172    if (bool_count > BOOLCOUNT)
173	lseek(fd, (off_t) (bool_count - BOOLCOUNT), 1);
174    else
175	for (i=bool_count; i < BOOLCOUNT; i++)
176	    ptr->Booleans[i] = 0;
177
178    /*
179     * If booleans end on an odd byte, skip it.  The machine they
180     * originally wrote terminfo on must have been a 16-bit
181     * word-oriented machine that would trap out if you tried a
182     * word access off a 2-byte boundary.
183     */
184    if ((name_size + bool_count) % 2 != 0)
185	read(fd, buf, 1);
186
187    /* grab the numbers */
188    (void) read(fd, buf, min(NUMCOUNT*2, (unsigned)num_count*2));
189    for (i = 0; i < min(num_count, NUMCOUNT); i++)
190    {
191	if (IS_NEG1(buf + 2*i))
192	    ptr->Numbers[i] = ABSENT_NUMERIC;
193	else if (IS_NEG2(buf + 2*i))
194	    ptr->Numbers[i] = CANCELLED_NUMERIC;
195	else
196	    ptr->Numbers[i] = LOW_MSB(buf + 2*i);
197    }
198    if (num_count > NUMCOUNT)
199	lseek(fd, (off_t) (2 * (num_count - NUMCOUNT)), 1);
200    else
201	for (i=num_count; i < NUMCOUNT; i++)
202	    ptr->Numbers[i] = ABSENT_NUMERIC;
203
204    if (str_count)
205    {
206	if (str_count*2 >= MAX_ENTRY_SIZE)
207	{
208	    close(fd);
209	    return(0);
210	}
211	/* grab the string offsets */
212	numread = read(fd, buf, (unsigned)(str_count*2));
213	if (numread < str_count*2)
214	{
215	    close(fd);
216	    return(0);
217	}
218	for (i = 0; i < numread/2; i++)
219	{
220	    if (i >= STRCOUNT)
221		break;
222	    if (IS_NEG1(buf + 2*i))
223		ptr->Strings[i] = ABSENT_STRING;
224	    else if (IS_NEG2(buf + 2*i))
225		ptr->Strings[i] = CANCELLED_STRING;
226	    else if (LOW_MSB(buf + 2*i) > str_size)
227		ptr->Strings[i] = ABSENT_STRING;
228	    else
229		ptr->Strings[i] = (LOW_MSB(buf+2*i) + ptr->str_table);
230	}
231    }
232
233    if (str_count > STRCOUNT)
234	lseek(fd, (off_t) (2 * (str_count - STRCOUNT)), 1);
235    else
236	for (i = str_count; i < STRCOUNT; i++)
237	    ptr->Strings[i] = ABSENT_STRING;
238
239    if (str_size)
240    {
241	/* finally, grab the string table itself */
242	numread = read(fd, ptr->str_table, (unsigned)str_size);
243	if (numread != str_size)
244	{
245	    close(fd);
246	    return(0);
247	}
248    }
249
250    /* make sure all strings are NUL terminated */
251    for (i = str_count; i < STRCOUNT; i++) {
252	char *p;
253
254	if (VALID_STRING(ptr->Strings[i])) {
255	    for (p = ptr->Strings[i]; p <= ptr->str_table + str_size; p++)
256		if (*p == '\0')
257		    break;
258	    /* if there is no NUL, ignore the string */
259	    if (p > ptr->str_table + str_size)
260		ptr->Strings[i] = ABSENT_STRING;
261	}
262    }
263
264    close(fd);
265    return(1);
266}
267
268/*
269 * Build a terminfo pathname and try to read the data.  Returns 1 on success,
270 * 0 on failure.
271 */
272static int _nc_read_tic_entry(char *const filename,
273	const char *const dir, const char *ttn, TERMTYPE *const tp)
274{
275/* maximum safe length of terminfo root directory name */
276#define MAX_TPATH	(PATH_MAX - MAX_ALIAS - 6)
277
278	if (strlen(dir) > MAX_TPATH)
279		return 0;
280	(void) sprintf(filename, "%s/%s", dir, ttn);
281	return _nc_read_file_entry(filename, tp);
282}
283
284/*
285 * Process the list of :-separated directories, looking for the terminal type.
286 * We don't use strtok because it does not show us empty tokens.
287 */
288static int _nc_read_terminfo_dirs(const char *dirs, char *const filename, const char *const ttn, TERMTYPE *const tp)
289{
290	char *list, *a;
291	const char *b;
292	int code = 0;
293
294	/* we'll modify the argument, so we must copy */
295	if ((b = a = list = malloc(strlen(dirs) + 1)) == NULL)
296		return(0);
297	(void) strcpy(list, dirs);
298
299	for (;;) {
300		int c = *a;
301		if (c == 0 || c == ':') {
302			*a = 0;
303			if ((b + 1) >= a)
304				b = TERMINFO;
305			if (_nc_read_tic_entry(filename, b, ttn, tp) == 1) {
306				code = 1;
307				break;
308			}
309			b = a + 1;
310			if (c == 0)
311				break;
312		}
313		a++;
314	}
315
316	free(list);
317	return(code);
318}
319
320/*
321 *	_nc_read_entry(char *tn, char *filename, TERMTYPE *tp)
322 *
323 *	Find and read the compiled entry for a given terminal type,
324 *	if it exists.  We take pains here to make sure no combination
325 *	of environment variables and terminal type name can be used to
326 *	overrun the file buffer.
327 */
328
329int _nc_read_entry(const char *const tn, char *const filename, TERMTYPE *const tp)
330{
331char		*envp;
332char		ttn[MAX_ALIAS + 3];
333
334#ifdef __OpenBSD__
335	/* First check the BSD terminfo.db file */
336	if (_nc_read_bsd_terminfo_entry(tn, filename, tp) == 1)
337		return 1;
338#endif /* __OpenBSD__ */
339
340	/* truncate the terminal name to prevent dangerous buffer airline */
341	(void) sprintf(ttn, "%c/%.*s", *tn, MAX_ALIAS, tn);
342
343	/* This is System V behavior, in conjunction with our requirements for
344	 * writing terminfo entries.
345	 */
346	if (have_tic_directory
347	 && _nc_read_tic_entry(filename, _nc_tic_dir(0), ttn, tp) == 1)
348		return 1;
349
350	if (!issetugid() && (envp = getenv("TERMINFO")) != 0
351	 && _nc_read_tic_entry(filename, _nc_tic_dir(envp), ttn, tp) == 1)
352		return 1;
353
354	if (!issetugid() && (envp = _nc_home_terminfo()) != 0) {
355		if (_nc_read_tic_entry(filename, envp, ttn, tp) == 1) {
356			return(1);
357		}
358	}
359
360	/* this is an ncurses extension */
361	if (!issetugid() && (envp = getenv("TERMINFO_DIRS")) != 0)
362		return _nc_read_terminfo_dirs(envp, filename, ttn, tp);
363
364	/* Try the system directory.  Note that the TERMINFO_DIRS value, if
365	 * defined by the configure script, begins with a ":", which will be
366	 * interpreted as TERMINFO.
367	 */
368#ifdef TERMINFO_DIRS
369	return _nc_read_terminfo_dirs(TERMINFO_DIRS, filename, ttn, tp);
370#else
371	return _nc_read_tic_entry(filename, TERMINFO, ttn, tp);
372#endif
373}
374
375