]> ncurses.scripts.mit.edu Git - ncurses.git/blobdiff - ncurses/tinfo/read_termcap.c
ncurses 6.2 - patch 20210619
[ncurses.git] / ncurses / tinfo / read_termcap.c
index d60a92d63f0ad6a84747cb73a70297b4622c8fab..675f470a860d266e38d86ea1efccfd6a3a4d3985 100644 (file)
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998 Free Software Foundation, Inc.                        *
+ * Copyright 2018-2020,2021 Thomas E. Dickey                                *
+ * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
@@ -29,9 +30,9 @@
 /****************************************************************************
  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
+ *     and: Thomas E. Dickey                        1996-on                 *
  ****************************************************************************/
 
-
 /*
  * Termcap compatibility support
  *
 #include <curses.priv.h>
 
 #include <ctype.h>
+#include <sys/types.h>
 #include <tic.h>
-#include <term_entry.h>
 
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
+MODULE_ID("$Id: read_termcap.c,v 1.100 2021/06/17 21:11:08 tom Exp $")
 
-MODULE_ID("$Id: read_termcap.c,v 1.43 1999/04/10 20:52:52 tom Exp $")
+#if !PURE_TERMINFO
 
-#ifndef PURE_TERMINFO
+#define TC_SUCCESS     0
+#define TC_NOT_FOUND  -1
+#define TC_SYS_ERR    -2
+#define TC_REF_LOOP   -3
+#define TC_UNRESOLVED -4       /* this is not returned by BSD cgetent */
 
-#ifdef __EMX__
-#define is_pathname(s) ((((s) != 0) && ((s)[0] == '/')) \
-                 || (((s)[0] != 0) && ((s)[1] == ':')))
-#else
-#define is_pathname(s) ((s) != 0 && (s)[0] == '/')
-#endif
+static const char *
+get_termpath(void)
+{
+    const char *result;
 
-#define TC_SUCCESS     0
-#define TC_UNRESOLVED -1
-#define TC_NOT_FOUND  -2
-#define TC_SYS_ERR    -3
-#define TC_REF_LOOP   -4
+    if (!use_terminfo_vars() || (result = getenv("TERMPATH")) == 0)
+       result = TERMPATH;
+    TR(TRACE_DATABASE, ("TERMPATH is %s", result));
+    return result;
+}
 
+/*
+ * Note:
+ * getcap(), cgetent(), etc., are BSD functions.  A copy of those was added to
+ * this file in November 1995, derived from the BSD4.4 Lite sources.
+ *
+ * The initial adaptation uses 518 lines from that source.
+ * The current source (in 2009) uses 183 lines of BSD4.4 Lite (441 ignoring
+ * whitespace).
+ */
 #if USE_GETCAP
 
 #if HAVE_BSD_CGETENT
@@ -86,7 +96,8 @@ MODULE_ID("$Id: read_termcap.c,v 1.43 1999/04/10 20:52:52 tom Exp $")
 #define _nc_cgetset   cgetset
 #else
 static int _nc_cgetmatch(char *, const char *);
-static int _nc_getent(char **, unsigned int *, int *, int, char **, int, const char *, int, char *);
+static int _nc_getent(char **, unsigned *, int *, int, char **, int, const char
+                     *, int, char *);
 static int _nc_nfcmp(const char *, char *);
 
 /*-
@@ -104,11 +115,7 @@ static int _nc_nfcmp(const char *, char *);
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgment:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
@@ -129,17 +136,11 @@ static int _nc_nfcmp(const char *, char *);
 
 #define        BFRAG           1024
 #define        BSIZE           1024
-#define        ESC             ('[' & 037)     /* ASCII ESC */
-#define        MAX_RECURSION   32              /* maximum getent recursion */
-#define        SFRAG           100             /* cgetstr mallocs in SFRAG chunks */
-
-#define RECOK  (char)0
-#define TCERR  (char)1
-#define        SHADOW  (char)2
+#define        MAX_RECURSION   32      /* maximum getent recursion */
 
-static size_t   topreclen;     /* toprec length */
-static char    *toprec;        /* Additional record specified by cgetset() */
-static int      gottoprec;     /* Flag indicating retrieval of toprecord */
+static size_t topreclen;       /* toprec length */
+static char *toprec;           /* Additional record specified by cgetset() */
+static int gottoprec;          /* Flag indicating retrieval of toprecord */
 
 /*
  * Cgetset() allows the addition of a user specified buffer to be added to the
@@ -149,20 +150,20 @@ static int         gottoprec;     /* Flag indicating retrieval of toprecord */
 static int
 _nc_cgetset(const char *ent)
 {
-       if (ent == 0) {
-               FreeIfNeeded(toprec);
-               toprec = 0;
-               topreclen = 0;
-               return (0);
-       }
-       topreclen = strlen(ent);
-       if ((toprec = typeMalloc(char, topreclen + 1)) == 0) {
-               errno = ENOMEM;
-               return (-1);
-       }
-       gottoprec = 0;
-       (void)strcpy(toprec, ent);
+    if (ent == 0) {
+       FreeIfNeeded(toprec);
+       toprec = 0;
+       topreclen = 0;
        return (0);
+    }
+    topreclen = strlen(ent);
+    if ((toprec = typeMalloc(char, topreclen + 1)) == 0) {
+       errno = ENOMEM;
+       return (-1);
+    }
+    gottoprec = 0;
+    _nc_STRCPY(toprec, ent, topreclen);
+    return (0);
 }
 
 /*
@@ -180,43 +181,43 @@ _nc_cgetset(const char *ent)
 static char *
 _nc_cgetcap(char *buf, const char *cap, int type)
 {
-       register const char *cp;
-       register char *bp;
+    register const char *cp;
+    register char *bp;
 
-       bp = buf;
+    bp = buf;
+    for (;;) {
+       /*
+        * Skip past the current capability field - it's either the
+        * name field if this is the first time through the loop, or
+        * the remainder of a field whose name failed to match cap.
+        */
        for (;;) {
-               /*
-                * Skip past the current capability field - it's either the
-                * name field if this is the first time through the loop, or
-                * the remainder of a field whose name failed to match cap.
-                */
-               for (;;) {
-                       if (*bp == '\0')
-                               return (0);
-                       else if (*bp++ == ':')
-                               break;
-               }
+           if (*bp == '\0')
+               return (0);
+           else if (*bp++ == ':')
+               break;
+       }
 
-               /*
-                * Try to match (cap, type) in buf.
-                */
-               for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
-                       continue;
-               if (*cp != '\0')
-                       continue;
-               if (*bp == '@')
-                       return (0);
-               if (type == ':') {
-                       if (*bp != '\0' && *bp != ':')
-                               continue;
-                       return(bp);
-               }
-               if (*bp != type)
-                       continue;
-               bp++;
-               return (*bp == '@' ? 0 : bp);
+       /*
+        * Try to match (cap, type) in buf.
+        */
+       for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
+           continue;
+       if (*cp != '\0')
+           continue;
+       if (*bp == '@')
+           return (0);
+       if (type == ':') {
+           if (*bp != '\0' && *bp != ':')
+               continue;
+           return (bp);
        }
