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) { 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); if ((h = getenv("HOME")) != NULL && strlen(h) + 9 < PATH_MAX) { /* user's .termcap, if any, should override it */ (void) strcpy(envhome, h); (void) sprintf(pathbuf, "%s/.termcap", 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); }
/* * Get an entry for terminal name in buffer _nc_termcap from the termcap * file. */ int _nc_read_termcap_entry(const char *const name, TERMTYPE *const tp) { ENTRY *ep; char *p; 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; _nc_termcap[0] = '\0'; /* in case */ dummy = NULL; fname = pathvec; pvec = pathvec; p = pathbuf; cp = getenv("TERMCAP"); /* * TERMCAP can have one of two things in it. It can be the * name of a file to use instead of /etc/termcap. In this * case it better start with a "/". Or it can be an entry to * use so we don't have to read the file. In this case it * has to already have the newlines crunched out. If TERMCAP * does not hold a file name then a path of names is searched * instead. The path is found in the TERMPATH variable, or * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists. */ if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */ if ( (termpath = getenv("TERMPATH")) ) strncpy(pathbuf, termpath, PBUFSIZ); else { if ( (home = getenv("HOME")) ) {/* set up default */ strncpy(pathbuf, home, PBUFSIZ - 1); /* $HOME first */ pathbuf[PBUFSIZ - 2] = '\0'; /* -2 because we add a slash */ p += strlen(pathbuf); /* path, looking in */ *p++ = '/'; } /* if no $HOME look in current directory */ strncpy(p, _PATH_DEF, PBUFSIZ - (p - pathbuf)); } } else /* user-defined name in TERMCAP */ strncpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */ /* For safety */ if (issetugid()) strcpy(pathbuf, _PATH_DEF_SEC); pathbuf[PBUFSIZ - 1] = '\0'; *fname++ = pathbuf; /* tokenize path into vector of names */ while (*++p) if (*p == ' ' || *p == ':') { *p = '\0'; while (*++p) if (*p != ' ' && *p != ':') break; if (*p == '\0') break; *fname++ = p; if (fname >= pathvec + PVECSIZ) { fname--; break; } } *fname = (char *) 0; /* mark end of vector */ if (cp && *cp && *cp != '/') if (cgetset(cp) < 0) return(-2); i = cgetent(&dummy, pathvec, (char *)name); if (i == 0) { char *pd, *ps, *tok, *s, *tcs; size_t len; pd = _nc_termcap; ps = dummy; if ((tok = strchr(ps, ':')) == NULL) { len = strlen(ps); if (len >= TBUFSIZ) i = -1; else strcpy(pd, ps); goto done; } len = tok - ps + 1; if (pd + len + 1 - _nc_termcap >= TBUFSIZ) { i = -1; goto done; } memcpy(pd, ps, len); ps += len; pd += len; *pd = '\0'; tcs = pd - 1; for (;;) { while ((tok = strsep(&ps, ":")) != NULL && *(tok - 2) != '\\' && (*tok == '\0' || *tok == '\\' || !isgraph(UChar(*tok)))) ; if (tok == NULL) break; for (s = tcs; s != NULL && s[1] != '\0'; s = strchr(s, ':')) { s++; if (s[0] == tok[0] && s[1] == tok[1]) goto skip_it; } len = strlen(tok); if (pd + len + 1 - _nc_termcap >= TBUFSIZ) { i = -1; break; } memcpy(pd, tok, len); pd += len; *pd++ = ':'; *pd = '\0'; skip_it: ; } } done: if (dummy) free(dummy); /* * From here on is ncurses-specific glue code */ if (i < 0) return(TGETENT_ERR); _nc_set_source("TERMCAP"); _nc_read_entry_source((FILE *)NULL, _nc_termcap, FALSE, TRUE, NULLHOOK); if (_nc_head == (ENTRY *)NULL) return(TGETENT_ERR); /* resolve all use references */ _nc_resolve_uses2(TRUE, FALSE); for_entry_list(ep) if (_nc_name_match(ep->tterm.term_names, name, "|:")) { /* * Make a local copy of the terminal capabilities, delinked * from the list. */ memcpy(tp, &ep->tterm, sizeof(TERMTYPE)); _nc_delink_entry(_nc_head, &(ep->tterm)); free(ep); _nc_free_entries(_nc_head); _nc_head = _nc_tail = NULL; /* do not reuse! */ return TGETENT_YES; /* OK */ } _nc_free_entries(_nc_head); _nc_head = _nc_tail = NULL; /* do not reuse! */ return(TGETENT_NO); /* not found */ }