X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Ftinfo%2Fread_termcap.c;h=021fc7d1f197ad3acdd4a7a1be24d1d5c993d6f0;hp=26e72d40326345c20f5c702cc5b1145b2c677c7c;hb=d3aaa2f7ce1fec732af0f6c0396719cbc846ac18;hpb=b1f61d9f3aa244512045a6b02e759825d7049d34 diff --git a/ncurses/tinfo/read_termcap.c b/ncurses/tinfo/read_termcap.c index 26e72d40..021fc7d1 100644 --- a/ncurses/tinfo/read_termcap.c +++ b/ncurses/tinfo/read_termcap.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * + * Copyright (c) 1998-2011,2012 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,6 +29,7 @@ /**************************************************************************** * Author: Zeyd M. Ben-Halim 1992,1995 * * and: Eric S. Raymond * + * and: Thomas E. Dickey 1996-on * ****************************************************************************/ /* @@ -52,26 +53,39 @@ #include #include +#include #include -#include -MODULE_ID("$Id: read_termcap.c,v 1.47 2000/04/15 16:53:19 Todd.C.Miller Exp $") +MODULE_ID("$Id: read_termcap.c,v 1.87 2012/05/05 19:40:50 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 +#if !PURE_TERMINFO #define TC_SUCCESS 0 -#define TC_UNRESOLVED -1 -#define TC_NOT_FOUND -2 -#define TC_SYS_ERR -3 -#define TC_REF_LOOP -4 +#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 */ + +static NCURSES_CONST char * +get_termpath(void) +{ + NCURSES_CONST char *result; + + 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 @@ -81,7 +95,8 @@ MODULE_ID("$Id: read_termcap.c,v 1.47 2000/04/15 16:53:19 Todd.C.Miller Exp $") #define _nc_cgetset cgetset #else static int _nc_cgetmatch(char *, const char *); -static int _nc_getent(char **, unsigned *, 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 *); /*- @@ -99,11 +114,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. * @@ -124,13 +135,7 @@ 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 static size_t topreclen; /* toprec length */ static char *toprec; /* Additional record specified by cgetset() */ @@ -156,7 +161,7 @@ _nc_cgetset(const char *ent) return (-1); } gottoprec = 0; - (void) strcpy(toprec, ent); + _nc_STRCPY(toprec, ent, topreclen); return (0); } @@ -223,10 +228,10 @@ _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) @@ -257,15 +262,15 @@ _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 *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; @@ -289,7 +294,7 @@ _nc_getent( errno = ENOMEM; return (TC_SYS_ERR); } - (void) strcpy(record, toprec); + _nc_STRCPY(record, toprec, topreclen + BFRAG); rp = record + topreclen + 1; r_end = rp + BFRAG; current = in_array; @@ -318,7 +323,7 @@ _nc_getent( 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) { + || (fd = open(db_array[current], O_RDONLY, 0)) < 0) { /* No error on unfound file. */ if (errno == ENOENT) continue; @@ -378,7 +383,14 @@ _nc_getent( c = *bp++; if (c == '\n') { lineno++; - if (rp == record || *(rp - 1) != '\\') + /* + * 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++ = c; @@ -436,8 +448,10 @@ _nc_getent( break; } - if (!foundit) + if (!foundit) { + free(record); return (TC_NOT_FOUND); + } } /* @@ -449,7 +463,7 @@ _nc_getent( register int newilen; unsigned ilen; int diff, iret, tclen, oline; - char *icap, *scan, *tc, *tcstart, *tcend; + char *icap = 0, *scan, *tc, *tcstart, *tcend; /* * Loop invariants: @@ -462,8 +476,9 @@ _nc_getent( scan = record; tc_not_resolved = FALSE; for (;;) { - if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0) + if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0) { break; + } /* * Find end of tc=name and stomp on the trailing `:' @@ -480,8 +495,9 @@ _nc_getent( tclen = s - tcstart; tcend = s; + icap = 0; iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd, - tc, depth + 1, 0); + tc, depth + 1, 0); newicap = icap; /* Put into a register. */ newilen = ilen; if (iret != TC_SUCCESS) { @@ -490,12 +506,13 @@ _nc_getent( if (myfd) (void) close(fd); free(record); + FreeIfNeeded(icap); return (iret); } - if (iret == TC_UNRESOLVED) + if (iret == TC_UNRESOLVED) { tc_not_resolved = TRUE; - /* couldn't resolve tc */ - if (iret == TC_NOT_FOUND) { + /* couldn't resolve tc */ + } else if (iret == TC_NOT_FOUND) { *(s - 1) = ':'; scan = s - 1; tc_not_resolved = TRUE; @@ -575,8 +592,9 @@ _nc_getent( } *cap = record; - if (tc_not_resolved) + if (tc_not_resolved) { return (TC_UNRESOLVED); + } return (current); } @@ -691,8 +709,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 ':'. @@ -712,7 +728,7 @@ get_tc_token(char **srcp, int *endp) if (*s == '\0') { break; } else if (*s++ == '\n') { - while (isspace(*s)) + while (isspace(UChar(*s))) s++; } else { found = TRUE; @@ -726,7 +742,7 @@ get_tc_token(char **srcp, int *endp) break; } base = s; - } else if (isgraph(ch)) { + } else if (isgraph(UChar(ch))) { found = TRUE; } } @@ -746,7 +762,7 @@ copy_tc_token(char *dst, const char *src, size_t len) while ((ch = *src++) != '\0') { if (ch == '\\' && *src == '\n') { - while (isspace(*src)) + while (isspace(UChar(*src))) src++; continue; } @@ -769,20 +785,21 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) register char *p; register char *cp; - char *dummy; - char **fname; + char *dummy = NULL; + CGETENT_CONST 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; + CGETENT_CONST char *pathvec[PVECSIZ]; /* point to names in pathbuf */ + CGETENT_CONST char **pvec; /* holds usable tail of path vector */ + NCURSES_CONST char *termpath; + string_desc desc; + *lineno = 1; fname = pathvec; pvec = pathvec; - tbuf = bp; p = pathbuf; - cp = getenv("TERMCAP"); + cp = use_terminfo_vars()? getenv("TERMCAP") : NULL; /* * TERMCAP can have one of two things in it. It can be the name of a file @@ -793,29 +810,37 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) * 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); + _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 { - 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)); + 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'; + } 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 == ':') { + if (*p == ' ' || *p == NCURSES_PATHSEP) { *p = '\0'; while (*++p) - if (*p != ' ' && *p != ':') + if (*p != ' ' && *p != NCURSES_PATHSEP) break; if (*p == '\0') break; @@ -827,7 +852,10 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) } } *fname = 0; /* mark end of vector */ - if (is_pathname(cp)) { +#if !HAVE_BSD_CGETENT + (void) _nc_cgetset(0); +#endif + if (_nc_is_abs_path(cp)) { if (_nc_cgetset(cp) < 0) { return (TC_SYS_ERR); } @@ -839,6 +867,7 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) * 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; @@ -880,8 +909,21 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) * 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); @@ -899,43 +941,61 @@ _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) static int add_tc(char *termpaths[], char *path, int count) { + char *save = strchr(path, NCURSES_PATHSEP); + if (save != 0) + *save = '\0'; if (count < MAXPATHS - && _nc_access(path, R_OK) == 0) + && _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, TERMTYPE *const tp) { - int found = FALSE; + int found = TGETENT_NO; ENTRY *ep; #if USE_GETCAP_CACHE char cwd_buf[PATH_MAX]; #endif #if USE_GETCAP char *p, tc[TBUFSIZ]; + int status; static char *source; static int lineno; - if ((p = getenv("TERMCAP")) != 0 - && !is_pathname(p) && _nc_name_match(p, tn, "|:")) { + 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 */ strncpy(tc, p, sizeof(tc) - 1); tc[sizeof(tc) - 1] = '\0'; _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); + _nc_read_entry_source((FILE *) 0, tc, FALSE, TRUE, NULLHOOK); #else /* * Here is what the 4.4BSD termcap(3) page prescribes: @@ -966,53 +1026,84 @@ _nc_read_termcap_entry(const char *const tn, TERMTYPE * const tp) FILE *fp; char *tc, *termpaths[MAXPATHS]; int filecount = 0; + int j, k; bool use_buffer = FALSE; + bool normal = TRUE; char tc_buf[1024]; char pathbuf[PATH_MAX]; + char *copied = 0; + char *cp; + struct stat test_stat[MAXPATHS]; termpaths[filecount] = 0; - if ((tc = getenv("TERMCAP")) != 0) { - if (is_pathname(tc)) { /* interpret as a filename */ + 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 */ 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); - } - } + _nc_SPRINTF(tc_buf, + _nc_SLIMIT(sizeof(tc_buf)) + "%.*s\n", (int) sizeof(tc_buf) - 2, tc); + normal = FALSE; } - } else { /* normal case */ - char envhome[PATH_MAX], *h; + } - filecount = 0; + if (normal) { /* normal case */ + char envhome[PATH_MAX], *h; - /* - * 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); + 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); + } + } #define PRIVATE_CAP "%s/.termcap" - if ((h = getenv("HOME")) != NULL + 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 */ - (void) strcpy(envhome, h); - (void) sprintf(pathbuf, PRIVATE_CAP, envhome); + _nc_STRCPY(envhome, h, sizeof(envhome)); + _nc_SPRINTF(pathbuf, _nc_SLIMIT(sizeof(pathbuf)) + 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"); @@ -1027,8 +1118,9 @@ _nc_read_termcap_entry(const char *const tn, TERMTYPE * const tp) for (i = 0; i < filecount; i++) { - T(("Looking for %s in %s", tn, termpaths[i])); - if ((fp = fopen(termpaths[i], "r")) != (FILE *) 0) { + 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]); /* @@ -1042,13 +1134,15 @@ _nc_read_termcap_entry(const char *const tn, TERMTYPE * const tp) } } } + if (copied != 0) + free(copied); #endif /* USE_GETCAP */ if (_nc_head == 0) - return (ERR); + return (TGETENT_ERR); /* resolve all use references */ - _nc_resolve_uses(TRUE); + _nc_resolve_uses2(TRUE, FALSE); /* find a terminal matching tn, if we can */ #if USE_GETCAP_CACHE @@ -1058,13 +1152,11 @@ _nc_read_termcap_entry(const char *const tn, TERMTYPE * const tp) 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). + * Make a local copy of the terminal capabilities, delinked + * from the list. */ *tp = ep->tterm; - ep->tterm.str_table = (char *) 0; + _nc_free_entry(_nc_head, &(ep->tterm)); /* * OK, now try to write the type to user's terminfo directory. @@ -1081,7 +1173,7 @@ _nc_read_termcap_entry(const char *const tn, TERMTYPE * const tp) #if USE_GETCAP_CACHE (void) _nc_write_entry(tp); #endif - found = TRUE; + found = TGETENT_YES; break; } } @@ -1090,12 +1182,13 @@ _nc_read_termcap_entry(const char *const tn, TERMTYPE * const tp) } #endif - _nc_free_entries(_nc_head); return (found); } #else -extern void _nc_read_termcap(void); -void +extern +NCURSES_EXPORT(void) +_nc_read_termcap(void); +NCURSES_EXPORT(void) _nc_read_termcap(void) { }