/* $OpenBSD: read_termcap.c,v 1.7 1998/11/19 17:22:23 millert Exp $ */ /**************************************************************************** * Copyright (c) 1998 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 * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, distribute with modifications, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is * * furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included * * in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * * Except as contained in this notice, the name(s) of the above copyright * * holders shall not be used in advertising or otherwise to promote the * * sale, use or other dealings in this Software without prior written * * authorization. * ****************************************************************************/ /**************************************************************************** * Author: Zeyd M. Ben-Halim 1992,1995 * * and: Eric S. Raymond * ****************************************************************************/ /* * Termcap compatibility support * * If your OS integrator didn't install a terminfo database, you can call * _nc_read_termcap_entry() to support reading and translating capabilities * from the system termcap file. This is a kludge; it will bulk up and slow * down every program that uses ncurses, and translated termcap entries cannot * use full terminfo capabilities. Don't use it unless you absolutely have to; * instead, get your system people to run tic(1) from root on the terminfo * master included with ncurses to translate it into a terminfo database. * * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD * getcap code to fetch entries. There are disadvantages to this; mainly that * getcap(3) does its own resolution, meaning that entries read in in this way * can't reference the terminfo tree. The only thing it buys is faster startup * time, getcap(3) is much faster than our tic parser. */ #include #include #include #include #include #if HAVE_FCNTL_H #include #endif MODULE_ID("$From: read_termcap.c,v 1.37 1998/09/19 21:42:14 tom Exp $") #ifndef PURE_TERMINFO #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 #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_GETCAP #if HAVE_BSD_CGETENT #define _nc_cgetcap cgetcap #define _nc_cgetent(buf, oline, db_array, name) cgetent(buf, db_array, name) #define _nc_cgetmatch cgetmatch #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_nfcmp(const char *, char *); /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Casey Leedom of Lawrence Livermore National Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 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 * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; */ #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 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 * database array, in effect "pushing" the buffer on top of the virtual * database. 0 is returned on success, -1 on failure. */ static int _nc_cgetset(const char *ent) { if (ent == 0) { FreeIfNeeded(toprec); toprec = 0; topreclen = 0; return (0); } topreclen = strlen(ent); if ((toprec = malloc (topreclen + 1)) == 0) { errno = ENOMEM; return (-1); } gottoprec = 0; (void)strcpy(toprec, ent); return (0); } /* * Cgetcap searches the capability record buf for the capability cap with type * `type'. A pointer to the value of cap is returned on success, 0 if the * requested capability couldn't be found. * * Specifying a type of ':' means that nothing should follow cap (:cap:). In * this case a pointer to the terminating ':' or NUL will be returned if cap is * found. * * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) * return 0. */ static char * _nc_cgetcap(char *buf, const char *cap, int type) { register const char *cp; register char *bp; 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 (;;) { 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); } /* NOTREACHED */ } /* * Cgetent extracts the capability record name from the NULL terminated file * array db_array and returns a pointer to a malloc'd copy of it in buf. Buf * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag, * and cgetstr, but may then be freed. * * 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 */ static int _nc_cgetent(char **buf, int *oline, char **db_array, const char *name) { unsigned int dummy; return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0)); } /* * Getent implements the functions of cgetent. If fd is non-negative, * *db_array has already been opened and fd is the open file descriptor. We * do this to save time and avoid using up file descriptors for tc= * recursions. * * Getent returns the same success/failure codes as cgetent. On success, a * pointer to a malloc'd capability record with all tc= capabilities fully * expanded and its length (not including trailing ASCII NUL) are left in * *cap and *len. * * Basic algorithm: * + Allocate memory incrementally as needed in chunks of size BFRAG * for capability buffer. * + Recurse for each tc=name and interpolate result. Stop when all * names interpolated, a name can't be found, or depth exceeds * MAX_RECURSION. */ #define DOALLOC(size) (char *)_nc_doalloc(record, size) 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) { 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); } (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); } /* * Got the capability record, but now we have to expand all tc=name * references in it ... */ { register char *newicap, *s; register int newilen; unsigned int ilen; int diff, iret, tclen, oline; char *icap, *scan, *tc, *tcstart, *tcend; /* * 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. */ 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; } } /* 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++; } /* * Make sure there's enough room to insert the * new record. */ 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; } /* * 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 = 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); } /* * Cgetmatch will return 0 if name is one of the names of the capability * record buf, -1 if not. */ static int _nc_cgetmatch(char *buf, const char *name) { register const char *np; register char *bp; /* * Start search at beginning of record. */ bp = buf; 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; } } /* * 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 */ } } } /* * Compare name field of record. */ static int _nc_nfcmp(const char *nf, char *rec) { char *cp, tmp; int ret; for (cp = rec; *cp != ':'; cp++) ; tmp = *(cp + 1); *(cp + 1) = '\0'; ret = strcmp(nf, rec); *(cp + 1) = tmp; return (ret); } #endif /* HAVE_BSD_CGETENT */ /* * Since ncurses provides its own 'tgetent()', we cannot use the native one. * So we reproduce the logic to get down to cgetent() -- or our cut-down * version of that -- to circumvent the problem of configuring against the * termcap library. */ #define USE_BSD_TGETENT 1 #if USE_BSD_TGETENT /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 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 * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* static char sccsid[] = "@(#)termcap.c 8.1 (Berkeley) 6/4/93" */ #define PBUFSIZ 512 /* max length of filename path */ #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 ':'. */ 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) { tok = base; } return tok; } static char * copy_tc_token(char *dst, const char *src, size_t len) { int ch; while ((ch = *src++) != '\0') { if (ch == '\\' && *src == '\n') { while (isspace(*src)) src++; continue; } if (--len == 0) { dst = 0; break; } *dst++ = ch; } return dst; } /* * Get an entry for terminal name in buffer bp from the termcap file. */ 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. */ #define MY_PATH_DEF "/etc/termcap /usr/share/misc/termcap" if (issetugid()) strlcpy(pathbuf, MY_PATH_DEF, PBUFSIZ); else if (!is_pathname(cp)) { /* no TERMCAP or it holds an entry */ if ((termpath = getenv("TERMPATH")) != 0) { strlcpy(pathbuf, termpath, PBUFSIZ); } 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 */ strlcpy(p, ".termcap " MY_PATH_DEF, (size_t)(PBUFSIZ - (p - pathbuf))); } } else /* user-defined name in TERMCAP */ strlcpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */ *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; } } } *fname = 0; /* mark end of vector */ if (is_pathname(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'; } } } 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) { the_source = malloc(strlen(pathvec[i]) + 1); if (the_source != 0) *sourcename = strcpy(the_source, pathvec[i]); } return(i); } #endif /* USE_BSD_TGETENT */ #endif /* USE_GETCAP */ #define MAXPATHS 32 /* * Add a filename to the list in 'termpaths[]', checking that we really have * a right to open the file. */ #if !USE_GETCAP 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; } #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) { int found = FALSE; ENTRY *ep; #if USE_GETCAP_CACHE char cwd_buf[PATH_MAX]; #endif #if USE_GETCAP char tc[TBUFSIZ]; static char *source; static int lineno; /* we're using getcap(3) */ if (_nc_tgetent(tc, &source, &lineno, tn) < 0) return (ERR); _nc_curr_line = lineno; _nc_set_source(source); _nc_read_entry_source((FILE *)0, tc, FALSE, FALSE, NULLHOOK); #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 && (!issetugid() || !is_pathname(tc))) { 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); } } } } 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 (!issetugid() && (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); } } /* 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; 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]); /* * 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); } } } #endif /* USE_GETCAP */ if (_nc_head == 0) return(ERR); /* resolve all use references */ _nc_resolve_uses(); /* 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 */ #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). */ memcpy(tp, &ep->tterm, sizeof(TERMTYPE)); 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. */ #if USE_GETCAP_CACHE (void) _nc_write_entry(tp); #endif found = TRUE; break; } } #if USE_GETCAP_CACHE chdir(cwd_buf); } #endif _nc_free_entries(_nc_head); return(found); } #else extern void _nc_read_termcap(void); void _nc_read_termcap(void) { } #endif /* PURE_TERMINFO */