Back to index

tetex-bin  3.0
read_entry.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
00031  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
00032  *     and: Thomas E. Dickey                                                *
00033  ****************************************************************************/
00034 
00035 /*
00036  *     read_entry.c -- Routine for reading in a compiled terminfo file
00037  *
00038  */
00039 
00040 #include <curses.priv.h>
00041 
00042 #include <tic.h>
00043 #include <term_entry.h>
00044 
00045 MODULE_ID("$Id: read_entry.c,v 1.80 2004/09/25 23:00:25 tom Exp $")
00046 
00047 #if !HAVE_TELL
00048 #define tell(fd) lseek(fd, 0, SEEK_CUR)          /* lseek() is POSIX, but not tell() */
00049 #endif
00050 
00051 #define TYPE_CALLOC(type,elts) typeCalloc(type, (unsigned)(elts))
00052 
00053 /*
00054  *     int
00055  *     _nc_read_file_entry(filename, ptr)
00056  *
00057  *     Read the compiled terminfo entry in the given file into the
00058  *     structure pointed to by ptr, allocating space for the string
00059  *     table.
00060  */
00061 
00062 #undef  BYTE
00063 #define BYTE(p,n)    (unsigned char)((p)[n])
00064 
00065 #define IS_NEG1(p)   ((BYTE(p,0) == 0377) && (BYTE(p,1) == 0377))
00066 #define IS_NEG2(p)   ((BYTE(p,0) == 0376) && (BYTE(p,1) == 0377))
00067 #define LOW_MSB(p)   (BYTE(p,0) + 256*BYTE(p,1))
00068 
00069 static bool have_tic_directory = FALSE;
00070 static bool keep_tic_directory = FALSE;
00071 
00072 /*
00073  * Record the "official" location of the terminfo directory, according to
00074  * the place where we're writing to, or the normal default, if not.
00075  */
00076 NCURSES_EXPORT(const char *)
00077 _nc_tic_dir(const char *path)
00078 {
00079     static const char *result = TERMINFO;
00080 
00081     if (!keep_tic_directory) {
00082        if (path != 0) {
00083            result = path;
00084            have_tic_directory = TRUE;
00085        } else if (!have_tic_directory && use_terminfo_vars()) {
00086            char *envp;
00087            if ((envp = getenv("TERMINFO")) != 0)
00088               return _nc_tic_dir(envp);
00089        }
00090     }
00091     return result;
00092 }
00093 
00094 /*
00095  * Special fix to prevent the terminfo directory from being moved after tic
00096  * has chdir'd to it.  If we let it be changed, then if $TERMINFO has a
00097  * relative path, we'll lose track of the actual directory.
00098  */
00099 NCURSES_EXPORT(void)
00100 _nc_keep_tic_dir(const char *path)
00101 {
00102     _nc_tic_dir(path);
00103     keep_tic_directory = TRUE;
00104 }
00105 
00106 static void
00107 convert_shorts(char *buf, short *Numbers, int count)
00108 {
00109     int i;
00110     for (i = 0; i < count; i++) {
00111        if (IS_NEG1(buf + 2 * i))
00112            Numbers[i] = ABSENT_NUMERIC;
00113        else if (IS_NEG2(buf + 2 * i))
00114            Numbers[i] = CANCELLED_NUMERIC;
00115        else
00116            Numbers[i] = LOW_MSB(buf + 2 * i);
00117        TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
00118     }
00119 }
00120 
00121 static void
00122 convert_strings(char *buf, char **Strings, int count, int size, char *table)
00123 {
00124     int i;
00125     char *p;
00126 
00127     for (i = 0; i < count; i++) {
00128        if (IS_NEG1(buf + 2 * i)) {
00129            Strings[i] = ABSENT_STRING;
00130        } else if (IS_NEG2(buf + 2 * i)) {
00131            Strings[i] = CANCELLED_STRING;
00132        } else if (LOW_MSB(buf + 2 * i) > size) {
00133            Strings[i] = ABSENT_STRING;
00134        } else {
00135            Strings[i] = (LOW_MSB(buf + 2 * i) + table);
00136            TR(TRACE_DATABASE, ("Strings[%d] = %s", i, _nc_visbuf(Strings[i])));
00137        }
00138 
00139        /* make sure all strings are NUL terminated */
00140        if (VALID_STRING(Strings[i])) {
00141            for (p = Strings[i]; p <= table + size; p++)
00142               if (*p == '\0')
00143                   break;
00144            /* if there is no NUL, ignore the string */
00145            if (p > table + size)
00146               Strings[i] = ABSENT_STRING;
00147        }
00148     }
00149 }
00150 
00151 #define read_shorts(fd, buf, count) \
00152        (read(fd, buf, (unsigned) (count)*2) == (int) (count)*2)
00153 
00154 #define even_boundary(value) \
00155     if ((value) % 2 != 0) read(fd, buf, 1)
00156 
00157 static int
00158 read_termtype(int fd, TERMTYPE *ptr)
00159 /* return 1 if read, 0 if not found or garbled */
00160 {
00161     int name_size, bool_count, num_count, str_count, str_size;
00162     int i;
00163     char buf[MAX_ENTRY_SIZE + 1];
00164     unsigned want, have;
00165 
00166     TR(TRACE_DATABASE, ("READ termtype header @%ld", (long) tell(fd)));
00167 
00168     memset(ptr, 0, sizeof(*ptr));
00169 
00170     /* grab the header */
00171     if (!read_shorts(fd, buf, 6)
00172        || LOW_MSB(buf) != MAGIC) {
00173        return (0);
00174     }
00175 
00176     name_size = LOW_MSB(buf + 2);
00177     bool_count = LOW_MSB(buf + 4);
00178     num_count = LOW_MSB(buf + 6);
00179     str_count = LOW_MSB(buf + 8);
00180     str_size = LOW_MSB(buf + 10);
00181 
00182     TR(TRACE_DATABASE,
00183        ("TERMTYPE name_size=%d, bool=%d/%d, num=%d/%d str=%d/%d(%d)",
00184        name_size, bool_count, BOOLCOUNT, num_count, NUMCOUNT,
00185        str_count, STRCOUNT, str_size));
00186     if (name_size < 0
00187        || bool_count < 0
00188        || num_count < 0
00189        || str_count < 0
00190        || str_size < 0) {
00191        return (0);
00192     }
00193 
00194     if (str_size) {
00195        /* try to allocate space for the string table */
00196        if (str_count * 2 >= (int) sizeof(buf)
00197            || (ptr->str_table = typeMalloc(char, (unsigned) str_size)) == 0) {
00198            return (0);
00199        }
00200     } else {
00201        str_count = 0;
00202     }
00203 
00204     /* grab the name (a null-terminated string) */
00205     want = min(MAX_NAME_SIZE, (unsigned) name_size);
00206     if ((have = read(fd, buf, want)) != want) {
00207        memset(buf + have, 0, want - have);
00208     }
00209     buf[want] = '\0';
00210     ptr->term_names = TYPE_CALLOC(char, strlen(buf) + 1);
00211     if (ptr->term_names == NULL) {
00212        return (0);
00213     }
00214     (void) strcpy(ptr->term_names, buf);
00215     if (have > MAX_NAME_SIZE)
00216        lseek(fd, (off_t) (have - MAX_NAME_SIZE), 1);
00217 
00218     /* grab the booleans */
00219     if ((ptr->Booleans = TYPE_CALLOC(char, max(BOOLCOUNT, bool_count))) == 0
00220        || read(fd, ptr->Booleans, (unsigned) bool_count) < bool_count) {
00221        return (0);
00222     }
00223 
00224     /*
00225      * If booleans end on an odd byte, skip it.  The machine they
00226      * originally wrote terminfo on must have been a 16-bit
00227      * word-oriented machine that would trap out if you tried a
00228      * word access off a 2-byte boundary.
00229      */
00230     even_boundary(name_size + bool_count);
00231 
00232     /* grab the numbers */
00233     if ((ptr->Numbers = TYPE_CALLOC(short, max(NUMCOUNT, num_count))) == 0
00234        || !read_shorts(fd, buf, num_count)) {
00235        return (0);
00236     }
00237     convert_shorts(buf, ptr->Numbers, num_count);
00238 
00239     if ((ptr->Strings = TYPE_CALLOC(char *, max(STRCOUNT, str_count))) == 0)
00240          return (0);
00241 
00242     if (str_count) {
00243        /* grab the string offsets */
00244        if (!read_shorts(fd, buf, str_count)) {
00245            return (0);
00246        }
00247        /* finally, grab the string table itself */
00248        if (read(fd, ptr->str_table, (unsigned) str_size) != str_size)
00249            return (0);
00250        convert_strings(buf, ptr->Strings, str_count, str_size, ptr->str_table);
00251     }
00252 #if NCURSES_XNAMES
00253 
00254     ptr->num_Booleans = BOOLCOUNT;
00255     ptr->num_Numbers = NUMCOUNT;
00256     ptr->num_Strings = STRCOUNT;
00257 
00258     /*
00259      * Read extended entries, if any, after the normal end of terminfo data.
00260      */
00261     even_boundary(str_size);
00262     TR(TRACE_DATABASE, ("READ extended_header @%ld", (long) tell(fd)));
00263     if (_nc_user_definable && read_shorts(fd, buf, 5)) {
00264        int ext_bool_count = LOW_MSB(buf + 0);
00265        int ext_num_count = LOW_MSB(buf + 2);
00266        int ext_str_count = LOW_MSB(buf + 4);
00267        int ext_str_size = LOW_MSB(buf + 6);
00268        int ext_str_limit = LOW_MSB(buf + 8);
00269        unsigned need = (ext_bool_count + ext_num_count + ext_str_count);
00270        int base = 0;
00271 
00272        if (need >= sizeof(buf)
00273            || ext_str_size >= (int) sizeof(buf)
00274            || ext_str_limit >= (int) sizeof(buf)
00275            || ext_bool_count < 0
00276            || ext_num_count < 0
00277            || ext_str_count < 0
00278            || ext_str_size < 0
00279            || ext_str_limit < 0)
00280            return (0);
00281 
00282        ptr->num_Booleans = BOOLCOUNT + ext_bool_count;
00283        ptr->num_Numbers = NUMCOUNT + ext_num_count;
00284        ptr->num_Strings = STRCOUNT + ext_str_count;
00285 
00286        ptr->Booleans = typeRealloc(char, ptr->num_Booleans, ptr->Booleans);
00287        ptr->Numbers = typeRealloc(short, ptr->num_Numbers, ptr->Numbers);
00288        ptr->Strings = typeRealloc(char *, ptr->num_Strings, ptr->Strings);
00289 
00290        TR(TRACE_DATABASE, ("extended header is %d/%d/%d(%d:%d)",
00291                          ext_bool_count, ext_num_count, ext_str_count,
00292                          ext_str_size, ext_str_limit));
00293 
00294        TR(TRACE_DATABASE, ("READ %d extended-booleans @%ld",
00295                          ext_bool_count, (long) tell(fd)));
00296        if ((ptr->ext_Booleans = ext_bool_count) != 0) {
00297            if (read(fd, ptr->Booleans + BOOLCOUNT, (unsigned)
00298                    ext_bool_count) != ext_bool_count)
00299               return (0);
00300        }
00301        even_boundary(ext_bool_count);
00302 
00303        TR(TRACE_DATABASE, ("READ %d extended-numbers @%ld",
00304                          ext_num_count, (long) tell(fd)));
00305        if ((ptr->ext_Numbers = ext_num_count) != 0) {
00306            if (!read_shorts(fd, buf, ext_num_count))
00307               return (0);
00308            TR(TRACE_DATABASE, ("Before converting extended-numbers"));
00309            convert_shorts(buf, ptr->Numbers + NUMCOUNT, ext_num_count);
00310        }
00311 
00312        TR(TRACE_DATABASE, ("READ extended-offsets @%ld", (long) tell(fd)));
00313        if ((ext_str_count || need)
00314            && !read_shorts(fd, buf, ext_str_count + need))
00315            return (0);
00316 
00317        TR(TRACE_DATABASE, ("READ %d bytes of extended-strings @%ld",
00318                          ext_str_limit, (long) tell(fd)));
00319 
00320        if (ext_str_limit) {
00321            if ((ptr->ext_str_table = typeMalloc(char, ext_str_limit)) == 0)
00322                 return (0);
00323            if (read(fd, ptr->ext_str_table, (unsigned) ext_str_limit) != ext_str_limit)
00324               return (0);
00325            TR(TRACE_DATABASE, ("first extended-string is %s", _nc_visbuf(ptr->ext_str_table)));
00326        }
00327 
00328        if ((ptr->ext_Strings = ext_str_count) != 0) {
00329            TR(TRACE_DATABASE,
00330               ("Before computing extended-string capabilities str_count=%d, ext_str_count=%d",
00331               str_count, ext_str_count));
00332            convert_strings(buf, ptr->Strings + str_count, ext_str_count,
00333                          ext_str_limit, ptr->ext_str_table);
00334            for (i = ext_str_count - 1; i >= 0; i--) {
00335               TR(TRACE_DATABASE, ("MOVE from [%d:%d] %s",
00336                                 i, i + str_count,
00337                                 _nc_visbuf(ptr->Strings[i + str_count])));
00338               ptr->Strings[i + STRCOUNT] = ptr->Strings[i + str_count];
00339               if (VALID_STRING(ptr->Strings[i + STRCOUNT]))
00340                   base += (strlen(ptr->Strings[i + STRCOUNT]) + 1);
00341               TR(TRACE_DATABASE, ("... to    [%d] %s",
00342                                 i + STRCOUNT,
00343                                 _nc_visbuf(ptr->Strings[i + STRCOUNT])));
00344            }
00345        }
00346 
00347        if (need) {
00348            if ((ptr->ext_Names = TYPE_CALLOC(char *, need)) == 0)
00349                 return (0);
00350            TR(TRACE_DATABASE,
00351               ("ext_NAMES starting @%d in extended_strings, first = %s",
00352               base, _nc_visbuf(ptr->ext_str_table + base)));
00353            convert_strings(buf + (2 * ext_str_count),
00354                          ptr->ext_Names,
00355                          (int) need,
00356                          ext_str_limit, ptr->ext_str_table + base);
00357        }
00358 
00359        T(("...done reading terminfo bool %d(%d) num %d(%d) str %d(%d)",
00360           ptr->num_Booleans, ptr->ext_Booleans,
00361           ptr->num_Numbers, ptr->ext_Numbers,
00362           ptr->num_Strings, ptr->ext_Strings));
00363 
00364        TR(TRACE_DATABASE, ("extend: num_Booleans:%d", ptr->num_Booleans));
00365     } else
00366 #endif /* NCURSES_XNAMES */
00367     {
00368        T(("...done reading terminfo bool %d num %d str %d",
00369           bool_count, num_count, str_count));
00370 #if NCURSES_XNAMES
00371        TR(TRACE_DATABASE, ("normal: num_Booleans:%d", ptr->num_Booleans));
00372 #endif
00373     }
00374 
00375     for (i = bool_count; i < BOOLCOUNT; i++)
00376        ptr->Booleans[i] = FALSE;
00377     for (i = num_count; i < NUMCOUNT; i++)
00378        ptr->Numbers[i] = ABSENT_NUMERIC;
00379     for (i = str_count; i < STRCOUNT; i++)
00380        ptr->Strings[i] = ABSENT_STRING;
00381 
00382     return (1);
00383 }
00384 
00385 NCURSES_EXPORT(int)
00386 _nc_read_file_entry(const char *const filename, TERMTYPE *ptr)
00387 /* return 1 if read, 0 if not found or garbled */
00388 {
00389     int code, fd = -1;
00390 
00391     if (_nc_access(filename, R_OK) < 0
00392        || (fd = open(filename, O_RDONLY | O_BINARY)) < 0) {
00393        T(("cannot open terminfo %s (errno=%d)", filename, errno));
00394        code = 0;
00395     } else {
00396        T(("read terminfo %s", filename));
00397        if ((code = read_termtype(fd, ptr)) == 0)
00398            _nc_free_termtype(ptr);
00399        close(fd);
00400     }
00401 
00402     return (code);
00403 }
00404 
00405 /*
00406  * Build a terminfo pathname and try to read the data.  Returns 1 on success,
00407  * 0 on failure.
00408  */
00409 static int
00410 _nc_read_tic_entry(char *const filename,
00411                  const char *const dir, const char *ttn, TERMTYPE *const tp)
00412 {
00413     int need = 2 + strlen(dir) + strlen(ttn);
00414 
00415     if (need > PATH_MAX)
00416        return 0;
00417     (void) sprintf(filename, "%s/%s", dir, ttn);
00418     return _nc_read_file_entry(filename, tp);
00419 }
00420 
00421 /*
00422  * Process the list of :-separated directories, looking for the terminal type.
00423  * We don't use strtok because it does not show us empty tokens.
00424  */
00425 static int
00426 _nc_read_terminfo_dirs(const char *dirs, char *const filename, const char *const
00427                      ttn, TERMTYPE *const tp)
00428 {
00429     char *list, *a;
00430     const char *b;
00431     int code = 0;
00432 
00433     /* we'll modify the argument, so we must copy */
00434     if ((b = a = list = strdup(dirs)) == NULL)
00435        return (0);
00436 
00437     for (;;) {
00438        int c = *a;
00439        if (c == 0 || c == NCURSES_PATHSEP) {
00440            *a = 0;
00441            if ((b + 1) >= a)
00442               b = TERMINFO;
00443            if (_nc_read_tic_entry(filename, b, ttn, tp) == 1) {
00444               code = 1;
00445               break;
00446            }
00447            b = a + 1;
00448            if (c == 0)
00449               break;
00450        }
00451        a++;
00452     }
00453 
00454     free(list);
00455     return (code);
00456 }
00457 
00458 /*
00459  *     _nc_read_entry(char *tn, char *filename, TERMTYPE *tp)
00460  *
00461  *     Find and read the compiled entry for a given terminal type,
00462  *     if it exists.  We take pains here to make sure no combination
00463  *     of environment variables and terminal type name can be used to
00464  *     overrun the file buffer.
00465  */
00466 
00467 NCURSES_EXPORT(int)
00468 _nc_read_entry(const char *const tn, char *const filename, TERMTYPE *const tp)
00469 {
00470     char *envp;
00471     char ttn[PATH_MAX];
00472 
00473     if (strlen(tn) == 0
00474        || strcmp(tn, ".") == 0
00475        || strcmp(tn, "..") == 0
00476        || _nc_pathlast(tn) != 0) {
00477        T(("illegal or missing entry name '%s'", tn));
00478        return 0;
00479     }
00480 
00481     /* truncate the terminal name to prevent buffer overflow */
00482     (void) sprintf(ttn, "%c/%.*s", *tn, (int) sizeof(ttn) - 3, tn);
00483 
00484     /* This is System V behavior, in conjunction with our requirements for
00485      * writing terminfo entries.
00486      */
00487     if (have_tic_directory
00488        && _nc_read_tic_entry(filename, _nc_tic_dir(0), ttn, tp) == 1)
00489        return 1;
00490 
00491     if (use_terminfo_vars()) {
00492        if ((envp = getenv("TERMINFO")) != 0
00493            && _nc_read_tic_entry(filename, _nc_tic_dir(envp), ttn, tp) == 1)
00494            return 1;
00495 
00496        /* this is an ncurses extension */
00497        if ((envp = _nc_home_terminfo()) != 0) {
00498            if (_nc_read_tic_entry(filename, envp, ttn, tp) == 1) {
00499               return 1;
00500            }
00501        }
00502 
00503        /* this is an ncurses extension */
00504        if ((envp = getenv("TERMINFO_DIRS")) != 0)
00505            return _nc_read_terminfo_dirs(envp, filename, ttn, tp);
00506     }
00507 
00508     /* Try the system directory.  Note that the TERMINFO_DIRS value, if
00509      * defined by the configure script, begins with a ":", which will be
00510      * interpreted as TERMINFO.
00511      */
00512 #ifdef TERMINFO_DIRS
00513     return _nc_read_terminfo_dirs(TERMINFO_DIRS, filename, ttn, tp);
00514 #else
00515     return _nc_read_tic_entry(filename, TERMINFO, ttn, tp);
00516 #endif
00517 }