ncurses 4.1
[ncurses.git] / ncurses / read_entry.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                ncurses is copyright (C) 1992-1995                        *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *        Permission is hereby granted to reproduce and distribute ncurses  *
12 *        by any means and for any fee, whether alone or as part of a       *
13 *        larger distribution, in source or in binary form, PROVIDED        *
14 *        this notice is included with any such distribution, and is not    *
15 *        removed from any of its header files. Mention of ncurses in any   *
16 *        applications linked with it is highly appreciated.                *
17 *                                                                          *
18 *        ncurses comes AS IS with no warranty, implied or expressed.       *
19 *                                                                          *
20 ***************************************************************************/
21
22
23
24 /*
25  *      read_entry.c -- Routine for reading in a compiled terminfo file
26  *
27  */
28
29 #include <curses.priv.h>
30
31 #if HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34 #include <sys/stat.h>
35
36 #include <term.h>
37 #include <tic.h>
38
39 MODULE_ID("$Id: read_entry.c,v 1.31 1997/05/10 17:31:08 tom Exp $")
40
41 TERMINAL *cur_term;
42
43 /*
44  *      int
45  *      _nc_read_file_entry(filename, ptr)
46  *
47  *      Read the compiled terminfo entry in the given file into the
48  *      structure pointed to by ptr, allocating space for the string
49  *      table.
50  */
51
52 #undef  BYTE
53 #define BYTE(p,n)       (unsigned char)((p)[n])
54
55 #define IS_NEG1(p)      ((BYTE(p,0) == 0377) && (BYTE(p,1) == 0377))
56 #define IS_NEG2(p)      ((BYTE(p,0) == 0376) && (BYTE(p,1) == 0377))
57 #define LOW_MSB(p)      (BYTE(p,0) + 256*BYTE(p,1))
58
59 static bool have_tic_directory = FALSE;
60
61 /*
62  * Record the "official" location of the terminfo directory, according to
63  * the place where we're writing to, or the normal default, if not.
64  */
65 const char *_nc_tic_dir(const char *path)
66 {
67         static const char *result = TERMINFO;
68
69         if (path != 0) {
70                 result = path;
71                 have_tic_directory = TRUE;
72         } else if (!have_tic_directory) {
73                 char *envp;
74                 if ((envp = getenv("TERMINFO")) != 0)
75                         return _nc_tic_dir(envp);
76         }
77         return result;
78 }
79
80 int _nc_read_file_entry(const char *const filename, TERMTYPE *ptr)
81 /* return 1 if read, 0 if not found or garbled */
82 {
83     int         name_size, bool_count, num_count, str_count, str_size;
84     int         i, fd, numread;
85     char        buf[MAX_ENTRY_SIZE];
86
87     if ((fd = open(filename, O_RDONLY)) < 0)
88         return(0);
89
90     T(("read terminfo %s", filename));
91
92     /* grab the header */
93     (void) read(fd, buf, 12);
94     if (LOW_MSB(buf) != MAGIC)
95     {
96         close(fd);
97         return(0);
98     }
99     name_size  = LOW_MSB(buf + 2);
100     bool_count = LOW_MSB(buf + 4);
101     num_count  = LOW_MSB(buf + 6);
102     str_count  = LOW_MSB(buf + 8);
103     str_size   = LOW_MSB(buf + 10);
104
105     if (str_size)
106     {
107         /* try to allocate space for the string table */
108         ptr->str_table = malloc((unsigned)str_size);
109         if (ptr->str_table == 0)
110         {
111             close(fd);
112             return(0);
113         }
114     }
115
116     /* grab the name */
117     read(fd, buf, min(MAX_NAME_SIZE, (unsigned)name_size));
118     buf[MAX_NAME_SIZE] = '\0';
119     ptr->term_names = calloc(strlen(buf) + 1, sizeof(char));
120     (void) strcpy(ptr->term_names, buf);
121     if (name_size > MAX_NAME_SIZE)
122         lseek(fd, (off_t) (name_size - MAX_NAME_SIZE), 1);
123
124     /* grab the booleans */
125     read(fd, ptr->Booleans, min(BOOLCOUNT, (unsigned)bool_count));
126     if (bool_count > BOOLCOUNT)
127         lseek(fd, (off_t) (bool_count - BOOLCOUNT), 1);
128     else
129         for (i=bool_count; i < BOOLCOUNT; i++)
130             ptr->Booleans[i] = 0;
131
132     /*
133      * If booleans end on an odd byte, skip it.  The machine they
134      * originally wrote terminfo on must have been a 16-bit
135      * word-oriented machine that would trap out if you tried a
136      * word access off a 2-byte boundary.
137      */
138     if ((name_size + bool_count) % 2 != 0)
139         read(fd, buf, 1);
140
141     /* grab the numbers */
142     (void) read(fd, buf, min(NUMCOUNT*2, (unsigned)num_count*2));
143     for (i = 0; i < min(num_count, NUMCOUNT); i++)
144     {
145         if (IS_NEG1(buf + 2*i))
146             ptr->Numbers[i] = ABSENT_NUMERIC;
147         else if (IS_NEG2(buf + 2*i))
148             ptr->Numbers[i] = CANCELLED_NUMERIC;
149         else
150             ptr->Numbers[i] = LOW_MSB(buf + 2*i);
151     }
152     if (num_count > NUMCOUNT)
153         lseek(fd, (off_t) (2 * (num_count - NUMCOUNT)), 1);
154     else
155         for (i=num_count; i < NUMCOUNT; i++)
156             ptr->Numbers[i] = ABSENT_NUMERIC;
157
158     if (str_count)
159     {
160         /* grab the string offsets */
161         numread = read(fd, buf, (unsigned)(str_count*2));
162         if (numread < str_count*2)
163         {
164             close(fd);
165             return(0);
166         }
167         for (i = 0; i < numread/2; i++)
168         {
169             if (IS_NEG1(buf + 2*i))
170                 ptr->Strings[i] = ABSENT_STRING;
171             else if (IS_NEG2(buf + 2*i))
172                 ptr->Strings[i] = CANCELLED_STRING;
173             else
174                 ptr->Strings[i] = (LOW_MSB(buf+2*i) + ptr->str_table);
175         }
176     }
177
178     if (str_count > STRCOUNT)
179         lseek(fd, (off_t) (2 * (str_count - STRCOUNT)), 1);
180     else
181         for (i = str_count; i < STRCOUNT; i++)
182             ptr->Strings[i] = ABSENT_STRING;
183
184     if (str_size)
185     {
186         /* finally, grab the string table itself */
187         numread = read(fd, ptr->str_table, (unsigned)str_size);
188         if (numread != str_size)
189         {
190             close(fd);
191             return(0);
192         }
193     }
194
195     close(fd);
196     return(1);
197 }
198
199 /*
200  * Build a terminfo pathname and try to read the data.  Returns 1 on success,
201  * 0 on failure.
202  */
203 static int _nc_read_tic_entry(char *const filename,
204         const char *const dir, const char *ttn, TERMTYPE *const tp)
205 {
206 /* maximum safe length of terminfo root directory name */
207 #define MAX_TPATH       (PATH_MAX - MAX_ALIAS - 6)
208
209         if (strlen(dir) > MAX_TPATH)
210                 return 0;
211         (void) sprintf(filename, "%s/%s", dir, ttn);
212         return _nc_read_file_entry(filename, tp);
213 }
214
215 /*
216  *      _nc_read_entry(char *tn, char *filename, TERMTYPE *tp)
217  *
218  *      Find and read the compiled entry for a given terminal type,
219  *      if it exists.  We take pains here to make sure no combination
220  *      of environment variables and terminal type name can be used to
221  *      overrun the file buffer.
222  */
223
224 int _nc_read_entry(const char *const tn, char *const filename, TERMTYPE *const tp)
225 {
226 char            *envp;
227 char            ttn[MAX_ALIAS + 3];
228
229         /* truncate the terminal name to prevent dangerous buffer airline */
230         (void) sprintf(ttn, "%c/%.*s", *tn, MAX_ALIAS, tn);
231
232         /* This is System V behavior, in conjunction with our requirements for
233          * writing terminfo entries.
234          */
235         if (have_tic_directory
236          && _nc_read_tic_entry(filename, _nc_tic_dir(0), ttn, tp) == 1)
237                 return 1;
238
239         if ((envp = getenv("TERMINFO")) != 0
240          && _nc_read_tic_entry(filename, _nc_tic_dir(envp), ttn, tp) == 1)
241                 return 1;
242
243         /* this is an ncurses extension */
244         if ((envp = getenv("HOME")) != 0)
245         {
246                 char *home = malloc(strlen(envp) + strlen(PRIVATE_INFO) + 2);
247
248                 (void) sprintf(home, PRIVATE_INFO, envp);
249                 if (_nc_read_tic_entry(filename, home, ttn, tp) == 1) {
250                         free(home);
251                         return(1);
252                 }
253                 free(home);
254         }
255
256         /* this is an ncurses extension */
257         if ((envp = getenv("TERMINFO_DIRS")) != 0)
258         {
259             /* strtok modifies its argument, so we must copy */
260             char *list = strcpy(malloc(strlen(envp)+1), envp);
261             const char *cp = strtok(list, ":");
262             int code = 0;
263
264             do {
265                 if (cp[0] == '\0')
266                     cp = TERMINFO;
267                 if (_nc_read_tic_entry(filename, cp, ttn, tp) == 1) {
268                         code = 1;
269                         break;
270                 }
271             } while
272                 ((cp = strtok((char *)0, ":")) != 0);
273
274             free(list);
275             return(code);
276         }
277
278         /* try the system directory */
279         return(_nc_read_tic_entry(filename, TERMINFO, ttn, tp));
280 }
281
282 /*
283  *      _nc_first_name(char *names)
284  *
285  *      Extract the primary name from a compiled entry.
286  */
287
288 char *_nc_first_name(const char *const sp)
289 /* get the first name from the given name list */
290 {
291     static char buf[MAX_NAME_SIZE];
292     register char *cp;
293
294     (void) strcpy(buf, sp);
295
296     cp = strchr(buf, '|');
297     if (cp)
298         *cp = '\0';
299
300     return(buf);
301 }
302
303 /*
304  *      bool _nc_name_match(namelist, name, delim)
305  *
306  *      Is the given name matched in namelist?
307  */
308
309 int _nc_name_match(const char *const namelst, const char *const name, const char *const delim)
310 /* microtune this, it occurs in several critical loops */
311 {
312 char namecopy[MAX_ENTRY_SIZE];  /* this may get called on a TERMCAP value */
313 register char *cp;
314
315         if (namelst == 0)
316                 return(FALSE);
317         (void) strcpy(namecopy, namelst);
318         if ((cp = strtok(namecopy, delim)) != 0) {
319                 do {
320                         /* avoid strcmp() function-call cost if possible */
321                         if (cp[0] == name[0] && strcmp(cp, name) == 0)
322                             return(TRUE);
323                 } while
324                     ((cp = strtok((char *)0, delim)) != 0);
325         }
326         return(FALSE);
327 }