* Copyright (c) 1980 The Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)termcap.c 5.5 (Berkeley) %G%";
#define MAXHOP 32 /* max number of tc= indirections */
#define PBUFSIZ 512 /* max length of filename path */
#define PVECSIZ 32 /* max number of names in path */
* termcap - routines for dealing with the terminal capability data base
* BUG: Should use a "last" pointer in tbuf, so that searching
* for capabilities alphabetically would not be a n**2/2
* process when large numbers of capabilities are given.
* Note: If we add a last pointer now we will screw up the
* tc capability. We really should compile termcap.
* Essentially all the work here is scanning and decoding escapes
* in string capabilities. We don't use stdio because the editor
* doesn't, and because living w/o it is not hard.
static int hopcount
; /* detect infinite loops in termcap, init 0 */
static char pathbuf
[PBUFSIZ
]; /* holds raw path of filenames */
static char *pathvec
[PVECSIZ
]; /* to point to names in pathbuf */
static char **pvec
; /* holds usable tail of path vector */
* Get an entry for terminal name in buffer bp from the termcap file.
char *term
, *home
, *termpath
;
* 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
);
if (home
= getenv("HOME")) { /* set up default */
p
+= strlen(home
); /* path, looking in */
strcpy(pathbuf
, home
); /* $HOME first */
} /* 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 */
*fname
++ = pathbuf
; /* tokenize path into vector of names */
if (*p
== ' ' || *p
== ':') {
if (*p
!= ' ' && *p
!= ':')
if (fname
>= pathvec
+ PVECSIZ
) {
*fname
= (char *) 0; /* mark end of vector */
if (cp
&& *cp
&& *cp
!= '/') {
return (tfindent(bp
, name
)); /* find terminal entry in path */
* tfindent - reads through the list of files in pathvec as if they were one
* continuous file searching for terminal entries along the way. It will
* participate in indirect recursion if the call to tnchktc() finds a tc=
* field, which is only searched for in the current file and files ocurring
* after it in pathvec. The usable part of this vector is kept in the global
* variable pvec. Terminal entries may not be broken across files. Parse is
* very rudimentary; we just notice escaped newlines.
while (*pvec
&& (tf
= open(*pvec
, 0)) < 0)
return (opencnt
? 0 : -1);
cnt
= read(tf
, ibuf
, BUFSIZ
);
if (cp
> bp
&& cp
[-1] == '\\'){
write(2,"Termcap entry too long\n", 23);
* The real work for the match.
* tnchktc: check the last entry, see if it's tc=xxx. If so,
* recursively find xxx and append that entry (minus the names)
* to take the place of the tc=xxx entry. This allows termcap
* entries to say "like an HP2621 but doesn't turn on the labels".
* Note that this works because of the left to right scan.
char tcname
[16]; /* name of similar terminal */
p
= tbuf
+ strlen(tbuf
) - 2; /* before the last colon */
write(2, "Bad termcap entry\n", 18);
/* p now points to beginning of last field */
if (p
[0] != 't' || p
[1] != 'c')
if (++hopcount
> MAXHOP
) {
write(2, "Infinite tc= loop\n", 18);
if (tfindent(tcbuf
, tcname
) != 1) {
hopcount
= 0; /* unwind recursion */
for (q
=tcbuf
; *q
!= ':'; q
++)
l
= p
- holdtbuf
+ strlen(q
);
write(2, "Termcap entry too long\n", 23);
q
[BUFSIZ
- (p
-tbuf
)] = 0;
hopcount
= 0; /* unwind recursion */
* Tnamatch deals with name matching. The first field of the termcap
* entry is a sequence of names separated by |'s, so we compare
* against each such name. The normal : terminator after the last
* name (before the first field) stops us.
for (Np
= np
; *Np
&& *Bp
== *Np
; Bp
++, Np
++)
if (*Np
== 0 && (*Bp
== '|' || *Bp
== ':' || *Bp
== 0))
while (*Bp
&& *Bp
!= ':' && *Bp
!= '|')
if (*Bp
== 0 || *Bp
== ':')
* Skip to the next field. Notice that this is very dumb, not
* knowing about \: escapes or any such. If necessary, :'s can be put
* into the termcap file in octal.
while (*bp
&& *bp
!= ':')
* Return the (numeric) option id.
* Numeric options look like
* i.e. the option string is separated from the numeric value by
* a # character. If the option is not found we return -1.
* Note that we handle octal numbers beginning with 0.
register char *bp
= tbuf
;
if (*bp
++ != id
[0] || *bp
== 0 || *bp
++ != id
[1])
i
*= base
, i
+= *bp
++ - '0';
* Flag options are given "naked", i.e. followed by a : or the end
* of the buffer. Return 1 if we find the option, or 0 if it is
register char *bp
= tbuf
;
if (*bp
++ == id
[0] && *bp
!= 0 && *bp
++ == id
[1]) {
* Get a string valued option.
* Much decoding is done on the strings, and the strings are
* placed in area, which is a ref parameter which is updated.
* No checking on area overflow.
register char *bp
= tbuf
;
if (*bp
++ != id
[0] || *bp
== 0 || *bp
++ != id
[1])
return (tdecode(bp
, area
));
* Tdecode does the grung work to decode the
* string capability escapes.
while ((c
= *str
++) && c
!= ':') {
dp
= "E\033^^\\\\::n\nr\rt\tb\bf\f";
c
<<= 3, c
|= *str
++ - '0';
while (--i
&& isdigit(*str
));