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