read_entry.c revision 1.5
1/*	$OpenBSD: read_entry.c,v 1.5 1999/08/15 11:40:56 millert Exp $	*/
2
3/****************************************************************************
4 * Copyright (c) 1999 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 <tic.h>
50#include <term_entry.h>
51
52MODULE_ID("$From: read_entry.c,v 1.61 1999/07/24 20:07:20 tom Exp $")
53
54#ifndef O_BINARY
55#define O_BINARY 0
56#endif
57
58#if 0
59#define TRACE_IN(p) DEBUG(2, p)
60#else
61#define TRACE_IN(p) /*nothing*/
62#endif
63
64/*
65 *	int
66 *	_nc_read_file_entry(filename, ptr)
67 *
68 *	Read the compiled terminfo entry in the given file into the
69 *	structure pointed to by ptr, allocating space for the string
70 *	table.
71 */
72
73#undef  BYTE
74#define BYTE(p,n)	(unsigned char)((p)[n])
75
76#define IS_NEG1(p)	((BYTE(p,0) == 0377) && (BYTE(p,1) == 0377))
77#define IS_NEG2(p)	((BYTE(p,0) == 0376) && (BYTE(p,1) == 0377))
78#define LOW_MSB(p)	(BYTE(p,0) + 256*BYTE(p,1))
79
80static bool have_tic_directory = FALSE;
81static bool keep_tic_directory = FALSE;
82
83/*
84 * Record the "official" location of the terminfo directory, according to
85 * the place where we're writing to, or the normal default, if not.
86 */
87const char *_nc_tic_dir(const char *path)
88{
89    static const char *result = TERMINFO;
90
91    if (!keep_tic_directory) {
92	if (path != 0) {
93	    result = path;
94	    have_tic_directory = TRUE;
95	} else if (!have_tic_directory) {
96	    char *envp;
97	    if (!issetugid() && (envp = getenv("TERMINFO")) != 0)
98		return _nc_tic_dir(envp);
99	}
100    }
101    return result;
102}
103
104/*
105 * Special fix to prevent the terminfo directory from being moved after tic
106 * has chdir'd to it.  If we let it be changed, then if $TERMINFO has a
107 * relative path, we'll lose track of the actual directory.
108 */
109void _nc_keep_tic_dir(const char *path)
110{
111    _nc_tic_dir(path);
112    keep_tic_directory = TRUE;
113}
114
115static void convert_shorts(char *buf, short *Numbers, int count)
116{
117    int i;
118    for (i = 0; i < count; i++)
119    {
120	if (IS_NEG1(buf + 2*i))
121	    Numbers[i] = ABSENT_NUMERIC;
122	else if (IS_NEG2(buf + 2*i))
123	    Numbers[i] = CANCELLED_NUMERIC;
124	else
125	    Numbers[i] = LOW_MSB(buf + 2*i);
126	TRACE_IN(("get Numbers[%d]=%d", i, Numbers[i]));
127    }
128}
129
130static void convert_strings(char *buf, char **Strings, int count, int size, char *table)
131{
132    int i;
133    char *p;
134
135    for (i = 0; i < count; i++) {
136	if (IS_NEG1(buf + 2*i)) {
137	    Strings[i] = ABSENT_STRING;
138	} else if (IS_NEG2(buf + 2*i)) {
139	    Strings[i] = CANCELLED_STRING;
140	} else if (LOW_MSB(buf + 2*i) > size) {
141	    Strings[i] = ABSENT_STRING;
142	} else {
143	    Strings[i] = (LOW_MSB(buf+2*i) + table);
144	    TRACE_IN(("Strings[%d] = %s", i, _nc_visbuf(Strings[i])));
145	}
146
147	/* make sure all strings are NUL terminated */
148	if (VALID_STRING(Strings[i])) {
149	    for (p = Strings[i]; p <= table + size; p++)
150		if (*p == '\0')
151		    break;
152	    /* if there is no NUL, ignore the string */
153	    if (p > table + size)
154		Strings[i] = ABSENT_STRING;
155	}
156    }
157}
158
159#define read_shorts(fd, buf, count) (read(fd, buf, (count)*2) == (count)*2)
160
161#define even_boundary(value) \
162    if ((value) % 2 != 0) read(fd, buf, 1)
163
164static int read_termtype(int fd, TERMTYPE *ptr)
165/* return 1 if read, 0 if not found or garbled */
166{
167    int		name_size, bool_count, num_count, str_count, str_size;
168    int		i;
169    char	buf[MAX_ENTRY_SIZE];
170
171    TRACE_IN(("READ termtype header @%d", tell(fd)));
172
173    /* grab the header */
174    if (!read_shorts(fd, buf, 6)
175     || LOW_MSB(buf) != MAGIC) {
176	return(0);
177    }
178
179    _nc_free_termtype(ptr);
180    name_size  = LOW_MSB(buf + 2);
181    bool_count = LOW_MSB(buf + 4);
182    num_count  = LOW_MSB(buf + 6);
183    str_count  = LOW_MSB(buf + 8);
184    str_size   = LOW_MSB(buf + 10);
185
186    TRACE_IN(("header is %d/%d/%d/%d(%d)", name_size, bool_count, num_count, str_count, str_size));
187    if (name_size  < 0
188     || bool_count < 0
189     || num_count  < 0
190     || str_count  < 0
191     || str_size   < 0) {
192	return(0);
193    }
194
195    if (str_size) {
196	/* try to allocate space for the string table */
197	if (str_count*2 >= (int) sizeof(buf)
198	 || (ptr->str_table = typeMalloc(char, (unsigned)str_size)) == 0) {
199	    return(0);
200	}
201    } else {
202	str_count = 0;
203    }
204
205    /* grab the name (a null-terminate string) */
206    read(fd, buf, min(MAX_NAME_SIZE, (unsigned)name_size));
207    buf[MAX_NAME_SIZE] = '\0';
208    ptr->term_names = typeCalloc(char, strlen(buf) + 1);
209    if (ptr->term_names == NULL) {
210	return(0);
211    }
212    (void) strcpy(ptr->term_names, buf);
213    if (name_size > MAX_NAME_SIZE)
214	lseek(fd, (off_t) (name_size - MAX_NAME_SIZE), 1);
215
216    /* grab the booleans */
217    if ((ptr->Booleans = typeCalloc(char, max(BOOLCOUNT, bool_count))) == 0
218     || read(fd, ptr->Booleans, (unsigned)bool_count) < bool_count) {
219	return(0);
220    }
221
222    /*
223     * If booleans end on an odd byte, skip it.  The machine they
224     * originally wrote terminfo on must have been a 16-bit
225     * word-oriented machine that would trap out if you tried a
226     * word access off a 2-byte boundary.
227     */
228    even_boundary(name_size + bool_count);
229
230    /* grab the numbers */
231    if ((ptr->Numbers = typeCalloc(short, max(NUMCOUNT, num_count))) == 0
232     || !read_shorts(fd, buf, num_count)) {
233	return(0);
234    }
235    convert_shorts(buf, ptr->Numbers, num_count);
236
237    if ((ptr->Strings = typeCalloc(char *, max(STRCOUNT, str_count))) == 0)
238	return(0);
239
240    if (str_count)
241    {
242	/* grab the string offsets */
243	if (!read_shorts(fd, buf, str_count)) {
244	    return(0);
245	}
246	/* finally, grab the string table itself */
247	if (read(fd, ptr->str_table, (unsigned)str_size) != str_size)
248	    return(0);
249	convert_strings(buf, ptr->Strings, str_count, str_size, ptr->str_table);
250    }
251
252#if NCURSES_XNAMES
253
254    ptr->num_Booleans = BOOLCOUNT;
255    ptr->num_Numbers  = NUMCOUNT;
256    ptr->num_Strings  = STRCOUNT;
257
258    /*
259     * Read extended entries, if any, after the normal end of terminfo data.
260     */
261    even_boundary(str_size);
262    TRACE_IN(("READ extended_header @%d", tell(fd)));
263    if (_nc_user_definable && read_shorts(fd, buf, 5)) {
264	int ext_bool_count = LOW_MSB(buf + 0);
265	int ext_num_count  = LOW_MSB(buf + 2);
266	int ext_str_count  = LOW_MSB(buf + 4);
267	int ext_str_size   = LOW_MSB(buf + 6);
268	int ext_str_limit  = LOW_MSB(buf + 8);
269	int need = (ext_bool_count + ext_num_count + ext_str_count);
270	int base = 0;
271
272	if (need >= (int) sizeof(buf)
273	 || ext_str_size >= (int) sizeof(buf)
274	 || ext_str_limit >= (int) sizeof(buf)
275	 || ext_bool_count < 0
276	 || ext_num_count  < 0
277	 || ext_str_count  < 0
278	 || ext_str_size   < 0
279	 || ext_str_limit  < 0)
280	    return(0);
281
282	ptr->num_Booleans = BOOLCOUNT + ext_bool_count;
283	ptr->num_Numbers  = NUMCOUNT + ext_num_count;
284	ptr->num_Strings  = STRCOUNT + ext_str_count;
285
286	ptr->Booleans = typeRealloc(char, ptr->num_Booleans,ptr->Booleans);
287	ptr->Numbers = typeRealloc(short, ptr->num_Numbers, ptr->Numbers);
288	ptr->Strings = typeRealloc(char*, ptr->num_Strings, ptr->Strings);
289
290	TRACE_IN(("extended header is %d/%d/%d(%d:%d)", ext_bool_count, ext_num_count, ext_str_count, ext_str_size, ext_str_limit));
291
292	TRACE_IN(("READ %d extended-booleans @%d", ext_bool_count, tell(fd)));
293	if ((ptr->ext_Booleans = ext_bool_count) != 0) {
294	    if (read(fd, ptr->Booleans + BOOLCOUNT, (unsigned)ext_bool_count) != ext_bool_count)
295		return(0);
296	}
297	even_boundary(ext_bool_count);
298
299	TRACE_IN(("READ %d extended-numbers @%d", ext_num_count, tell(fd)));
300	if ((ptr->ext_Numbers = ext_num_count) != 0) {
301	    if (!read_shorts(fd, buf, ext_num_count))
302		return(0);
303	    TRACE_IN(("Before converting extended-numbers"));
304	    convert_shorts(buf, ptr->Numbers + NUMCOUNT, ext_num_count);
305	}
306
307	TRACE_IN(("READ extended-offsets @%d", tell(fd)));
308	if ((ext_str_count || need)
309	 && !read_shorts(fd, buf, ext_str_count+need))
310	    return(0);
311
312	TRACE_IN(("READ %d bytes of extended-strings @%d", ext_str_limit, tell(fd)));
313	if (ext_str_limit) {
314	    if ((ptr->ext_str_table = typeMalloc(char, ext_str_limit)) == 0)
315		return(0);
316	    if (read(fd, ptr->ext_str_table, ext_str_limit) != ext_str_limit)
317		return(0);
318	    TRACE_IN(("first extended-string is %s", _nc_visbuf(ptr->ext_str_table)));
319	}
320
321	if ((ptr->ext_Strings = ext_str_count) != 0) {
322	    TRACE_IN(("Before computing extended-string capabilities str_count=%d, ext_str_count=%d", str_count, ext_str_count));
323	    convert_strings(buf, ptr->Strings + str_count, ext_str_count, ext_str_limit, ptr->ext_str_table);
324	    for (i = ext_str_count-1; i >= 0; i--) {
325		TRACE_IN(("MOVE from [%d:%d] %s", i, i+str_count, _nc_visbuf(ptr->Strings[i+str_count])));
326		ptr->Strings[i+STRCOUNT] = ptr->Strings[i+str_count];
327		if (VALID_STRING(ptr->Strings[i+STRCOUNT]))
328		    base += (strlen(ptr->Strings[i+STRCOUNT]) + 1);
329		TRACE_IN(("... to    [%d] %s", i+STRCOUNT, _nc_visbuf(ptr->Strings[i+STRCOUNT])));
330	    }
331	}
332
333	if (need) {
334	    if ((ptr->ext_Names = typeCalloc(char *, need)) == 0)
335		return(0);
336	    TRACE_IN(("ext_NAMES starting @%d in extended_strings, first = %s", base, _nc_visbuf(ptr->ext_str_table+base)));
337	    convert_strings(buf + (2 * ext_str_count), ptr->ext_Names, need, ext_str_limit, ptr->ext_str_table + base);
338	}
339
340	T(("...done reading terminfo bool %d(%d) num %d(%d) str %d(%d)",
341	    ptr->num_Booleans, ptr->ext_Booleans,
342	    ptr->num_Numbers,  ptr->ext_Numbers,
343	    ptr->num_Strings,  ptr->ext_Strings));
344
345	TRACE_IN(("extend: num_Booleans:%d", ptr->num_Booleans));
346    } else
347#endif /* NCURSES_XNAMES */
348    {
349	T(("...done reading terminfo bool %d num %d str %d",
350	    bool_count,
351	    num_count,
352	    str_count));
353	TRACE_IN(("normal: num_Booleans:%d", ptr->num_Booleans));
354    }
355
356    for (i = bool_count; i < BOOLCOUNT; i++)
357	ptr->Booleans[i] = FALSE;
358    for (i = num_count; i < NUMCOUNT; i++)
359	ptr->Numbers[i] = ABSENT_NUMERIC;
360    for (i = str_count; i < STRCOUNT; i++)
361	ptr->Strings[i] = ABSENT_STRING;
362
363    return(1);
364}
365
366int _nc_read_file_entry(const char *const filename, TERMTYPE *ptr)
367/* return 1 if read, 0 if not found or garbled */
368{
369    int code, fd = -1;
370
371#ifdef __OpenBSD__
372    if (_nc_read_bsd_terminfo_file(filename, ptr) == 1)
373	return(1);
374#endif /* __OpenBSD__ */
375
376    if (_nc_access(filename, R_OK) < 0
377     || (fd = open(filename, O_RDONLY|O_BINARY)) < 0) {
378	T(("cannot open terminfo %s (errno=%d)", filename, errno));
379	return(0);
380    }
381
382    T(("read terminfo %s", filename));
383    if ((code = read_termtype(fd, ptr)) == 0)
384	_nc_free_termtype(ptr);
385    close(fd);
386
387    return (code);
388}
389
390/*
391 * Build a terminfo pathname and try to read the data.  Returns 1 on success,
392 * 0 on failure.
393 */
394static int _nc_read_tic_entry(char *const filename,
395	const char *const dir, const char *ttn, TERMTYPE *const tp)
396{
397/* maximum safe length of terminfo root directory name */
398#define MAX_TPATH	(PATH_MAX - MAX_ALIAS - 6)
399
400	if (strlen(dir) > MAX_TPATH)
401		return 0;
402	(void) sprintf(filename, "%s/%s", dir, ttn);
403	return _nc_read_file_entry(filename, tp);
404}
405
406/*
407 * Process the list of :-separated directories, looking for the terminal type.
408 * We don't use strtok because it does not show us empty tokens.
409 */
410static int _nc_read_terminfo_dirs(const char *dirs, char *const filename, const char *const ttn, TERMTYPE *const tp)
411{
412	char *list, *a;
413	const char *b;
414	int code = 0;
415
416	/* we'll modify the argument, so we must copy */
417	if ((b = a = list = strdup(dirs)) == NULL)
418		return(0);
419
420	for (;;) {
421		int c = *a;
422		if (c == 0 || c == ':') {
423			*a = 0;
424			if ((b + 1) >= a)
425				b = TERMINFO;
426			if (_nc_read_tic_entry(filename, b, ttn, tp) == 1) {
427				code = 1;
428				break;
429			}
430			b = a + 1;
431			if (c == 0)
432				break;
433		}
434		a++;
435	}
436
437	free(list);
438	return(code);
439}
440
441/*
442 *	_nc_read_entry(char *tn, char *filename, TERMTYPE *tp)
443 *
444 *	Find and read the compiled entry for a given terminal type,
445 *	if it exists.  We take pains here to make sure no combination
446 *	of environment variables and terminal type name can be used to
447 *	overrun the file buffer.
448 */
449
450int _nc_read_entry(const char *const tn, char *const filename, TERMTYPE *const tp)
451{
452char		*envp;
453char		ttn[MAX_ALIAS + 3];
454
455#ifdef __OpenBSD__
456	/* First check the BSD terminfo.db file */
457	if (_nc_read_bsd_terminfo_entry(tn, filename, tp) == 1)
458		return 1;
459#endif /* __OpenBSD__ */
460
461	/* truncate the terminal name to prevent dangerous buffer airline */
462	(void) sprintf(ttn, "%c/%.*s", *tn, MAX_ALIAS, tn);
463
464	/* This is System V behavior, in conjunction with our requirements for
465	 * writing terminfo entries.
466	 */
467	if (have_tic_directory
468	 && _nc_read_tic_entry(filename, _nc_tic_dir(0), ttn, tp) == 1)
469		return 1;
470
471	if (!issetugid() && (envp = getenv("TERMINFO")) != 0
472	 && _nc_read_tic_entry(filename, _nc_tic_dir(envp), ttn, tp) == 1)
473		return 1;
474
475	if ((envp = _nc_home_terminfo()) != 0) {
476		if (_nc_read_tic_entry(filename, envp, ttn, tp) == 1) {
477			return(1);
478		}
479	}
480
481	/* this is an ncurses extension */
482	if (!issetugid() && (envp = getenv("TERMINFO_DIRS")) != 0)
483		return _nc_read_terminfo_dirs(envp, filename, ttn, tp);
484
485	/* Try the system directory.  Note that the TERMINFO_DIRS value, if
486	 * defined by the configure script, begins with a ":", which will be
487	 * interpreted as TERMINFO.
488	 */
489#ifdef TERMINFO_DIRS
490	return _nc_read_terminfo_dirs(TERMINFO_DIRS, filename, ttn, tp);
491#else
492	return _nc_read_tic_entry(filename, TERMINFO, ttn, tp);
493#endif
494}
495
496