-       /* NOTREACHED */
+       if (*bp != type)
+           continue;
+       bp++;
+       return (*bp == '@' ? 0 : bp);
+    }
+    /* NOTREACHED */
 }
 
 /*
@@ -228,17 +229,17 @@ _nc_cgetcap(char *buf, const char *cap, int type)
  * Returns:
  *
  * positive #    on success (i.e., the index in db_array)
- * TC_UNRESOLVED if we had too many recurrences to resolve
  * TC_NOT_FOUND  if the requested record couldn't be found
  * TC_SYS_ERR    if a system error was encountered (e.g.,couldn't open a file)
  * TC_REF_LOOP   if a potential reference loop is detected
+ * TC_UNRESOLVED if we had too many recurrences to resolve
  */
 static int
 _nc_cgetent(char **buf, int *oline, char **db_array, const char *name)
 {
-       unsigned int dummy;
+    unsigned dummy;
 
-       return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
+    return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
 }
 
 /*
@@ -262,327 +263,340 @@ _nc_cgetent(char **buf, int *oline, char **db_array, const char *name)
 #define DOALLOC(size) typeRealloc(char, size, record)
 static int
 _nc_getent(
-       char **cap,         /* termcap-content */
-       unsigned int *len,  /* length, needed for recursion */
-       int *beginning,     /* line-number at match */
-       int in_array,       /* index in 'db_array[] */
-       char **db_array,    /* list of files to search */
-       int fd,
-       const char *name,
-       int depth,
-       char *nfield)
+             char **cap,       /* termcap-content */
+             unsigned *len,    /* length, needed for recursion */
+             int *beginning,   /* line-number at match */
+             int in_array,     /* index in 'db_array[] */
+             char **db_array,  /* list of files to search */
+             int fd,
+             const char *name,
+             int depth,
+             char *nfield)
 {
-       register char *r_end, *rp;
-       int myfd = FALSE;
-       char *record = 0;
-       int tc_not_resolved;
-       int current;
-       int lineno;
-
-       /*
-        * Return with ``loop detected'' error if we've recurred more than
-        * MAX_RECURSION times.
-        */
-       if (depth > MAX_RECURSION)
-               return (TC_REF_LOOP);
+    register char *r_end, *rp;
+    int myfd = FALSE;
+    char *record = 0;
+    int tc_not_resolved;
+    int current;
+    int lineno;
+
+    /*
+     * Return with ``loop detected'' error if we've recurred more than
+     * MAX_RECURSION times.
+     */
+    if (depth > MAX_RECURSION)
+       return (TC_REF_LOOP);
+
+    /*
+     * Check if we have a top record from cgetset().
+     */
+    if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) {
+       if ((record = DOALLOC(topreclen + BFRAG)) == 0) {
+           errno = ENOMEM;
+           return (TC_SYS_ERR);
+       }
+       _nc_STRCPY(record, toprec, topreclen + BFRAG);
+       rp = record + topreclen + 1;
+       r_end = rp + BFRAG;
+       current = in_array;
+    } else {
+       int foundit;
 
        /*
-        * Check if we have a top record from cgetset().
+        * Allocate first chunk of memory.
         */
-       if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) {
-               if ((record = DOALLOC(topreclen + BFRAG)) == 0) {
-                       errno = ENOMEM;
-                       return (TC_SYS_ERR);
-               }
-               (void)strcpy(record, toprec);
-               rp = record + topreclen + 1;
-               r_end = rp + BFRAG;
-               current = in_array;
-       } else {
-               int foundit;
-
-               /*
-                * Allocate first chunk of memory.
-                */
-               if ((record = DOALLOC(BFRAG)) == 0) {
-                       errno = ENOMEM;
-                       return (TC_SYS_ERR);
-               }
-               rp = r_end = record + BFRAG;
-               foundit = FALSE;
-
-               /*
-                * Loop through database array until finding the record.
-                */
-               for (current = in_array; db_array[current] != 0; current++) {
-                       int eof = FALSE;
-
-                       /*
-                        * Open database if not already open.
-                        */
-                       if (fd >= 0) {
-                               (void)lseek(fd, (off_t)0, SEEK_SET);
-                       } else if ((_nc_access(db_array[current], R_OK) < 0)
-                         || (fd = open(db_array[current], O_RDONLY, 0)) < 0) {
-                               /* No error on unfound file. */
-                               if (errno == ENOENT)
-                                       continue;
-                               free(record);
-                               return (TC_SYS_ERR);
-                       } else {
-                               myfd = TRUE;
-                       }
-                       lineno = 0;
-
-                       /*
-                        * Find the requested capability record ...
-                        */
-                       {
-                               char buf[2048];
-                               register char *b_end = buf;
-                               register char *bp = buf;
-                               register int c;
-
-                               /*
-                                * Loop invariants:
-                                *      There is always room for one more character in record.
-                                *      R_end always points just past end of record.
-                                *      Rp always points just past last character in record.
-                                *      B_end always points just past last character in buf.
-                                *      Bp always points at next character in buf.
-                                */
-
-                               for (;;) {
-                                       int first = lineno + 1;
-
-                                       /*
-                                        * Read in a line implementing (\, newline)
-                                        * line continuation.
-                                        */
-                                       rp = record;
-                                       for (;;) {
-                                               if (bp >= b_end) {
-                                                       int n;
-
-                                                       n = read(fd, buf, sizeof(buf));
-                                                       if (n <= 0) {
-                                                               if (myfd)
-                                                                       (void)close(fd);
-                                                               if (n < 0) {
-                                                                       free(record);
-                                                                       return (TC_SYS_ERR);
-                                                               }
-                                                               fd = -1;
-                                                               eof = TRUE;
-                                                               break;
-                                                       }
-                                                       b_end = buf+n;
-                                                       bp = buf;
-                                               }
-
-                                               c = *bp++;
-                                               if (c == '\n') {
-                                                       lineno++;
-                                                       if (rp == record || *(rp-1) != '\\')
-                                                               break;
-                                               }
-                                               *rp++ = c;
-
-                                               /*
-                                                * Enforce loop invariant: if no room
-                                                * left in record buffer, try to get
-                                                * some more.
-                                                */
-                                               if (rp >= r_end) {
-                                                       unsigned int pos;
-                                                       size_t newsize;
-
-                                                       pos = rp - record;
-                                                       newsize = r_end - record + BFRAG;
-                                                       record = DOALLOC(newsize);
-                                                       if (record == 0) {
-                                                               if (myfd)
-                                                                       (void)close(fd);
-                                                               errno = ENOMEM;
-                                                               return (TC_SYS_ERR);
-                                                       }
-                                                       r_end = record + newsize;
-                                                       rp = record + pos;
-                                               }
-                                       }
-                                       /* loop invariant lets us do this */
-                                       *rp++ = '\0';
-
-                                       /*
-                                        * If encountered eof check next file.
-                                        */
-                                       if (eof)
-                                               break;
-
-                                       /*
-                                        * Toss blank lines and comments.
-                                        */
-                                       if (*record == '\0' || *record == '#')
-                                               continue;
-
-                                       /*
-                                        * See if this is the record we want ...
-                                        */
-                                       if (_nc_cgetmatch(record, name) == 0
-                                        && (nfield == 0
-                                         || !_nc_nfcmp(nfield, record))) {
-                                               foundit = TRUE;
-                                               *beginning = first;
-                                               break;  /* found it! */
-                                       }
-                               }
-                       }
-                       if (foundit)
-                               break;
-               }
-
-               if (!foundit)
-                       return (TC_NOT_FOUND);
+       if ((record = DOALLOC(BFRAG)) == 0) {
+           errno = ENOMEM;
+           return (TC_SYS_ERR);
        }
+       rp = r_end = record + BFRAG;
+       foundit = FALSE;
 
        /*
-        * Got the capability record, but now we have to expand all tc=name
-        * references in it ...
+        * Loop through database array until finding the record.
         */
-       {
-               register char *newicap, *s;
-               register int newilen;
-               unsigned int ilen;
-               int diff, iret, tclen, oline;
-               char *icap, *scan, *tc, *tcstart, *tcend;
+       for (current = in_array; db_array[current] != 0; current++) {
+           int eof = FALSE;
+
+           /*
+            * Open database if not already open.
+            */
+           if (fd >= 0) {
+               (void) lseek(fd, (off_t) 0, SEEK_SET);
+           } else if ((_nc_access(db_array[current], R_OK) < 0)
+                      || (fd = open(db_array[current], O_RDONLY, 0)) < 0) {
+               /* No error on unfound file. */
+               if (errno == ENOENT)
+                   continue;
+               free(record);
+               return (TC_SYS_ERR);
+           } else {
+               myfd = TRUE;
+           }
+           lineno = 0;
+
+           /*
+            * Find the requested capability record ...
+            */
+           {
+               char buf[2048];
+               register char *b_end = buf;
+               register char *bp = buf;
+               register int c;
 
                /*
                 * Loop invariants:
-                *      There is room for one more character in record.
-                *      R_end points just past end of record.
-                *      Rp points just past last character in record.
-                *      Scan points at remainder of record that needs to be
-                *      scanned for tc=name constructs.
+                *      There is always room for one more character in record.
+                *      R_end always points just past end of record.
+                *      Rp always points just past last character in record.
+                *      B_end always points just past last character in buf.
+                *      Bp always points at next character in buf.
                 */
-               scan = record;
-               tc_not_resolved = FALSE;
-               for (;;) {
-                       if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0)
-                               break;
 
-                       /*
-                        * Find end of tc=name and stomp on the trailing `:'
-                        * (if present) so we can use it to call ourselves.
-                        */
-                       s = tc;
-                       while (*s != '\0') {
-                               if (*s++ == ':') {
-                                       *(s - 1) = '\0';
-                                       break;
-                               }
-                       }
-                       tcstart = tc - 3;
-                       tclen = s - tcstart;
-                       tcend = s;
-
-                       iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd, tc, depth+1, 0);
-                       newicap = icap;         /* Put into a register. */
-                       newilen = ilen;
-                       if (iret != TC_SUCCESS) {
-                               /* an error */
-                               if (iret < TC_NOT_FOUND) {
-                                       if (myfd)
-                                               (void)close(fd);
-                                       free(record);
-                                       return (iret);
-                               }
-                               if (iret == TC_UNRESOLVED)
-                                       tc_not_resolved = TRUE;
-                               /* couldn't resolve tc */
-                               if (iret == TC_NOT_FOUND) {
-                                       *(s - 1) = ':';
-                                       scan = s - 1;
-                                       tc_not_resolved = TRUE;
-                                       continue;
+               for (;;) {
+                   int first = lineno + 1;
+
+                   /*
+                    * Read in a line implementing (\, newline)
+                    * line continuation.
+                    */
+                   rp = record;
+                   for (;;) {
+                       if (bp >= b_end) {
+                           int n;
+
+                           n = (int) read(fd, buf, sizeof(buf));
+                           if (n <= 0) {
+                               if (myfd)
+                                   (void) close(fd);
+                               if (n < 0) {
+                                   free(record);
+                                   return (TC_SYS_ERR);
                                }
+                               fd = -1;
+                               eof = TRUE;
+                               break;
+                           }
+                           b_end = buf + n;
+                           bp = buf;
                        }
 
-                       /* not interested in name field of tc'ed record */
-                       s = newicap;
-                       while (*s != '\0' && *s++ != ':')
-                               ;
-                       newilen -= s - newicap;
-                       newicap = s;
-
-                       /* make sure interpolated record is `:'-terminated */
-                       s += newilen;
-                       if (*(s-1) != ':') {
-                               *s = ':';       /* overwrite NUL with : */
-                               newilen++;
+                       c = *bp++;
+                       if (c == '\n') {
+                           lineno++;
+                           /*
+                            * Unlike BSD 4.3, this ignores a backslash at the
+                            * end of a comment-line.  That makes it consistent
+                            * with the rest of ncurses -TD
+                            */
+                           if (rp == record
+                               || *record == '#'
+                               || *(rp - 1) != '\\')
+                               break;
                        }
+                       *rp++ = (char) c;
 
                        /*
-                        * Make sure there's enough room to insert the
-                        * new record.
+                        * Enforce loop invariant: if no room
+                        * left in record buffer, try to get
+                        * some more.
                         */
-                       diff = newilen - tclen;
-                       if (diff >= r_end - rp) {
-                               unsigned int pos, tcpos, tcposend;
-                               size_t newsize;
-
-                               pos = rp - record;
-                               newsize = r_end - record + diff + BFRAG;
-                               tcpos = tcstart - record;
-                               tcposend = tcend - record;
-                               record = DOALLOC(newsize);
-                               if (record == 0) {
-                                       if (myfd)
-                                               (void)close(fd);
-                                       free(icap);
-                                       errno = ENOMEM;
-                                       return (TC_SYS_ERR);
-                               }
-                               r_end = record + newsize;
-                               rp = record + pos;
-                               tcstart = record + tcpos;
-                               tcend = record + tcposend;
+                       if (rp >= r_end) {
+                           unsigned pos;
+                           size_t newsize;
+
+                           pos = (unsigned) (rp - record);
+                           newsize = (size_t) (r_end - record + BFRAG);
+                           record = DOALLOC(newsize);
+                           if (record == 0) {
+                               if (myfd)
+                                   (void) close(fd);
+                               errno = ENOMEM;
+                               return (TC_SYS_ERR);
+                           }
+                           r_end = record + newsize;
+                           rp = record + pos;
                        }
+                   }
+                   /* loop invariant lets us do this */
+                   *rp++ = '\0';
+
+                   /*
+                    * If encountered eof check next file.
+                    */
+                   if (eof)
+                       break;
 
-                       /*
-                        * Insert tc'ed record into our record.
-                        */
-                       s = tcstart + newilen;
-                       memmove(s, tcend, (size_t)(rp - tcend));
-                       memmove(tcstart, newicap, (size_t)newilen);
-                       rp += diff;
-                       free(icap);
+                   /*
+                    * Toss blank lines and comments.
+                    */
+                   if (*record == '\0' || *record == '#')
+                       continue;
 
-                       /*
-                        * Start scan on `:' so next cgetcap works properly
-                        * (cgetcap always skips first field).
-                        */
-                       scan = s-1;
+                   /*
+                    * See if this is the record we want ...
+                    */
+                   if (_nc_cgetmatch(record, name) == 0
+                       && (nfield == 0
+                           || !_nc_nfcmp(nfield, record))) {
+                       foundit = TRUE;
+                       *beginning = first;
+                       break;  /* found it! */
+                   }
                }
+           }
+           if (foundit)
+               break;
+       }
+
+       if (!foundit) {
+           free(record);
+           return (TC_NOT_FOUND);
        }
+    }
+
+    /*
+     * Got the capability record, but now we have to expand all tc=name
+     * references in it ...
+     */
+    {
+       register char *newicap, *s;
+       register int newilen;
+       unsigned ilen;
+       int diff, iret, tclen, oline;
+       char *icap = 0, *scan, *tc, *tcstart, *tcend;
 
        /*
-        * Close file (if we opened it), give back any extra memory, and
-        * return capability, length and success.
+        * Loop invariants:
+        *      There is room for one more character in record.
+        *      R_end points just past end of record.
+        *      Rp points just past last character in record.
+        *      Scan points at remainder of record that needs to be
+        *      scanned for tc=name constructs.
         */
-       if (myfd)
-               (void)close(fd);
-       *len = rp - record - 1; /* don't count NUL */
-       if (r_end > rp) {
-               if ((record = DOALLOC((size_t)(rp - record))) == 0) {
-                       errno = ENOMEM;
-                       return (TC_SYS_ERR);
+       scan = record;
+       tc_not_resolved = FALSE;
+       for (;;) {
+           if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0) {
+               break;
+           }
+
+           /*
+            * Find end of tc=name and stomp on the trailing `:'
+            * (if present) so we can use it to call ourselves.
+            */
+           s = tc;
+           while (*s != '\0') {
+               if (*s++ == ':') {
+                   *(s - 1) = '\0';
+                   break;
+               }
+           }
+           tcstart = tc - 3;
+           tclen = (int) (s - tcstart);
+           tcend = s;
+
+           icap = 0;
+           iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd,
+                             tc, depth + 1, 0);
+           newicap = icap;     /* Put into a register. */
+           newilen = (int) ilen;
+           if (iret != TC_SUCCESS) {
+               /* an error */
+               if (iret < TC_NOT_FOUND) {
+                   if (myfd)
+                       (void) close(fd);
+                   free(record);
+                   FreeIfNeeded(icap);
+                   return (iret);
+               }
+               if (iret == TC_UNRESOLVED) {
+                   tc_not_resolved = TRUE;
+                   /* couldn't resolve tc */
+               } else if (iret == TC_NOT_FOUND) {
+                   *(s - 1) = ':';
+                   scan = s - 1;
+                   tc_not_resolved = TRUE;
+                   continue;
                }
+           }
+
+           /* not interested in name field of tc'ed record */
+           s = newicap;
+           while (*s != '\0' && *s++ != ':') ;
+           newilen -= (int) (s - newicap);
+           newicap = s;
+
+           /* make sure interpolated record is `:'-terminated */
+           s += newilen;
+           if (*(s - 1) != ':') {
+               *s = ':';       /* overwrite NUL with : */
+               newilen++;
+           }
+
+           /*
+            * Make sure there's enough room to insert the
+            * new record.
+            */
+           diff = newilen - tclen;
+           if (diff >= r_end - rp) {
+               unsigned pos, tcpos, tcposend;
+               size_t newsize;
+
+               pos = (unsigned) (rp - record);
+               newsize = (size_t) (r_end - record + diff + BFRAG);
+               tcpos = (unsigned) (tcstart - record);
+               tcposend = (unsigned) (tcend - record);
+               record = DOALLOC(newsize);
+               if (record == 0) {
+                   if (myfd)
+                       (void) close(fd);
+                   free(icap);
+                   errno = ENOMEM;
+                   return (TC_SYS_ERR);
+               }
+               r_end = record + newsize;
+               rp = record + pos;
+               tcstart = record + tcpos;
+               tcend = record + tcposend;
+           }
+
+           /*
+            * Insert tc'ed record into our record.
+            */
+           s = tcstart + newilen;
+           memmove(s, tcend, (size_t) (rp - tcend));
+           memmove(tcstart, newicap, (size_t) newilen);
+           rp += diff;
+           free(icap);
+
+           /*
+            * Start scan on `:' so next cgetcap works properly
+            * (cgetcap always skips first field).
+            */
+           scan = s - 1;
+       }
+    }
+
+    /*
+     * Close file (if we opened it), give back any extra memory, and
+     * return capability, length and success.
+     */
+    if (myfd)
+       (void) close(fd);
+    *len = (unsigned) (rp - record - 1);       /* don't count NUL */
+    if (r_end > rp) {
+       if ((record = DOALLOC((size_t) (rp - record))) == 0) {
+           errno = ENOMEM;
+           return (TC_SYS_ERR);
        }
+    }
 
-       *cap = record;
-       if (tc_not_resolved)
-               return (TC_UNRESOLVED);
-       return (current);
+    *cap = record;
+    if (tc_not_resolved) {
+       return (TC_UNRESOLVED);
+    }
+    return (current);
 }
 
 /*
@@ -592,40 +606,40 @@ _nc_getent(
 static int
 _nc_cgetmatch(char *buf, const char *name)
 {
-       register const char *np;
-       register char *bp;
-
+    register const char *np;
+    register char *bp;
+
+    /*
+     * Start search at beginning of record.
+     */
+    bp = buf;
+    for (;;) {
        /*
-        * Start search at beginning of record.
+        * Try to match a record name.
         */
-       bp = buf;
+       np = name;
        for (;;) {
-               /*
-                * Try to match a record name.
-                */
-               np = name;
-               for (;;) {
-                       if (*np == '\0') {
-                               if (*bp == '|' || *bp == ':' || *bp == '\0')
-                                       return (0);
-                               else
-                                       break;
-                       } else if (*bp++ != *np++) {
-                               break;
-                       }
-               }
+           if (*np == '\0') {
+               if (*bp == '|' || *bp == ':' || *bp == '\0')
+                   return (0);
+               else
+                   break;
+           } else if (*bp++ != *np++) {
+               break;
+           }
+       }
 
-               /*
-                * Match failed, skip to next name in record.
-                */
-               bp--;   /* a '|' or ':' may have stopped the match */
-               for (;;) {
-                       if (*bp == '\0' || *bp == ':')
-                               return (-1);    /* match failed totally */
-                       else if (*bp++ == '|')
-                               break;  /* found next name */
-               }
+       /*
+        * Match failed, skip to next name in record.
+        */
+       bp--;                   /* a '|' or ':' may have stopped the match */
+       for (;;) {
+           if (*bp == '\0' || *bp == ':')
+               return (-1);    /* match failed totally */
+           else if (*bp++ == '|')
+               break;          /* found next name */
        }
+    }
 }
 
 /*
@@ -634,18 +648,17 @@ _nc_cgetmatch(char *buf, const char *name)
 static int
 _nc_nfcmp(const char *nf, char *rec)
 {
-       char *cp, tmp;
-       int ret;
+    char *cp, tmp;
+    int ret;
 
-       for (cp = rec; *cp != ':'; cp++)
-               ;
+    for (cp = rec; *cp != ':'; cp++) ;
 
-       tmp = *(cp + 1);
-       *(cp + 1) = '\0';
-       ret = strcmp(nf, rec);
-       *(cp + 1) = tmp;
+    tmp = *(cp + 1);
+    *(cp + 1) = '\0';
+    ret = strcmp(nf, rec);
+    *(cp + 1) = tmp;
 
-       return (ret);
+    return (ret);
 }
 #endif /* HAVE_BSD_CGETENT */
 
@@ -697,8 +710,6 @@ _nc_nfcmp(const char *nf, char *rec)
 #define        PVECSIZ         32      /* max number of names in path */
 #define TBUFSIZ (2048*2)
 
-static char *tbuf;
-
 /*
  * On entry, srcp points to a non ':' character which is the beginning of the
  * token, if any.  We'll try to return a string that doesn't end with a ':'.
@@ -706,63 +717,63 @@ static char *tbuf;
 static char *
 get_tc_token(char **srcp, int *endp)
 {
-       int ch;
-       bool found = FALSE;
-       char *s, *base;
-       char *tok = 0;
-
-       *endp = TRUE;
-       for (s = base = *srcp; *s != '\0'; ) {
-               ch = *s++;
-               if (ch == '\\') {
-                       if (*s == '\0') {
-                               break;
-                       } else if (*s++ == '\n') {
-                               while (isspace(*s))
-                                       s++;
-                       } else {
-                               found = TRUE;
-                       }
-               } else if (ch == ':') {
-                       if (found) {
-                               tok = base;
-                               s[-1] = '\0';
-                               *srcp = s;
-                               *endp = FALSE;
-                               break;
-                       }
-                       base = s;
-               } else if (isgraph(ch)) {
-                       found = TRUE;
-               }
-       }
-
-       /* malformed entry may end without a ':' */
-       if (tok == 0 && found) {
+    int ch;
+    bool found = FALSE;
+    char *s, *base;
+    char *tok = 0;
+
+    *endp = TRUE;
+    for (s = base = *srcp; *s != '\0';) {
+       ch = *s++;
+       if (ch == '\\') {
+           if (*s == '\0') {
+               break;
+           } else if (*s++ == '\n') {
+               while (isspace(UChar(*s)))
+                   s++;
+           } else {
+               found = TRUE;
+           }
+       } else if (ch == ':') {
+           if (found) {
                tok = base;
+               s[-1] = '\0';
+               *srcp = s;
+               *endp = FALSE;
+               break;
+           }
+           base = s;
+       } else if (isgraph(UChar(ch))) {
+           found = TRUE;
        }
+    }
 
-       return tok;
+    /* malformed entry may end without a ':' */
+    if (tok == 0 && found) {
+       tok = base;
+    }
+
+    return tok;
 }
 
 static char *
 copy_tc_token(char *dst, const char *src, size_t len)
 {
-       int ch;
+    int ch;
 
-       while ((ch = *src++) != '\0') {
-               if (ch == '\\' && *src == '\n') {
-                       while (isspace(*src))
-                               src++;
-                       continue;
-               }
-               if (--len == 0) {
-                       dst = 0;
-                       break;
-               }
-               *dst++ = ch;
+    while ((ch = *src++) != '\0') {
+       if (ch == '\\' && *src == '\n') {
+           while (isspace(UChar(*src)))
+               src++;
+           continue;
        }
-       return dst;
+       if (--len == 0) {
+           dst = 0;
+           break;
+       }
+       *dst++ = (char) ch;
+    }
+    return dst;
 }
 
 /*
@@ -771,127 +782,150 @@ copy_tc_token(char *dst, const char *src, size_t len)
 static int
 _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
 {
-       static char *the_source;
-
-       register char *p;
-       register char *cp;
-       char  *dummy;
-       char **fname;
-       char  *home;
-       int    i;
-       char   pathbuf[PBUFSIZ];        /* holds raw path of filenames */
-       char  *pathvec[PVECSIZ];        /* to point to names in pathbuf */
-       char **pvec;                    /* holds usable tail of path vector */
-       char  *termpath;
-
-       fname = pathvec;
-       pvec = pathvec;
-       tbuf = bp;
-       p = pathbuf;
-       cp = getenv("TERMCAP");
-
-       /*
-        * TERMCAP can have one of two things in it.  It can be the name of a
-        * file to use instead of /etc/termcap.  In this case it better start
-        * with a "/".  Or it can be an entry to use so we don't have to read
-        * the file.  In this case it has to already have the newlines crunched
-        * out.  If TERMCAP does not hold a file name then a path of names is
-        * searched instead.  The path is found in the TERMPATH variable, or
-        * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
-        */
-       if (!is_pathname(cp)) { /* no TERMCAP or it holds an entry */
-               if ((termpath = getenv("TERMPATH")) != 0) {
-                       strncpy(pathbuf, termpath, PBUFSIZ - 1);
-               } else {
-                       if ((home = getenv("HOME")) != 0 &&
-                           strlen(home) < PBUFSIZ) { /* setup path */
-                               p += strlen(home);      /* path, looking in */
-                               strcpy(pathbuf, home);  /* $HOME first */
-                               *p++ = '/';
-                       }       /* if no $HOME look in current directory */
-#define        MY_PATH_DEF     ".termcap /etc/termcap /usr/share/misc/termcap"
-                       strncpy(p, MY_PATH_DEF, (size_t)(PBUFSIZ - (p - pathbuf) - 1));
-               }
+    static char *the_source;
+
+    register char *p;
+    register char *cp;
+    char *dummy = NULL;
+    CGETENT_CONST char **fname;
+    char *home;
+    int i;
+    char pathbuf[PBUFSIZ];     /* holds raw path of filenames */
+    CGETENT_CONST char *pathvec[PVECSIZ];      /* point to names in pathbuf */
+    const char *termpath;
+    string_desc desc;
+
+    *lineno = 1;
+    fname = pathvec;
+    p = pathbuf;
+    cp = use_terminfo_vars()? getenv("TERMCAP") : NULL;
+
+    /*
+     * TERMCAP can have one of two things in it.  It can be the name of a file
+     * to use instead of /etc/termcap.  In this case it better start with a
+     * "/".  Or it can be an entry to use so we don't have to read the file.
+     * In this case it has to already have the newlines crunched out.  If
+     * TERMCAP does not hold a file name then a path of names is searched
+     * instead.  The path is found in the TERMPATH variable, or becomes
+     * "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
+     */
+    _nc_str_init(&desc, pathbuf, sizeof(pathbuf));
+    if (cp == NULL) {
+       _nc_safe_strcpy(&desc, get_termpath());
+    } else if (!_nc_is_abs_path(cp)) { /* TERMCAP holds an entry */
+       if ((termpath = get_termpath()) != 0) {
+           _nc_safe_strcat(&desc, termpath);
+       } else {
+           char temp[PBUFSIZ];
+           temp[0] = 0;
+           if ((home = getenv("HOME")) != 0 && *home != '\0'
+               && strchr(home, ' ') == 0
+               && strlen(home) < sizeof(temp) - 10) {  /* setup path */
+               _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
+                           "%s/", home);       /* $HOME first */
+           }
+           /* if no $HOME look in current directory */
+           _nc_STRCAT(temp, ".termcap", sizeof(temp));
+           _nc_safe_strcat(&desc, temp);
+           _nc_safe_strcat(&desc, " ");
+           _nc_safe_strcat(&desc, get_termpath());
        }
-       else                            /* user-defined name in TERMCAP */
-               strncpy(pathbuf, cp, PBUFSIZ - 1); /* still can be tokenized */
-       pathbuf[PBUFSIZ - 1] = '\0';
-
-       *fname++ = pathbuf;     /* tokenize path into vector of names */
-       while (*++p) {
-               if (*p == ' ' || *p == ':') {
-                       *p = '\0';
-                       while (*++p)
-                               if (*p != ' ' && *p != ':')
-                                       break;
-                       if (*p == '\0')
-                               break;
-                       *fname++ = p;
-                       if (fname >= pathvec + PVECSIZ) {
-                               fname--;
-                               break;
-                       }
-               }
+    } else {                   /* user-defined name in TERMCAP */
+       _nc_safe_strcat(&desc, cp);     /* still can be tokenized */
+    }
+
+    *fname++ = pathbuf;                /* tokenize path into vector of names */
+    while (*++p) {
+       if (*p == ' ' || *p == NCURSES_PATHSEP) {
+           *p = '\0';
+           while (*++p)
+               if (*p != ' ' && *p != NCURSES_PATHSEP)
+                   break;
+           if (*p == '\0')
+               break;
+           *fname++ = p;
+           if (fname >= pathvec + PVECSIZ) {
+               fname--;
+               break;
+           }
        }
-       *fname = 0;                     /* mark end of vector */
-       if (is_pathname(cp)) {
-               if (_nc_cgetset(cp) < 0) {
-                       return(TC_SYS_ERR);
-               }
+    }
+    *fname = 0;                        /* mark end of vector */
+#if !HAVE_BSD_CGETENT
+    (void) _nc_cgetset(0);
+#endif
+    if (_nc_is_abs_path(cp)) {
+       if (_nc_cgetset(cp) < 0) {
+           return (TC_SYS_ERR);
        }
-
-       i = _nc_cgetent(&dummy, lineno, pathvec, name);
-
-       /* ncurses' termcap-parsing routines cannot handle multiple adjacent
-        * empty fields, and mistakenly use the last valid cap entry instead of
-        * the first (breaks tc= includes)
-        */
-       if (i >= 0) {
-               char *pd, *ps, *tok;
-               int endflag = FALSE;
-               char *list[1023];
-               size_t n, count = 0;
-
-               pd = bp;
-               ps = dummy;
-               while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) {
-                       bool ignore = FALSE;
-
-                       for (n = 1; n < count; n++) {
-                               char *s = list[n];
-                               if (s[0] == tok[0]
-                                && s[1] == tok[1]) {
-                                       ignore = TRUE;
-                                       break;
-                               }
-                       }
-                       if (ignore != TRUE) {
-                               list[count++] = tok;
-                               pd = copy_tc_token(pd, tok, TBUFSIZ - (2+pd-bp));
-                               if (pd == 0) {
-                                       i = -1;
-                                       break;
-                               }
-                               *pd++ = ':';
-                               *pd = '\0';
-                       }
+    }
+
+    i = _nc_cgetent(&dummy, lineno, pathvec, name);
+
+    /* ncurses' termcap-parsing routines cannot handle multiple adjacent
+     * empty fields, and mistakenly use the last valid cap entry instead of
+     * the first (breaks tc= includes)
+     */
+    *bp = '\0';
+    if (i >= 0) {
+       char *pd, *ps, *tok;
+       int endflag = FALSE;
+       char *list[1023];
+       size_t n, count = 0;
+
+       pd = bp;
+       ps = dummy;
+       while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) {
+           bool ignore = FALSE;
+
+           for (n = 1; n < count; n++) {
+               char *s = list[n];
+               if (s[0] == tok[0]
+                   && s[1] == tok[1]) {
+                   ignore = TRUE;
+                   break;
+               }
+           }
+           if (ignore != TRUE) {
+               list[count++] = tok;
+               pd = copy_tc_token(pd, tok, (size_t) (TBUFSIZ - (2 + pd - bp)));
+               if (pd == 0) {
+                   i = -1;
+                   break;
                }
+               *pd++ = ':';
+               *pd = '\0';
+           }
        }
+    }
 
-       FreeIfNeeded(dummy);
-       FreeIfNeeded(the_source);
-       the_source = 0;
+    FreeIfNeeded(dummy);
+    FreeIfNeeded(the_source);
+    the_source = 0;
 
-       /* This is not related to the BSD cgetent(), but to fake up a suitable
-        * filename for ncurses' error reporting.  (If we are not using BSD
-        * cgetent, then it is the actual filename).
-        */
-       if (i >= 0) {
-               if ((the_source = strdup(pathvec[i])) != 0)
-                       *sourcename = the_source;
+    /* This is not related to the BSD cgetent(), but to fake up a suitable
+     * filename for ncurses' error reporting.  (If we are not using BSD
+     * cgetent, then it is the actual filename).
+     */
+    if (i >= 0) {
+#if HAVE_BSD_CGETENT
+       char temp[PATH_MAX];
+
+       _nc_str_init(&desc, temp, sizeof(temp));
+       _nc_safe_strcpy(&desc, pathvec[i]);
+       _nc_safe_strcat(&desc, ".db");
+       if (_nc_access(temp, R_OK) == 0) {
+           _nc_safe_strcpy(&desc, pathvec[i]);
        }
+       if ((the_source = strdup(temp)) != 0)
+           *sourcename = the_source;
+#else
+       if ((the_source = strdup(pathvec[i])) != 0)
+           *sourcename = the_source;
+#endif
+    }
 
-       return(i);
+    return (i);
 }
 #endif /* USE_BSD_TGETENT */
 #endif /* USE_GETCAP */
@@ -903,214 +937,266 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
  * a right to open the file.
  */
 #if !USE_GETCAP
-static int add_tc(char *termpaths[], char *path, int count)
+static int
+add_tc(char *termpaths[], char *path, int count)
 {
-       if (count < MAXPATHS
-        && _nc_access(path, R_OK) == 0)
-               termpaths[count++] = path;
-       termpaths[count] = 0;
-       return count;
+    char *save = strchr(path, NCURSES_PATHSEP);
+    if (save != 0)
+       *save = '\0';
+    if (count < MAXPATHS
+       && _nc_access(path, R_OK) == 0) {
+       termpaths[count++] = path;
+       TR(TRACE_DATABASE, ("Adding termpath %s", path));
+    }
+    termpaths[count] = 0;
+    if (save != 0)
+       *save = NCURSES_PATHSEP;
+    return count;
 }
 #define ADD_TC(path, count) filecount = add_tc(termpaths, path, count)
 #endif /* !USE_GETCAP */
 
-int _nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
+NCURSES_EXPORT(int)
+_nc_read_termcap_entry(const char *const tn, TERMTYPE2 *const tp)
 {
-       int found = FALSE;
-       ENTRY   *ep;
+    int found = TGETENT_NO;
+    ENTRY *ep;
 #if USE_GETCAP_CACHE
-       char    cwd_buf[PATH_MAX];
+    char cwd_buf[PATH_MAX];
 #endif
 #if USE_GETCAP
-       char    tc[TBUFSIZ];
-       static char     *source;
-       static int lineno;
-
+    char *p, tc[TBUFSIZ];
+    char *tc_buf = 0;
+#define MY_SIZE sizeof(tc) - 1
+    int status;
+    static char *source;
+    static int lineno;
+
+    TR(TRACE_DATABASE, ("read termcap entry for %s", tn));
+
+    if (strlen(tn) == 0
+       || strcmp(tn, ".") == 0
+       || strcmp(tn, "..") == 0
+       || _nc_pathlast(tn) != 0) {
+       TR(TRACE_DATABASE, ("illegal or missing entry name '%s'", tn));
+       return TGETENT_NO;
+    }
+
+    if (use_terminfo_vars() && (p = getenv("TERMCAP")) != 0
+       && !_nc_is_abs_path(p) && _nc_name_match(p, tn, "|:")) {
+       /* TERMCAP holds a termcap entry */
+       tc_buf = strdup(p);
+       _nc_set_source("TERMCAP");
+    } else {
        /* we're using getcap(3) */
-       if (_nc_tgetent(tc, &source, &lineno, tn) < 0)
-               return (ERR);
+       if ((status = _nc_tgetent(tc, &source, &lineno, tn)) < 0)
+           return (status == TC_NOT_FOUND ? TGETENT_NO : TGETENT_ERR);
 
        _nc_curr_line = lineno;
        _nc_set_source(source);
-       _nc_read_entry_source((FILE *)0, tc, FALSE, FALSE, NULLHOOK);
+       tc_buf = tc;
+    }
+    if (tc_buf == 0)
+       return (TGETENT_ERR);
+    _nc_read_entry_source((FILE *) 0, tc_buf, FALSE, TRUE, NULLHOOK);
+    if (tc_buf != tc)
+       free(tc_buf);
 #else
-       /*
-        * Here is what the 4.4BSD termcap(3) page prescribes:
-        *
-        * It will look in the environment for a TERMCAP variable.  If found,
-        * and the value does not begin with a slash, and the terminal type
-        * name is the same as the environment string TERM, the TERMCAP string
-        * is used instead of reading a termcap file.  If it does begin with a
-        * slash, the string is used as a path name of the termcap file to
-        * search.  If TERMCAP does not begin with a slash and name is
-        * different from TERM, tgetent() searches the files $HOME/.termcap and
-        * /usr/share/misc/termcap, in that order, unless the environment
-        * variable TERMPATH exists, in which case it specifies a list of file
-        * pathnames (separated by spaces or colons) to be searched instead.
-        *
-        * It goes on to state:
-        *
-        * Whenever multiple files are searched and a tc field occurs in the
-        * requested entry, the entry it names must be found in the same file
-        * or one of the succeeding files.
-        *
-        * However, this restriction is relaxed in ncurses; tc references to
-        * previous files are permitted.
-        *
-        * This routine returns 1 if an entry is found, 0 if not found, and -1
-        * if the database is not accessible.
-        */
-       FILE    *fp;
-       char    *tc, *termpaths[MAXPATHS];
-       int     filecount = 0;
-       bool    use_buffer = FALSE;
-       char    tc_buf[1024];
-       char    pathbuf[PATH_MAX];
-
-       termpaths[filecount] = 0;
-       if ((tc = getenv("TERMCAP")) != 0)
-       {
-               if (is_pathname(tc))    /* interpret as a filename */
-               {
-                       ADD_TC(tc, 0);
-               }
-               else if (_nc_name_match(tc, tn, "|:")) /* treat as a capability file */
-               {
-                       use_buffer = TRUE;
-                       (void) sprintf(tc_buf, "%.*s\n", (int)sizeof(tc_buf)-2, tc);
-               }
-               else if ((tc = getenv("TERMPATH")) != 0)
-               {
-                       char    *cp;
-
-                       for (cp = tc; *cp; cp++)
-                       {
-                               if (*cp == ':')
-                                       *cp = '\0';
-                               else if (cp == tc || cp[-1] == '\0')
-                               {
-                                       ADD_TC(cp, filecount);
-                               }
-                       }
-               }
+    /*
+     * Here is what the 4.4BSD termcap(3) page prescribes:
+     *
+     * It will look in the environment for a TERMCAP variable.  If found, and
+     * the value does not begin with a slash, and the terminal type name is the
+     * same as the environment string TERM, the TERMCAP string is used instead
+     * of reading a termcap file.  If it does begin with a slash, the string is
+     * used as a path name of the termcap file to search.  If TERMCAP does not
+     * begin with a slash and name is different from TERM, tgetent() searches
+     * the files $HOME/.termcap and /usr/share/misc/termcap, in that order,
+     * unless the environment variable TERMPATH exists, in which case it
+     * specifies a list of file pathnames (separated by spaces or colons) to be
+     * searched instead.
+     *
+     * It goes on to state:
+     *
+     * Whenever multiple files are searched and a tc field occurs in the
+     * requested entry, the entry it names must be found in the same file or
+     * one of the succeeding files.
+     *
+     * However, this restriction is relaxed in ncurses; tc references to
+     * previous files are permitted.
+     *
+     * This routine returns 1 if an entry is found, 0 if not found, and -1 if
+     * the database is not accessible.
+     */
+    FILE *fp;
+    char *tc, *termpaths[MAXPATHS];
+    int filecount = 0;
+    int j, k;
+    bool use_buffer = FALSE;
+    bool normal = TRUE;
+    char *tc_buf = 0;
+    char pathbuf[PATH_MAX];
+    char *copied = 0;
+    char *cp;
+    struct stat test_stat[MAXPATHS];
+
+    termpaths[filecount] = 0;
+    if (use_terminfo_vars() && (tc = getenv("TERMCAP")) != 0) {
+       if (_nc_is_abs_path(tc)) {      /* interpret as a filename */
+           ADD_TC(tc, 0);
+           normal = FALSE;
+       } else if (_nc_name_match(tc, tn, "|:")) {      /* treat as a capability file */
+           tc_buf = strdup(tc);
+           use_buffer = (tc_buf != 0);
+           normal = FALSE;
+       }
+    }
+
+    if (normal) {              /* normal case */
+       char envhome[PATH_MAX], *h;
+
+       copied = strdup(get_termpath());
+       for (cp = copied; *cp; cp++) {
+           if (*cp == NCURSES_PATHSEP)
+               *cp = '\0';
+           else if (cp == copied || cp[-1] == '\0') {
+               ADD_TC(cp, filecount);
+           }
        }
-       else    /* normal case */
-       {
-               char    envhome[PATH_MAX], *h;
-
-               filecount = 0;
-
-               /*
-                * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
-                * Avoid reading the same file twice.
-                */
-               if (_nc_access("/etc/termcap", F_OK) == 0)
-                       ADD_TC("/etc/termcap", filecount);
-               else
-                       ADD_TC("/usr/share/misc/termcap", filecount);
-
-#define PRIVATE_CAP "%s/.termcap"
 
-               if ((h = getenv("HOME")) != NULL
-                && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX)
-               {
-                   /* user's .termcap, if any, should override it */
-                   (void) strcpy(envhome, h);
-                   (void) sprintf(pathbuf, PRIVATE_CAP, envhome);
-                   ADD_TC(pathbuf, filecount);
+#define PRIVATE_CAP "%.*s/.termcap"
+
+       if (use_terminfo_vars() && (h = getenv("HOME")) != NULL && *h != '\0'
+           && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX) {
+           /* user's .termcap, if any, should override it */
+           _nc_STRCPY(envhome, h, sizeof(envhome));
+           _nc_SPRINTF(pathbuf, _nc_SLIMIT(sizeof(pathbuf))
+                       PRIVATE_CAP,
+                       (int) (sizeof(pathbuf) - sizeof(PRIVATE_CAP)),
+                       envhome);
+           ADD_TC(pathbuf, filecount);
+       }
+    }
+
+    /*
+     * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
+     * Avoid reading the same file twice.
+     */
+#if HAVE_LINK
+    for (j = 0; j < filecount; j++) {
+       bool omit = FALSE;
+       if (stat(termpaths[j], &test_stat[j]) != 0
+           || !S_ISREG(test_stat[j].st_mode)) {
+           omit = TRUE;
+       } else {
+           for (k = 0; k < j; k++) {
+               if (test_stat[k].st_dev == test_stat[j].st_dev
+                   && test_stat[k].st_ino == test_stat[j].st_ino) {
+                   omit = TRUE;
+                   break;
                }
+           }
        }
+       if (omit) {
+           TR(TRACE_DATABASE, ("Path %s is a duplicate", termpaths[j]));
+           for (k = j + 1; k < filecount; k++) {
+               termpaths[k - 1] = termpaths[k];
+               test_stat[k - 1] = test_stat[k];
+           }
+           --filecount;
+           --j;
+       }
+    }
+#endif
 
-       /* parse the sources */
-       if (use_buffer)
-       {
-               _nc_set_source("TERMCAP");
+    /* parse the sources */
+    if (use_buffer) {
+       _nc_set_source("TERMCAP");
 
-               /*
-                * We don't suppress warning messages here.  The presumption is
-                * that since it's just a single entry, they won't be a pain.
-                */
-               _nc_read_entry_source((FILE *)0, tc_buf, FALSE, FALSE, NULLHOOK);
-       } else {
-               int     i;
+       /*
+        * We don't suppress warning messages here.  The presumption is
+        * that since it's just a single entry, they won't be a pain.
+        */
+       _nc_read_entry_source((FILE *) 0, tc_buf, FALSE, FALSE, NULLHOOK);
+       free(tc_buf);
+    } else {
+       int i;
 
-               for (i = 0; i < filecount; i++) {
+       for (i = 0; i < filecount; i++) {
 
-                       T(("Looking for %s in %s", tn, termpaths[i]));
-                       if ((fp = fopen(termpaths[i], "r")) != (FILE *)0)
-                       {
-                               _nc_set_source(termpaths[i]);
+           TR(TRACE_DATABASE, ("Looking for %s in %s", tn, termpaths[i]));
+           if (_nc_access(termpaths[i], R_OK) == 0
+               && (fp = fopen(termpaths[i], "r")) != (FILE *) 0) {
+               _nc_set_source(termpaths[i]);
 
-                               /*
-                                * Suppress warning messages.  Otherwise you
-                                * get 400 lines of crap from archaic termcap
-                                * files as ncurses complains about all the
-                                * obsolete capabilities.
-                                */
-                               _nc_read_entry_source(fp, (char*)0, FALSE, TRUE, NULLHOOK);
+               /*
+                * Suppress warning messages.  Otherwise you get 400 lines of
+                * crap from archaic termcap files as ncurses complains about
+                * all the obsolete capabilities.
+                */
+               _nc_read_entry_source(fp, (char *) 0, FALSE, TRUE, NULLHOOK);
 
-                               (void) fclose(fp);
-                       }
-               }
+               (void) fclose(fp);
+           }
        }
+    }
+    if (copied != 0)
+       free(copied);
 #endif /* USE_GETCAP */
 
-       if (_nc_head == 0)
-               return(ERR);
+    if (_nc_head == 0)
+       return (TGETENT_ERR);
 
-       /* resolve all use references */
-       _nc_resolve_uses();
+    /* resolve all use references */
+    if (_nc_resolve_uses2(TRUE, FALSE) != TRUE)
+       return (TGETENT_ERR);
 
-       /* find a terminal matching tn, if we can */
+    /* find a terminal matching tn, if we can */
 #if USE_GETCAP_CACHE
-       if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0)
-       {
-               _nc_set_writedir((char *)0); /* note: this does a chdir */
+    if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0) {
+       _nc_set_writedir((char *) 0);   /* note: this does a chdir */
 #endif
-               for_entry_list(ep) {
-                       if (_nc_name_match(ep->tterm.term_names, tn, "|:"))
-                       {
-                               /*
-                                * Make a local copy of the terminal
-                                * capabilities.  Free all entry storage except
-                                * the string table for the loaded type (which
-                                * we disconnected from the list by NULLing out
-                                * ep->tterm.str_table above).
-                                */
-                               *tp = ep->tterm;
-                               ep->tterm.str_table = (char *)0;
-
-                               /*
-                                * OK, now try to write the type to user's
-                                * terminfo directory.  Next time he loads
-                                * this, it will come through terminfo.
-                                *
-                                * Advantage:  Second and subsequent fetches of
-                                * this entry will be very fast.
-                                *
-                                * Disadvantage:  After the first time a
-                                * termcap type is loaded by its user, editing
-                                * it in the /etc/termcap file, or in TERMCAP,
-                                * or in a local ~/.termcap, will be
-                                * ineffective unless the terminfo entry is
-                                * explicitly removed.
-                                */
+       for_entry_list(ep) {
+           if (_nc_name_match(ep->tterm.term_names, tn, "|:")) {
+               /*
+                * Make a local copy of the terminal capabilities, delinked
+                * from the list.
+                */
+               *tp = ep->tterm;
+               _nc_free_entry(_nc_head, &(ep->tterm));
+
+               /*
+                * OK, now try to write the type to user's terminfo directory.
+                * Next time he loads this, it will come through terminfo.
+                *
+                * Advantage:  Second and subsequent fetches of this entry will
+                * be very fast.
+                *
+                * Disadvantage:  After the first time a termcap type is loaded
+                * by its user, editing it in the /etc/termcap file, or in
+                * TERMCAP, or in a local ~/.termcap, will be ineffective
+                * unless the terminfo entry is explicitly removed.
+                */
 #if USE_GETCAP_CACHE
-                               (void) _nc_write_entry(tp);
+               (void) _nc_write_entry(tp);
 #endif
-                               found = TRUE;
-                               break;
-                       }
-               }
-#if USE_GETCAP_CACHE
-               chdir(cwd_buf);
+               found = TGETENT_YES;
+               break;
+           }
        }
+#if USE_GETCAP_CACHE
+       chdir(cwd_buf);
+    }
 #endif
 
-       _nc_free_entries(_nc_head);
-       return(found);
+    return (found);
 }
 #else
-extern void _nc_read_termcap(void);
-       void _nc_read_termcap(void) { }
-#endif /* PURE_TERMINFO */
+extern
+NCURSES_EXPORT(void)
+_nc_read_termcap(void);
+NCURSES_EXPORT(void)
+_nc_read_termcap(void)
+{
+}
+#endif /* PURE_TERMINFO */