150276Speter/****************************************************************************
2166124Srafan * Copyright (c) 1998-2005,2006 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey                        1996-on                 *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter * Termcap compatibility support
3750276Speter *
3850276Speter * If your OS integrator didn't install a terminfo database, you can call
3950276Speter * _nc_read_termcap_entry() to support reading and translating capabilities
4050276Speter * from the system termcap file.  This is a kludge; it will bulk up and slow
4150276Speter * down every program that uses ncurses, and translated termcap entries cannot
4250276Speter * use full terminfo capabilities.  Don't use it unless you absolutely have to;
4350276Speter * instead, get your system people to run tic(1) from root on the terminfo
4450276Speter * master included with ncurses to translate it into a terminfo database.
4550276Speter *
4650276Speter * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD
4750276Speter * getcap code to fetch entries.  There are disadvantages to this; mainly that
4850276Speter * getcap(3) does its own resolution, meaning that entries read in in this way
4950276Speter * can't reference the terminfo tree.  The only thing it buys is faster startup
5050276Speter * time, getcap(3) is much faster than our tic parser.
5150276Speter */
5250276Speter
5350276Speter#include <curses.priv.h>
5450276Speter
5550276Speter#include <ctype.h>
5697049Speter#include <sys/types.h>
5797049Speter#include <sys/stat.h>
5850276Speter#include <tic.h>
5950276Speter#include <term_entry.h>
6050276Speter
61166124SrafanMODULE_ID("$Id: read_termcap.c,v 1.71 2006/07/29 12:06:51 tom Exp $")
6250276Speter
6366963Speter#if !PURE_TERMINFO
6450276Speter
6550276Speter#define TC_SUCCESS     0
66166124Srafan#define TC_NOT_FOUND  -1
67166124Srafan#define TC_SYS_ERR    -2
68166124Srafan#define TC_REF_LOOP   -3
69166124Srafan#define TC_UNRESOLVED -4	/* this is not returned by BSD cgetent */
7050276Speter
71166124Srafanstatic NCURSES_CONST char *
7297049Speterget_termpath(void)
7397049Speter{
74166124Srafan    NCURSES_CONST char *result;
7597049Speter
7697049Speter    if (!use_terminfo_vars() || (result = getenv("TERMPATH")) == 0)
7797049Speter	result = TERMPATH;
7897049Speter    T(("TERMPATH is %s", result));
7997049Speter    return result;
8097049Speter}
8197049Speter
8250276Speter#if USE_GETCAP
8350276Speter
8450276Speter#if HAVE_BSD_CGETENT
8550276Speter#define _nc_cgetcap   cgetcap
8650276Speter#define _nc_cgetent(buf, oline, db_array, name) cgetent(buf, db_array, name)
8750276Speter#define _nc_cgetmatch cgetmatch
8850276Speter#define _nc_cgetset   cgetset
8950276Speter#else
9050276Speterstatic int _nc_cgetmatch(char *, const char *);
9166963Speterstatic int _nc_getent(char **, unsigned *, int *, int, char **, int, const char
9266963Speter		      *, int, char *);
9350276Speterstatic int _nc_nfcmp(const char *, char *);
9450276Speter
9550276Speter/*-
9650276Speter * Copyright (c) 1992, 1993
9750276Speter *	The Regents of the University of California.  All rights reserved.
9850276Speter *
9950276Speter * This code is derived from software contributed to Berkeley by
10050276Speter * Casey Leedom of Lawrence Livermore National Laboratory.
10150276Speter *
10250276Speter * Redistribution and use in source and binary forms, with or without
10350276Speter * modification, are permitted provided that the following conditions
10450276Speter * are met:
10550276Speter * 1. Redistributions of source code must retain the above copyright
10650276Speter *    notice, this list of conditions and the following disclaimer.
10750276Speter * 2. Redistributions in binary form must reproduce the above copyright
10850276Speter *    notice, this list of conditions and the following disclaimer in the
10950276Speter *    documentation and/or other materials provided with the distribution.
11050276Speter * 3. All advertising materials mentioning features or use of this software
11150276Speter *    must display the following acknowledgment:
11250276Speter *	This product includes software developed by the University of
11350276Speter *	California, Berkeley and its contributors.
11450276Speter * 4. Neither the name of the University nor the names of its contributors
11550276Speter *    may be used to endorse or promote products derived from this software
11650276Speter *    without specific prior written permission.
11750276Speter *
11850276Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
11950276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12050276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12150276Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12250276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12350276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12450276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12550276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12650276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12750276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12850276Speter * SUCH DAMAGE.
12950276Speter */
13050276Speter
13150276Speter/* static char sccsid[] = "@(#)getcap.c	8.3 (Berkeley) 3/25/94"; */
13250276Speter
13350276Speter#define	BFRAG		1024
13450276Speter#define	BSIZE		1024
13562449Speter#define	MAX_RECURSION	32	/* maximum getent recursion */
13650276Speter
13762449Speterstatic size_t topreclen;	/* toprec length */
13862449Speterstatic char *toprec;		/* Additional record specified by cgetset() */
13962449Speterstatic int gottoprec;		/* Flag indicating retrieval of toprecord */
14050276Speter
14150276Speter/*
14250276Speter * Cgetset() allows the addition of a user specified buffer to be added to the
14350276Speter * database array, in effect "pushing" the buffer on top of the virtual
14450276Speter * database.  0 is returned on success, -1 on failure.
14550276Speter */
14650276Speterstatic int
14750276Speter_nc_cgetset(const char *ent)
14850276Speter{
14962449Speter    if (ent == 0) {
15062449Speter	FreeIfNeeded(toprec);
15162449Speter	toprec = 0;
15262449Speter	topreclen = 0;
15350276Speter	return (0);
15462449Speter    }
15562449Speter    topreclen = strlen(ent);
15662449Speter    if ((toprec = typeMalloc(char, topreclen + 1)) == 0) {
15762449Speter	errno = ENOMEM;
15862449Speter	return (-1);
15962449Speter    }
16062449Speter    gottoprec = 0;
16162449Speter    (void) strcpy(toprec, ent);
16262449Speter    return (0);
16350276Speter}
16450276Speter
16550276Speter/*
16650276Speter * Cgetcap searches the capability record buf for the capability cap with type
16750276Speter * `type'.  A pointer to the value of cap is returned on success, 0 if the
16850276Speter * requested capability couldn't be found.
16950276Speter *
17050276Speter * Specifying a type of ':' means that nothing should follow cap (:cap:).  In
17150276Speter * this case a pointer to the terminating ':' or NUL will be returned if cap is
17250276Speter * found.
17350276Speter *
17450276Speter * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
17550276Speter * return 0.
17650276Speter */
17750276Speterstatic char *
17850276Speter_nc_cgetcap(char *buf, const char *cap, int type)
17950276Speter{
18062449Speter    register const char *cp;
18162449Speter    register char *bp;
18250276Speter
18362449Speter    bp = buf;
18462449Speter    for (;;) {
18562449Speter	/*
18662449Speter	 * Skip past the current capability field - it's either the
18762449Speter	 * name field if this is the first time through the loop, or
18862449Speter	 * the remainder of a field whose name failed to match cap.
18962449Speter	 */
19050276Speter	for (;;) {
19162449Speter	    if (*bp == '\0')
19262449Speter		return (0);
19362449Speter	    else if (*bp++ == ':')
19462449Speter		break;
19562449Speter	}
19650276Speter
19762449Speter	/*
19862449Speter	 * Try to match (cap, type) in buf.
19962449Speter	 */
20062449Speter	for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
20162449Speter	    continue;
20262449Speter	if (*cp != '\0')
20362449Speter	    continue;
20462449Speter	if (*bp == '@')
20562449Speter	    return (0);
20662449Speter	if (type == ':') {
20762449Speter	    if (*bp != '\0' && *bp != ':')
20862449Speter		continue;
20962449Speter	    return (bp);
21050276Speter	}
21162449Speter	if (*bp != type)
21262449Speter	    continue;
21362449Speter	bp++;
21462449Speter	return (*bp == '@' ? 0 : bp);
21562449Speter    }
21662449Speter    /* NOTREACHED */
21750276Speter}
21850276Speter
21950276Speter/*
22050276Speter * Cgetent extracts the capability record name from the NULL terminated file
22150276Speter * array db_array and returns a pointer to a malloc'd copy of it in buf.  Buf
22250276Speter * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag,
22350276Speter * and cgetstr, but may then be freed.
22450276Speter *
22550276Speter * Returns:
22650276Speter *
22750276Speter * positive #    on success (i.e., the index in db_array)
22850276Speter * TC_NOT_FOUND  if the requested record couldn't be found
22950276Speter * TC_SYS_ERR    if a system error was encountered (e.g.,couldn't open a file)
23050276Speter * TC_REF_LOOP   if a potential reference loop is detected
231166124Srafan * TC_UNRESOLVED if we had too many recurrences to resolve
23250276Speter */
23350276Speterstatic int
23450276Speter_nc_cgetent(char **buf, int *oline, char **db_array, const char *name)
23550276Speter{
23662449Speter    unsigned dummy;
23750276Speter
23862449Speter    return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
23950276Speter}
24050276Speter
24150276Speter/*
24250276Speter * Getent implements the functions of cgetent.  If fd is non-negative,
24350276Speter * *db_array has already been opened and fd is the open file descriptor.  We
24450276Speter * do this to save time and avoid using up file descriptors for tc=
24550276Speter * recursions.
24650276Speter *
24750276Speter * Getent returns the same success/failure codes as cgetent.  On success, a
24850276Speter * pointer to a malloc'd capability record with all tc= capabilities fully
24950276Speter * expanded and its length (not including trailing ASCII NUL) are left in
25050276Speter * *cap and *len.
25150276Speter *
25250276Speter * Basic algorithm:
25350276Speter *	+ Allocate memory incrementally as needed in chunks of size BFRAG
25450276Speter *	  for capability buffer.
25550276Speter *	+ Recurse for each tc=name and interpolate result.  Stop when all
25650276Speter *	  names interpolated, a name can't be found, or depth exceeds
25750276Speter *	  MAX_RECURSION.
25850276Speter */
25950276Speter#define DOALLOC(size) typeRealloc(char, size, record)
26050276Speterstatic int
26150276Speter_nc_getent(
26266963Speter	      char **cap,	/* termcap-content */
26366963Speter	      unsigned *len,	/* length, needed for recursion */
26466963Speter	      int *beginning,	/* line-number at match */
26566963Speter	      int in_array,	/* index in 'db_array[] */
26666963Speter	      char **db_array,	/* list of files to search */
26766963Speter	      int fd,
26866963Speter	      const char *name,
26966963Speter	      int depth,
27066963Speter	      char *nfield)
27150276Speter{
27262449Speter    register char *r_end, *rp;
27362449Speter    int myfd = FALSE;
27462449Speter    char *record = 0;
27562449Speter    int tc_not_resolved;
27662449Speter    int current;
27762449Speter    int lineno;
27850276Speter
27962449Speter    /*
28062449Speter     * Return with ``loop detected'' error if we've recurred more than
28162449Speter     * MAX_RECURSION times.
28262449Speter     */
28362449Speter    if (depth > MAX_RECURSION)
28462449Speter	return (TC_REF_LOOP);
28562449Speter
28662449Speter    /*
28762449Speter     * Check if we have a top record from cgetset().
28862449Speter     */
28962449Speter    if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) {
29062449Speter	if ((record = DOALLOC(topreclen + BFRAG)) == 0) {
29162449Speter	    errno = ENOMEM;
29262449Speter	    return (TC_SYS_ERR);
29362449Speter	}
29462449Speter	(void) strcpy(record, toprec);
29562449Speter	rp = record + topreclen + 1;
29662449Speter	r_end = rp + BFRAG;
29762449Speter	current = in_array;
29862449Speter    } else {
29962449Speter	int foundit;
30062449Speter
30150276Speter	/*
30262449Speter	 * Allocate first chunk of memory.
30350276Speter	 */
30462449Speter	if ((record = DOALLOC(BFRAG)) == 0) {
30562449Speter	    errno = ENOMEM;
30662449Speter	    return (TC_SYS_ERR);
30762449Speter	}
30862449Speter	rp = r_end = record + BFRAG;
30962449Speter	foundit = FALSE;
31050276Speter
31150276Speter	/*
31262449Speter	 * Loop through database array until finding the record.
31350276Speter	 */
31462449Speter	for (current = in_array; db_array[current] != 0; current++) {
31562449Speter	    int eof = FALSE;
31650276Speter
31762449Speter	    /*
31862449Speter	     * Open database if not already open.
31962449Speter	     */
32062449Speter	    if (fd >= 0) {
32162449Speter		(void) lseek(fd, (off_t) 0, SEEK_SET);
32262449Speter	    } else if ((_nc_access(db_array[current], R_OK) < 0)
32366963Speter		       || (fd = open(db_array[current], O_RDONLY, 0)) < 0) {
32462449Speter		/* No error on unfound file. */
32562449Speter		if (errno == ENOENT)
32662449Speter		    continue;
32762449Speter		free(record);
32862449Speter		return (TC_SYS_ERR);
32962449Speter	    } else {
33062449Speter		myfd = TRUE;
33162449Speter	    }
33262449Speter	    lineno = 0;
33350276Speter
33462449Speter	    /*
33562449Speter	     * Find the requested capability record ...
33662449Speter	     */
33762449Speter	    {
33862449Speter		char buf[2048];
33962449Speter		register char *b_end = buf;
34062449Speter		register char *bp = buf;
34162449Speter		register int c;
34262449Speter
34350276Speter		/*
34462449Speter		 * Loop invariants:
34562449Speter		 *      There is always room for one more character in record.
34662449Speter		 *      R_end always points just past end of record.
34762449Speter		 *      Rp always points just past last character in record.
34862449Speter		 *      B_end always points just past last character in buf.
34962449Speter		 *      Bp always points at next character in buf.
35050276Speter		 */
35150276Speter
35262449Speter		for (;;) {
35362449Speter		    int first = lineno + 1;
35462449Speter
35562449Speter		    /*
35662449Speter		     * Read in a line implementing (\, newline)
35762449Speter		     * line continuation.
35862449Speter		     */
35962449Speter		    rp = record;
36062449Speter		    for (;;) {
36162449Speter			if (bp >= b_end) {
36262449Speter			    int n;
36362449Speter
36462449Speter			    n = read(fd, buf, sizeof(buf));
36562449Speter			    if (n <= 0) {
36662449Speter				if (myfd)
36762449Speter				    (void) close(fd);
36862449Speter				if (n < 0) {
36962449Speter				    free(record);
37062449Speter				    return (TC_SYS_ERR);
37162449Speter				}
37262449Speter				fd = -1;
37362449Speter				eof = TRUE;
37462449Speter				break;
37562449Speter			    }
37662449Speter			    b_end = buf + n;
37762449Speter			    bp = buf;
37850276Speter			}
37950276Speter
38062449Speter			c = *bp++;
38162449Speter			if (c == '\n') {
38262449Speter			    lineno++;
38362449Speter			    if (rp == record || *(rp - 1) != '\\')
38462449Speter				break;
38562449Speter			}
38662449Speter			*rp++ = c;
38762449Speter
38850276Speter			/*
38962449Speter			 * Enforce loop invariant: if no room
39062449Speter			 * left in record buffer, try to get
39162449Speter			 * some more.
39250276Speter			 */
39362449Speter			if (rp >= r_end) {
39462449Speter			    unsigned pos;
39562449Speter			    size_t newsize;
39650276Speter
39762449Speter			    pos = rp - record;
39862449Speter			    newsize = r_end - record + BFRAG;
39962449Speter			    record = DOALLOC(newsize);
40062449Speter			    if (record == 0) {
40162449Speter				if (myfd)
40262449Speter				    (void) close(fd);
40362449Speter				errno = ENOMEM;
40462449Speter				return (TC_SYS_ERR);
40562449Speter			    }
40662449Speter			    r_end = record + newsize;
40762449Speter			    rp = record + pos;
40862449Speter			}
40962449Speter		    }
41062449Speter		    /* loop invariant lets us do this */
41162449Speter		    *rp++ = '\0';
41250276Speter
41362449Speter		    /*
41462449Speter		     * If encountered eof check next file.
41562449Speter		     */
41662449Speter		    if (eof)
41762449Speter			break;
41850276Speter
41962449Speter		    /*
42062449Speter		     * Toss blank lines and comments.
42162449Speter		     */
42262449Speter		    if (*record == '\0' || *record == '#')
42362449Speter			continue;
42450276Speter
42562449Speter		    /*
42662449Speter		     * See if this is the record we want ...
42762449Speter		     */
42862449Speter		    if (_nc_cgetmatch(record, name) == 0
42962449Speter			&& (nfield == 0
43062449Speter			    || !_nc_nfcmp(nfield, record))) {
43162449Speter			foundit = TRUE;
43262449Speter			*beginning = first;
43362449Speter			break;	/* found it! */
43462449Speter		    }
43562449Speter		}
43662449Speter	    }
43762449Speter	    if (foundit)
43862449Speter		break;
43962449Speter	}
44050276Speter
44162449Speter	if (!foundit)
44262449Speter	    return (TC_NOT_FOUND);
44362449Speter    }
44450276Speter
44562449Speter    /*
44662449Speter     * Got the capability record, but now we have to expand all tc=name
44762449Speter     * references in it ...
44862449Speter     */
44962449Speter    {
45062449Speter	register char *newicap, *s;
45162449Speter	register int newilen;
45262449Speter	unsigned ilen;
45362449Speter	int diff, iret, tclen, oline;
45462449Speter	char *icap, *scan, *tc, *tcstart, *tcend;
45550276Speter
45650276Speter	/*
45762449Speter	 * Loop invariants:
45862449Speter	 *      There is room for one more character in record.
45962449Speter	 *      R_end points just past end of record.
46062449Speter	 *      Rp points just past last character in record.
46162449Speter	 *      Scan points at remainder of record that needs to be
46262449Speter	 *      scanned for tc=name constructs.
46350276Speter	 */
46462449Speter	scan = record;
46562449Speter	tc_not_resolved = FALSE;
46662449Speter	for (;;) {
46762449Speter	    if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0)
46862449Speter		break;
46950276Speter
47062449Speter	    /*
47162449Speter	     * Find end of tc=name and stomp on the trailing `:'
47262449Speter	     * (if present) so we can use it to call ourselves.
47362449Speter	     */
47462449Speter	    s = tc;
47562449Speter	    while (*s != '\0') {
47662449Speter		if (*s++ == ':') {
47762449Speter		    *(s - 1) = '\0';
47862449Speter		    break;
47962449Speter		}
48062449Speter	    }
48162449Speter	    tcstart = tc - 3;
48262449Speter	    tclen = s - tcstart;
48362449Speter	    tcend = s;
48450276Speter
48562449Speter	    iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd,
48666963Speter			      tc, depth + 1, 0);
48762449Speter	    newicap = icap;	/* Put into a register. */
48862449Speter	    newilen = ilen;
48962449Speter	    if (iret != TC_SUCCESS) {
49062449Speter		/* an error */
49162449Speter		if (iret < TC_NOT_FOUND) {
49262449Speter		    if (myfd)
49362449Speter			(void) close(fd);
49462449Speter		    free(record);
49562449Speter		    return (iret);
49662449Speter		}
49762449Speter		if (iret == TC_UNRESOLVED)
49862449Speter		    tc_not_resolved = TRUE;
49962449Speter		/* couldn't resolve tc */
50062449Speter		if (iret == TC_NOT_FOUND) {
50162449Speter		    *(s - 1) = ':';
50262449Speter		    scan = s - 1;
50362449Speter		    tc_not_resolved = TRUE;
50462449Speter		    continue;
50562449Speter		}
50662449Speter	    }
50750276Speter
50862449Speter	    /* not interested in name field of tc'ed record */
50962449Speter	    s = newicap;
51062449Speter	    while (*s != '\0' && *s++ != ':') ;
51162449Speter	    newilen -= s - newicap;
51262449Speter	    newicap = s;
51350276Speter
51462449Speter	    /* make sure interpolated record is `:'-terminated */
51562449Speter	    s += newilen;
51662449Speter	    if (*(s - 1) != ':') {
51762449Speter		*s = ':';	/* overwrite NUL with : */
51862449Speter		newilen++;
51962449Speter	    }
52050276Speter
52162449Speter	    /*
52262449Speter	     * Make sure there's enough room to insert the
52362449Speter	     * new record.
52462449Speter	     */
52562449Speter	    diff = newilen - tclen;
52662449Speter	    if (diff >= r_end - rp) {
52762449Speter		unsigned pos, tcpos, tcposend;
52862449Speter		size_t newsize;
52950276Speter
53062449Speter		pos = rp - record;
53162449Speter		newsize = r_end - record + diff + BFRAG;
53262449Speter		tcpos = tcstart - record;
53362449Speter		tcposend = tcend - record;
53462449Speter		record = DOALLOC(newsize);
53562449Speter		if (record == 0) {
53662449Speter		    if (myfd)
53762449Speter			(void) close(fd);
53862449Speter		    free(icap);
53962449Speter		    errno = ENOMEM;
54062449Speter		    return (TC_SYS_ERR);
54162449Speter		}
54262449Speter		r_end = record + newsize;
54362449Speter		rp = record + pos;
54462449Speter		tcstart = record + tcpos;
54562449Speter		tcend = record + tcposend;
54662449Speter	    }
54750276Speter
54862449Speter	    /*
54962449Speter	     * Insert tc'ed record into our record.
55062449Speter	     */
55162449Speter	    s = tcstart + newilen;
55262449Speter	    memmove(s, tcend, (size_t) (rp - tcend));
55362449Speter	    memmove(tcstart, newicap, (size_t) newilen);
55462449Speter	    rp += diff;
55562449Speter	    free(icap);
55650276Speter
55762449Speter	    /*
55862449Speter	     * Start scan on `:' so next cgetcap works properly
55962449Speter	     * (cgetcap always skips first field).
56062449Speter	     */
56162449Speter	    scan = s - 1;
56250276Speter	}
56362449Speter    }
56450276Speter
56562449Speter    /*
56662449Speter     * Close file (if we opened it), give back any extra memory, and
56762449Speter     * return capability, length and success.
56862449Speter     */
56962449Speter    if (myfd)
57062449Speter	(void) close(fd);
57162449Speter    *len = rp - record - 1;	/* don't count NUL */
57262449Speter    if (r_end > rp) {
57362449Speter	if ((record = DOALLOC((size_t) (rp - record))) == 0) {
57462449Speter	    errno = ENOMEM;
57562449Speter	    return (TC_SYS_ERR);
57650276Speter	}
57762449Speter    }
57850276Speter
57962449Speter    *cap = record;
58062449Speter    if (tc_not_resolved)
58162449Speter	return (TC_UNRESOLVED);
58262449Speter    return (current);
58350276Speter}
58450276Speter
58550276Speter/*
58650276Speter * Cgetmatch will return 0 if name is one of the names of the capability
58750276Speter * record buf, -1 if not.
58850276Speter */
58950276Speterstatic int
59050276Speter_nc_cgetmatch(char *buf, const char *name)
59150276Speter{
59262449Speter    register const char *np;
59362449Speter    register char *bp;
59450276Speter
59562449Speter    /*
59662449Speter     * Start search at beginning of record.
59762449Speter     */
59862449Speter    bp = buf;
59962449Speter    for (;;) {
60050276Speter	/*
60162449Speter	 * Try to match a record name.
60250276Speter	 */
60362449Speter	np = name;
60450276Speter	for (;;) {
60562449Speter	    if (*np == '\0') {
60662449Speter		if (*bp == '|' || *bp == ':' || *bp == '\0')
60762449Speter		    return (0);
60862449Speter		else
60962449Speter		    break;
61062449Speter	    } else if (*bp++ != *np++) {
61162449Speter		break;
61262449Speter	    }
61362449Speter	}
61450276Speter
61562449Speter	/*
61662449Speter	 * Match failed, skip to next name in record.
61762449Speter	 */
61862449Speter	bp--;			/* a '|' or ':' may have stopped the match */
61962449Speter	for (;;) {
62062449Speter	    if (*bp == '\0' || *bp == ':')
62162449Speter		return (-1);	/* match failed totally */
62262449Speter	    else if (*bp++ == '|')
62362449Speter		break;		/* found next name */
62450276Speter	}
62562449Speter    }
62650276Speter}
62750276Speter
62850276Speter/*
62950276Speter * Compare name field of record.
63050276Speter */
63150276Speterstatic int
63250276Speter_nc_nfcmp(const char *nf, char *rec)
63350276Speter{
63462449Speter    char *cp, tmp;
63562449Speter    int ret;
63650276Speter
63762449Speter    for (cp = rec; *cp != ':'; cp++) ;
63850276Speter
63962449Speter    tmp = *(cp + 1);
64062449Speter    *(cp + 1) = '\0';
64162449Speter    ret = strcmp(nf, rec);
64262449Speter    *(cp + 1) = tmp;
64350276Speter
64462449Speter    return (ret);
64550276Speter}
64650276Speter#endif /* HAVE_BSD_CGETENT */
64750276Speter
64850276Speter/*
64950276Speter * Since ncurses provides its own 'tgetent()', we cannot use the native one.
65050276Speter * So we reproduce the logic to get down to cgetent() -- or our cut-down
65150276Speter * version of that -- to circumvent the problem of configuring against the
65250276Speter * termcap library.
65350276Speter */
65450276Speter#define USE_BSD_TGETENT 1
65550276Speter
65650276Speter#if USE_BSD_TGETENT
65750276Speter/*
65850276Speter * Copyright (c) 1980, 1993
65950276Speter *	The Regents of the University of California.  All rights reserved.
66050276Speter *
66150276Speter * Redistribution and use in source and binary forms, with or without
66250276Speter * modification, are permitted provided that the following conditions
66350276Speter * are met:
66450276Speter * 1. Redistributions of source code must retain the above copyright
66550276Speter *    notice, this list of conditions and the following disclaimer.
66650276Speter * 2. Redistributions in binary form must reproduce the above copyright
66750276Speter *    notice, this list of conditions and the following disclaimer in the
66850276Speter *    documentation and/or other materials provided with the distribution.
66950276Speter * 3. All advertising materials mentioning features or use of this software
67050276Speter *    must display the following acknowledgment:
67150276Speter *	This product includes software developed by the University of
67250276Speter *	California, Berkeley and its contributors.
67350276Speter * 4. Neither the name of the University nor the names of its contributors
67450276Speter *    may be used to endorse or promote products derived from this software
67550276Speter *    without specific prior written permission.
67650276Speter *
67750276Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
67850276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67950276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68050276Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
68150276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
68250276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68350276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68450276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
68550276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68650276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68750276Speter * SUCH DAMAGE.
68850276Speter */
68950276Speter
69050276Speter/* static char sccsid[] = "@(#)termcap.c	8.1 (Berkeley) 6/4/93" */
69150276Speter
69250276Speter#define	PBUFSIZ		512	/* max length of filename path */
69350276Speter#define	PVECSIZ		32	/* max number of names in path */
69450276Speter#define TBUFSIZ (2048*2)
69550276Speter
69650276Speterstatic char *tbuf;
69750276Speter
69850276Speter/*
69950276Speter * On entry, srcp points to a non ':' character which is the beginning of the
70050276Speter * token, if any.  We'll try to return a string that doesn't end with a ':'.
70150276Speter */
70250276Speterstatic char *
70350276Speterget_tc_token(char **srcp, int *endp)
70450276Speter{
70562449Speter    int ch;
70662449Speter    bool found = FALSE;
70762449Speter    char *s, *base;
70862449Speter    char *tok = 0;
70950276Speter
71062449Speter    *endp = TRUE;
71162449Speter    for (s = base = *srcp; *s != '\0';) {
71262449Speter	ch = *s++;
71362449Speter	if (ch == '\\') {
71462449Speter	    if (*s == '\0') {
71562449Speter		break;
71662449Speter	    } else if (*s++ == '\n') {
717166124Srafan		while (isspace(UChar(*s)))
71862449Speter		    s++;
71962449Speter	    } else {
72062449Speter		found = TRUE;
72162449Speter	    }
72262449Speter	} else if (ch == ':') {
72362449Speter	    if (found) {
72450276Speter		tok = base;
72562449Speter		s[-1] = '\0';
72662449Speter		*srcp = s;
72762449Speter		*endp = FALSE;
72862449Speter		break;
72962449Speter	    }
73062449Speter	    base = s;
731166124Srafan	} else if (isgraph(UChar(ch))) {
73262449Speter	    found = TRUE;
73350276Speter	}
73462449Speter    }
73550276Speter
73662449Speter    /* malformed entry may end without a ':' */
73762449Speter    if (tok == 0 && found) {
73862449Speter	tok = base;
73962449Speter    }
74062449Speter
74162449Speter    return tok;
74250276Speter}
74350276Speter
74450276Speterstatic char *
74550276Spetercopy_tc_token(char *dst, const char *src, size_t len)
74650276Speter{
74762449Speter    int ch;
74850276Speter
74962449Speter    while ((ch = *src++) != '\0') {
75062449Speter	if (ch == '\\' && *src == '\n') {
751166124Srafan	    while (isspace(UChar(*src)))
75262449Speter		src++;
75362449Speter	    continue;
75450276Speter	}
75562449Speter	if (--len == 0) {
75662449Speter	    dst = 0;
75762449Speter	    break;
75862449Speter	}
75962449Speter	*dst++ = ch;
76062449Speter    }
76162449Speter    return dst;
76250276Speter}
76350276Speter
76450276Speter/*
76550276Speter * Get an entry for terminal name in buffer bp from the termcap file.
76650276Speter */
76750276Speterstatic int
76850276Speter_nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
76950276Speter{
77062449Speter    static char *the_source;
77150276Speter
77262449Speter    register char *p;
77362449Speter    register char *cp;
77476726Speter    char *dummy = NULL;
77562449Speter    char **fname;
77662449Speter    char *home;
77762449Speter    int i;
77862449Speter    char pathbuf[PBUFSIZ];	/* holds raw path of filenames */
77962449Speter    char *pathvec[PVECSIZ];	/* to point to names in pathbuf */
78062449Speter    char **pvec;		/* holds usable tail of path vector */
781166124Srafan    NCURSES_CONST char *termpath;
78266963Speter    string_desc desc;
78350276Speter
78462449Speter    fname = pathvec;
78562449Speter    pvec = pathvec;
78662449Speter    tbuf = bp;
78762449Speter    p = pathbuf;
78876726Speter    cp = use_terminfo_vars()? getenv("TERMCAP") : NULL;
78950276Speter
79062449Speter    /*
79162449Speter     * TERMCAP can have one of two things in it.  It can be the name of a file
79262449Speter     * to use instead of /etc/termcap.  In this case it better start with a
79362449Speter     * "/".  Or it can be an entry to use so we don't have to read the file.
79462449Speter     * In this case it has to already have the newlines crunched out.  If
79562449Speter     * TERMCAP does not hold a file name then a path of names is searched
79662449Speter     * instead.  The path is found in the TERMPATH variable, or becomes
79762449Speter     * "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
79862449Speter     */
79966963Speter    _nc_str_init(&desc, pathbuf, sizeof(pathbuf));
80066963Speter    if (cp == NULL) {
80197049Speter	_nc_safe_strcpy(&desc, get_termpath());
802166124Srafan    } else if (!_nc_is_abs_path(cp)) {	/* TERMCAP holds an entry */
80397049Speter	if ((termpath = get_termpath()) != 0) {
80466963Speter	    _nc_safe_strcat(&desc, termpath);
80562449Speter	} else {
80666963Speter	    char temp[PBUFSIZ];
80766963Speter	    temp[0] = 0;
80866963Speter	    if ((home = getenv("HOME")) != 0 && *home != '\0'
80966963Speter		&& strchr(home, ' ') == 0
81066963Speter		&& strlen(home) < sizeof(temp) - 10) {	/* setup path */
81166963Speter		sprintf(temp, "%s/", home);	/* $HOME first */
81266963Speter	    }
81366963Speter	    /* if no $HOME look in current directory */
81466963Speter	    strcat(temp, ".termcap");
81566963Speter	    _nc_safe_strcat(&desc, temp);
81697049Speter	    _nc_safe_strcat(&desc, " ");
81797049Speter	    _nc_safe_strcat(&desc, get_termpath());
81850276Speter	}
81966963Speter    } else {			/* user-defined name in TERMCAP */
82066963Speter	_nc_safe_strcat(&desc, cp);	/* still can be tokenized */
82166963Speter    }
82250276Speter
82362449Speter    *fname++ = pathbuf;		/* tokenize path into vector of names */
82462449Speter    while (*++p) {
82576726Speter	if (*p == ' ' || *p == NCURSES_PATHSEP) {
82662449Speter	    *p = '\0';
82762449Speter	    while (*++p)
82876726Speter		if (*p != ' ' && *p != NCURSES_PATHSEP)
82962449Speter		    break;
83062449Speter	    if (*p == '\0')
83162449Speter		break;
83262449Speter	    *fname++ = p;
83362449Speter	    if (fname >= pathvec + PVECSIZ) {
83462449Speter		fname--;
83562449Speter		break;
83662449Speter	    }
83750276Speter	}
83862449Speter    }
83962449Speter    *fname = 0;			/* mark end of vector */
840166124Srafan    if (_nc_is_abs_path(cp)) {
84162449Speter	if (_nc_cgetset(cp) < 0) {
84262449Speter	    return (TC_SYS_ERR);
84350276Speter	}
84462449Speter    }
84550276Speter
84662449Speter    i = _nc_cgetent(&dummy, lineno, pathvec, name);
84750276Speter
84862449Speter    /* ncurses' termcap-parsing routines cannot handle multiple adjacent
84962449Speter     * empty fields, and mistakenly use the last valid cap entry instead of
85062449Speter     * the first (breaks tc= includes)
85162449Speter     */
85262449Speter    if (i >= 0) {
85362449Speter	char *pd, *ps, *tok;
85462449Speter	int endflag = FALSE;
85562449Speter	char *list[1023];
85662449Speter	size_t n, count = 0;
85750276Speter
85862449Speter	pd = bp;
85962449Speter	ps = dummy;
86062449Speter	while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) {
86162449Speter	    bool ignore = FALSE;
86250276Speter
86362449Speter	    for (n = 1; n < count; n++) {
86462449Speter		char *s = list[n];
86562449Speter		if (s[0] == tok[0]
86662449Speter		    && s[1] == tok[1]) {
86762449Speter		    ignore = TRUE;
86862449Speter		    break;
86950276Speter		}
87062449Speter	    }
87162449Speter	    if (ignore != TRUE) {
87262449Speter		list[count++] = tok;
87362449Speter		pd = copy_tc_token(pd, tok, TBUFSIZ - (2 + pd - bp));
87462449Speter		if (pd == 0) {
87562449Speter		    i = -1;
87662449Speter		    break;
87762449Speter		}
87862449Speter		*pd++ = ':';
87962449Speter		*pd = '\0';
88062449Speter	    }
88150276Speter	}
88262449Speter    }
88350276Speter
88462449Speter    FreeIfNeeded(dummy);
88562449Speter    FreeIfNeeded(the_source);
88662449Speter    the_source = 0;
88750276Speter
88862449Speter    /* This is not related to the BSD cgetent(), but to fake up a suitable
88962449Speter     * filename for ncurses' error reporting.  (If we are not using BSD
89062449Speter     * cgetent, then it is the actual filename).
89162449Speter     */
89262449Speter    if (i >= 0) {
893166124Srafan#if HAVE_BSD_CGETENT
894166124Srafan	char temp[PATH_MAX];
895166124Srafan
896166124Srafan	_nc_str_init(&desc, temp, sizeof(temp));
897166124Srafan	_nc_safe_strcpy(&desc, pathvec[i]);
898166124Srafan	_nc_safe_strcat(&desc, ".db");
899166124Srafan	if (_nc_access(temp, R_OK) == 0) {
900166124Srafan	    _nc_safe_strcpy(&desc, pathvec[i]);
901166124Srafan	}
902166124Srafan	if ((the_source = strdup(temp)) != 0)
903166124Srafan	    *sourcename = the_source;
904166124Srafan#else
90562449Speter	if ((the_source = strdup(pathvec[i])) != 0)
90662449Speter	    *sourcename = the_source;
907166124Srafan#endif
90862449Speter    }
90950276Speter
91062449Speter    return (i);
91150276Speter}
91250276Speter#endif /* USE_BSD_TGETENT */
91350276Speter#endif /* USE_GETCAP */
91450276Speter
91550276Speter#define MAXPATHS	32
91650276Speter
91750276Speter/*
91850276Speter * Add a filename to the list in 'termpaths[]', checking that we really have
91950276Speter * a right to open the file.
92050276Speter */
92150276Speter#if !USE_GETCAP
92262449Speterstatic int
92362449Speteradd_tc(char *termpaths[], char *path, int count)
92450276Speter{
92597049Speter    char *save = strchr(path, NCURSES_PATHSEP);
92697049Speter    if (save != 0)
92797049Speter	*save = '\0';
92862449Speter    if (count < MAXPATHS
92997049Speter	&& _nc_access(path, R_OK) == 0) {
93062449Speter	termpaths[count++] = path;
93197049Speter	T(("Adding termpath %s", path));
93297049Speter    }
93362449Speter    termpaths[count] = 0;
93497049Speter    if (save != 0)
93597049Speter	*save = NCURSES_PATHSEP;
93662449Speter    return count;
93750276Speter}
93850276Speter#define ADD_TC(path, count) filecount = add_tc(termpaths, path, count)
93950276Speter#endif /* !USE_GETCAP */
94050276Speter
94176726SpeterNCURSES_EXPORT(int)
942166124Srafan_nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
94350276Speter{
944166124Srafan    int found = TGETENT_NO;
94562449Speter    ENTRY *ep;
94650276Speter#if USE_GETCAP_CACHE
94762449Speter    char cwd_buf[PATH_MAX];
94850276Speter#endif
94950276Speter#if USE_GETCAP
95062449Speter    char *p, tc[TBUFSIZ];
951166124Srafan    int status;
95262449Speter    static char *source;
95362449Speter    static int lineno;
95450276Speter
95597049Speter    T(("read termcap entry for %s", tn));
956166124Srafan
957166124Srafan    if (strlen(tn) == 0
958166124Srafan	|| strcmp(tn, ".") == 0
959166124Srafan	|| strcmp(tn, "..") == 0
960166124Srafan	|| _nc_pathlast(tn) != 0) {
961166124Srafan	T(("illegal or missing entry name '%s'", tn));
962166124Srafan	return TGETENT_NO;
963166124Srafan    }
964166124Srafan
96566963Speter    if (use_terminfo_vars() && (p = getenv("TERMCAP")) != 0
966166124Srafan	&& !_nc_is_abs_path(p) && _nc_name_match(p, tn, "|:")) {
96762449Speter	/* TERMCAP holds a termcap entry */
96862449Speter	strncpy(tc, p, sizeof(tc) - 1);
96962449Speter	tc[sizeof(tc) - 1] = '\0';
97062449Speter	_nc_set_source("TERMCAP");
97162449Speter    } else {
97250276Speter	/* we're using getcap(3) */
973166124Srafan	if ((status = _nc_tgetent(tc, &source, &lineno, tn)) < 0)
974166124Srafan	    return (status == TC_NOT_FOUND ? TGETENT_NO : TGETENT_ERR);
97550276Speter
97650276Speter	_nc_curr_line = lineno;
97750276Speter	_nc_set_source(source);
97862449Speter    }
97962449Speter    _nc_read_entry_source((FILE *) 0, tc, FALSE, FALSE, NULLHOOK);
98050276Speter#else
98162449Speter    /*
98262449Speter     * Here is what the 4.4BSD termcap(3) page prescribes:
98362449Speter     *
98462449Speter     * It will look in the environment for a TERMCAP variable.  If found, and
98562449Speter     * the value does not begin with a slash, and the terminal type name is the
98662449Speter     * same as the environment string TERM, the TERMCAP string is used instead
98762449Speter     * of reading a termcap file.  If it does begin with a slash, the string is
98862449Speter     * used as a path name of the termcap file to search.  If TERMCAP does not
98962449Speter     * begin with a slash and name is different from TERM, tgetent() searches
99062449Speter     * the files $HOME/.termcap and /usr/share/misc/termcap, in that order,
99162449Speter     * unless the environment variable TERMPATH exists, in which case it
99262449Speter     * specifies a list of file pathnames (separated by spaces or colons) to be
99362449Speter     * searched instead.
99462449Speter     *
99562449Speter     * It goes on to state:
99662449Speter     *
99762449Speter     * Whenever multiple files are searched and a tc field occurs in the
99862449Speter     * requested entry, the entry it names must be found in the same file or
99962449Speter     * one of the succeeding files.
100062449Speter     *
100162449Speter     * However, this restriction is relaxed in ncurses; tc references to
100262449Speter     * previous files are permitted.
100362449Speter     *
100462449Speter     * This routine returns 1 if an entry is found, 0 if not found, and -1 if
100562449Speter     * the database is not accessible.
100662449Speter     */
100762449Speter    FILE *fp;
100862449Speter    char *tc, *termpaths[MAXPATHS];
100962449Speter    int filecount = 0;
101097049Speter    int j, k;
101162449Speter    bool use_buffer = FALSE;
101297049Speter    bool normal = TRUE;
101362449Speter    char tc_buf[1024];
101462449Speter    char pathbuf[PATH_MAX];
101597049Speter    char *copied = 0;
101697049Speter    char *cp;
101797049Speter    struct stat test_stat[MAXPATHS];
101850276Speter
101962449Speter    termpaths[filecount] = 0;
102066963Speter    if (use_terminfo_vars() && (tc = getenv("TERMCAP")) != 0) {
1021166124Srafan	if (_nc_is_abs_path(tc)) {	/* interpret as a filename */
102262449Speter	    ADD_TC(tc, 0);
102397049Speter	    normal = FALSE;
102462449Speter	} else if (_nc_name_match(tc, tn, "|:")) {	/* treat as a capability file */
102562449Speter	    use_buffer = TRUE;
102662449Speter	    (void) sprintf(tc_buf, "%.*s\n", (int) sizeof(tc_buf) - 2, tc);
102797049Speter	    normal = FALSE;
102897049Speter	}
102997049Speter    }
103050276Speter
103197049Speter    if (normal) {		/* normal case */
103297049Speter	char envhome[PATH_MAX], *h;
103397049Speter
103497049Speter	copied = strdup(get_termpath());
103597049Speter	for (cp = copied; *cp; cp++) {
103697049Speter	    if (*cp == NCURSES_PATHSEP)
103797049Speter		*cp = '\0';
103897049Speter	    else if (cp == copied || cp[-1] == '\0') {
103997049Speter		ADD_TC(cp, filecount);
104062449Speter	    }
104150276Speter	}
104250276Speter
104350276Speter#define PRIVATE_CAP "%s/.termcap"
104450276Speter
104576726Speter	if (use_terminfo_vars() && (h = getenv("HOME")) != NULL && *h != '\0'
104662449Speter	    && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX) {
104762449Speter	    /* user's .termcap, if any, should override it */
104862449Speter	    (void) strcpy(envhome, h);
104962449Speter	    (void) sprintf(pathbuf, PRIVATE_CAP, envhome);
105062449Speter	    ADD_TC(pathbuf, filecount);
105150276Speter	}
105262449Speter    }
105350276Speter
105497049Speter    /*
105597049Speter     * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
105697049Speter     * Avoid reading the same file twice.
105797049Speter     */
1058166124Srafan#if HAVE_LINK
105997049Speter    for (j = 0; j < filecount; j++) {
106097049Speter	bool omit = FALSE;
106197049Speter	if (stat(termpaths[j], &test_stat[j]) != 0
106297049Speter	    || (test_stat[j].st_mode & S_IFMT) != S_IFREG) {
106397049Speter	    omit = TRUE;
106497049Speter	} else {
106597049Speter	    for (k = 0; k < j; k++) {
106697049Speter		if (test_stat[k].st_dev == test_stat[j].st_dev
106797049Speter		    && test_stat[k].st_ino == test_stat[j].st_ino) {
106897049Speter		    omit = TRUE;
106997049Speter		    break;
107097049Speter		}
107197049Speter	    }
107297049Speter	}
107397049Speter	if (omit) {
107497049Speter	    T(("Path %s is a duplicate", termpaths[j]));
107597049Speter	    for (k = j + 1; k < filecount; k++) {
107697049Speter		termpaths[k - 1] = termpaths[k];
107797049Speter		test_stat[k - 1] = test_stat[k];
107897049Speter	    }
107997049Speter	    --filecount;
108097049Speter	    --j;
108197049Speter	}
108297049Speter    }
108397049Speter#endif
108497049Speter
108562449Speter    /* parse the sources */
108662449Speter    if (use_buffer) {
108762449Speter	_nc_set_source("TERMCAP");
108850276Speter
108962449Speter	/*
109062449Speter	 * We don't suppress warning messages here.  The presumption is
109162449Speter	 * that since it's just a single entry, they won't be a pain.
109262449Speter	 */
109362449Speter	_nc_read_entry_source((FILE *) 0, tc_buf, FALSE, FALSE, NULLHOOK);
109462449Speter    } else {
109562449Speter	int i;
109650276Speter
109762449Speter	for (i = 0; i < filecount; i++) {
109850276Speter
109962449Speter	    T(("Looking for %s in %s", tn, termpaths[i]));
110097049Speter	    if (_nc_access(termpaths[i], R_OK) == 0
110197049Speter		&& (fp = fopen(termpaths[i], "r")) != (FILE *) 0) {
110262449Speter		_nc_set_source(termpaths[i]);
110350276Speter
110462449Speter		/*
110562449Speter		 * Suppress warning messages.  Otherwise you get 400 lines of
110662449Speter		 * crap from archaic termcap files as ncurses complains about
110762449Speter		 * all the obsolete capabilities.
110862449Speter		 */
110962449Speter		_nc_read_entry_source(fp, (char *) 0, FALSE, TRUE, NULLHOOK);
111050276Speter
111162449Speter		(void) fclose(fp);
111262449Speter	    }
111350276Speter	}
111462449Speter    }
111597049Speter    if (copied != 0)
111697049Speter	free(copied);
111750276Speter#endif /* USE_GETCAP */
111850276Speter
111962449Speter    if (_nc_head == 0)
1120166124Srafan	return (TGETENT_ERR);
112150276Speter
112262449Speter    /* resolve all use references */
1123166124Srafan    _nc_resolve_uses2(TRUE, FALSE);
112450276Speter
112562449Speter    /* find a terminal matching tn, if we can */
112650276Speter#if USE_GETCAP_CACHE
112762449Speter    if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0) {
112862449Speter	_nc_set_writedir((char *) 0);	/* note: this does a chdir */
112950276Speter#endif
113062449Speter	for_entry_list(ep) {
113162449Speter	    if (_nc_name_match(ep->tterm.term_names, tn, "|:")) {
113262449Speter		/*
1133166124Srafan		 * Make a local copy of the terminal capabilities, delinked
1134166124Srafan		 * from the list.
113562449Speter		 */
113662449Speter		*tp = ep->tterm;
1137166124Srafan		_nc_delink_entry(_nc_head, &(ep->tterm));
1138166124Srafan		free(ep);
113950276Speter
114062449Speter		/*
114162449Speter		 * OK, now try to write the type to user's terminfo directory.
114262449Speter		 * Next time he loads this, it will come through terminfo.
114362449Speter		 *
114462449Speter		 * Advantage:  Second and subsequent fetches of this entry will
114562449Speter		 * be very fast.
114662449Speter		 *
114762449Speter		 * Disadvantage:  After the first time a termcap type is loaded
114862449Speter		 * by its user, editing it in the /etc/termcap file, or in
114962449Speter		 * TERMCAP, or in a local ~/.termcap, will be ineffective
115062449Speter		 * unless the terminfo entry is explicitly removed.
115162449Speter		 */
115250276Speter#if USE_GETCAP_CACHE
115362449Speter		(void) _nc_write_entry(tp);
115450276Speter#endif
1155166124Srafan		found = TGETENT_YES;
115662449Speter		break;
115762449Speter	    }
115862449Speter	}
115950276Speter#if USE_GETCAP_CACHE
116062449Speter	chdir(cwd_buf);
116162449Speter    }
116250276Speter#endif
116350276Speter
116462449Speter    return (found);
116550276Speter}
116650276Speter#else
116776726Speterextern
116876726SpeterNCURSES_EXPORT(void)
116976726Speter_nc_read_termcap(void);
117076726SpeterNCURSES_EXPORT(void)
117162449Speter_nc_read_termcap(void)
117262449Speter{
117362449Speter}
117462449Speter#endif /* PURE_TERMINFO */
1175