xref: /csrg-svn/lib/libterm/termcap.c (revision 22072)
1*22072Sdist /*
2*22072Sdist  * Copyright (c) 1980 Regents of the University of California.
3*22072Sdist  * All rights reserved.  The Berkeley software License Agreement
4*22072Sdist  * specifies the terms and conditions for redistribution.
5*22072Sdist  */
6*22072Sdist 
713300Ssam #ifndef lint
8*22072Sdist static char sccsid[] = "@(#)termcap.c	5.1 (Berkeley) 06/05/85";
9*22072Sdist #endif not lint
1013300Ssam 
1113300Ssam #define	BUFSIZ		1024
1213300Ssam #define MAXHOP		32	/* max number of tc= indirections */
1313300Ssam #define	E_TERMCAP	"/etc/termcap"
1413300Ssam 
1513300Ssam #include <ctype.h>
1613300Ssam /*
1713300Ssam  * termcap - routines for dealing with the terminal capability data base
1813300Ssam  *
1913300Ssam  * BUG:		Should use a "last" pointer in tbuf, so that searching
2013300Ssam  *		for capabilities alphabetically would not be a n**2/2
2113300Ssam  *		process when large numbers of capabilities are given.
2213300Ssam  * Note:	If we add a last pointer now we will screw up the
2313300Ssam  *		tc capability. We really should compile termcap.
2413300Ssam  *
2513300Ssam  * Essentially all the work here is scanning and decoding escapes
2613300Ssam  * in string capabilities.  We don't use stdio because the editor
2713300Ssam  * doesn't, and because living w/o it is not hard.
2813300Ssam  */
2913300Ssam 
3013300Ssam static	char *tbuf;
3113300Ssam static	int hopcount;	/* detect infinite loops in termcap, init 0 */
3213300Ssam char	*tskip();
3313300Ssam char	*tgetstr();
3413300Ssam char	*tdecode();
3513300Ssam char	*getenv();
3613300Ssam 
3713300Ssam /*
3813300Ssam  * Get an entry for terminal name in buffer bp,
3913300Ssam  * from the termcap file.  Parse is very rudimentary;
4013300Ssam  * we just notice escaped newlines.
4113300Ssam  */
4213300Ssam tgetent(bp, name)
4313300Ssam 	char *bp, *name;
4413300Ssam {
4513300Ssam 	register char *cp;
4613300Ssam 	register int c;
4713300Ssam 	register int i = 0, cnt = 0;
4813300Ssam 	char ibuf[BUFSIZ];
4913300Ssam 	int tf;
5013300Ssam 
5113300Ssam 	tbuf = bp;
5217584Ssam 	tf = -1;
5313300Ssam #ifndef V6
5413300Ssam 	cp = getenv("TERMCAP");
5513300Ssam 	/*
5613300Ssam 	 * TERMCAP can have one of two things in it. It can be the
5713300Ssam 	 * name of a file to use instead of /etc/termcap. In this
5813300Ssam 	 * case it better start with a "/". Or it can be an entry to
5913300Ssam 	 * use so we don't have to read the file. In this case it
6013300Ssam 	 * has to already have the newlines crunched out.
6113300Ssam 	 */
6213300Ssam 	if (cp && *cp) {
6317676Sserge 		if (*cp == '/') {
6417676Sserge 			tf = open(cp, 0);
6517676Sserge 		} else {
6617676Sserge 			tbuf = cp;
6717676Sserge 			c = tnamatch(name);
6817676Sserge 			tbuf = bp;
6917676Sserge 			if (c) {
7013300Ssam 				strcpy(bp,cp);
7113300Ssam 				return(tnchktc());
7213300Ssam 			}
7317676Sserge 		}
7413300Ssam 	}
7517584Ssam 	if (tf < 0)
7613300Ssam 		tf = open(E_TERMCAP, 0);
7713300Ssam #else
7813300Ssam 	tf = open(E_TERMCAP, 0);
7913300Ssam #endif
8013300Ssam 	if (tf < 0)
8113300Ssam 		return (-1);
8213300Ssam 	for (;;) {
8313300Ssam 		cp = bp;
8413300Ssam 		for (;;) {
8513300Ssam 			if (i == cnt) {
8613300Ssam 				cnt = read(tf, ibuf, BUFSIZ);
8713300Ssam 				if (cnt <= 0) {
8813300Ssam 					close(tf);
8913300Ssam 					return (0);
9013300Ssam 				}
9113300Ssam 				i = 0;
9213300Ssam 			}
9313300Ssam 			c = ibuf[i++];
9413300Ssam 			if (c == '\n') {
9513300Ssam 				if (cp > bp && cp[-1] == '\\'){
9613300Ssam 					cp--;
9713300Ssam 					continue;
9813300Ssam 				}
9913300Ssam 				break;
10013300Ssam 			}
10113300Ssam 			if (cp >= bp+BUFSIZ) {
10213300Ssam 				write(2,"Termcap entry too long\n", 23);
10313300Ssam 				break;
10413300Ssam 			} else
10513300Ssam 				*cp++ = c;
10613300Ssam 		}
10713300Ssam 		*cp = 0;
10813300Ssam 
10913300Ssam 		/*
11013300Ssam 		 * The real work for the match.
11113300Ssam 		 */
11213300Ssam 		if (tnamatch(name)) {
11313300Ssam 			close(tf);
11413300Ssam 			return(tnchktc());
11513300Ssam 		}
11613300Ssam 	}
11713300Ssam }
11813300Ssam 
11913300Ssam /*
12013300Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
12113300Ssam  * recursively find xxx and append that entry (minus the names)
12213300Ssam  * to take the place of the tc=xxx entry. This allows termcap
12313300Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
12413300Ssam  * Note that this works because of the left to right scan.
12513300Ssam  */
12613300Ssam tnchktc()
12713300Ssam {
12813300Ssam 	register char *p, *q;
12913300Ssam 	char tcname[16];	/* name of similar terminal */
13013300Ssam 	char tcbuf[BUFSIZ];
13113300Ssam 	char *holdtbuf = tbuf;
13213300Ssam 	int l;
13313300Ssam 
13413300Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
13513300Ssam 	while (*--p != ':')
13613300Ssam 		if (p<tbuf) {
13713300Ssam 			write(2, "Bad termcap entry\n", 18);
13813300Ssam 			return (0);
13913300Ssam 		}
14013300Ssam 	p++;
14113300Ssam 	/* p now points to beginning of last field */
14213300Ssam 	if (p[0] != 't' || p[1] != 'c')
14313300Ssam 		return(1);
14413300Ssam 	strcpy(tcname,p+3);
14513300Ssam 	q = tcname;
14617338Sralph 	while (*q && *q != ':')
14713300Ssam 		q++;
14813300Ssam 	*q = 0;
14913300Ssam 	if (++hopcount > MAXHOP) {
15013300Ssam 		write(2, "Infinite tc= loop\n", 18);
15113300Ssam 		return (0);
15213300Ssam 	}
15317584Ssam 	if (tgetent(tcbuf, tcname) != 1) {
15417584Ssam 		hopcount = 0;		/* unwind recursion */
15513300Ssam 		return(0);
15617584Ssam 	}
15713300Ssam 	for (q=tcbuf; *q != ':'; q++)
15813300Ssam 		;
15913300Ssam 	l = p - holdtbuf + strlen(q);
16013300Ssam 	if (l > BUFSIZ) {
16113300Ssam 		write(2, "Termcap entry too long\n", 23);
16213300Ssam 		q[BUFSIZ - (p-tbuf)] = 0;
16313300Ssam 	}
16413300Ssam 	strcpy(p, q+1);
16513300Ssam 	tbuf = holdtbuf;
16617584Ssam 	hopcount = 0;			/* unwind recursion */
16713300Ssam 	return(1);
16813300Ssam }
16913300Ssam 
17013300Ssam /*
17113300Ssam  * Tnamatch deals with name matching.  The first field of the termcap
17213300Ssam  * entry is a sequence of names separated by |'s, so we compare
17313300Ssam  * against each such name.  The normal : terminator after the last
17413300Ssam  * name (before the first field) stops us.
17513300Ssam  */
17613300Ssam tnamatch(np)
17713300Ssam 	char *np;
17813300Ssam {
17913300Ssam 	register char *Np, *Bp;
18013300Ssam 
18113300Ssam 	Bp = tbuf;
18213300Ssam 	if (*Bp == '#')
18313300Ssam 		return(0);
18413300Ssam 	for (;;) {
18513300Ssam 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
18613300Ssam 			continue;
18713300Ssam 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
18813300Ssam 			return (1);
18913300Ssam 		while (*Bp && *Bp != ':' && *Bp != '|')
19013300Ssam 			Bp++;
19113300Ssam 		if (*Bp == 0 || *Bp == ':')
19213300Ssam 			return (0);
19313300Ssam 		Bp++;
19413300Ssam 	}
19513300Ssam }
19613300Ssam 
19713300Ssam /*
19813300Ssam  * Skip to the next field.  Notice that this is very dumb, not
19913300Ssam  * knowing about \: escapes or any such.  If necessary, :'s can be put
20013300Ssam  * into the termcap file in octal.
20113300Ssam  */
20213300Ssam static char *
20313300Ssam tskip(bp)
20413300Ssam 	register char *bp;
20513300Ssam {
20613300Ssam 
20713300Ssam 	while (*bp && *bp != ':')
20813300Ssam 		bp++;
20913300Ssam 	if (*bp == ':')
21013300Ssam 		bp++;
21113300Ssam 	return (bp);
21213300Ssam }
21313300Ssam 
21413300Ssam /*
21513300Ssam  * Return the (numeric) option id.
21613300Ssam  * Numeric options look like
21713300Ssam  *	li#80
21813300Ssam  * i.e. the option string is separated from the numeric value by
21913300Ssam  * a # character.  If the option is not found we return -1.
22013300Ssam  * Note that we handle octal numbers beginning with 0.
22113300Ssam  */
22213300Ssam tgetnum(id)
22313300Ssam 	char *id;
22413300Ssam {
22513300Ssam 	register int i, base;
22613300Ssam 	register char *bp = tbuf;
22713300Ssam 
22813300Ssam 	for (;;) {
22913300Ssam 		bp = tskip(bp);
23013300Ssam 		if (*bp == 0)
23113300Ssam 			return (-1);
23213300Ssam 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
23313300Ssam 			continue;
23413300Ssam 		if (*bp == '@')
23513300Ssam 			return(-1);
23613300Ssam 		if (*bp != '#')
23713300Ssam 			continue;
23813300Ssam 		bp++;
23913300Ssam 		base = 10;
24013300Ssam 		if (*bp == '0')
24113300Ssam 			base = 8;
24213300Ssam 		i = 0;
24313300Ssam 		while (isdigit(*bp))
24413300Ssam 			i *= base, i += *bp++ - '0';
24513300Ssam 		return (i);
24613300Ssam 	}
24713300Ssam }
24813300Ssam 
24913300Ssam /*
25013300Ssam  * Handle a flag option.
25113300Ssam  * Flag options are given "naked", i.e. followed by a : or the end
25213300Ssam  * of the buffer.  Return 1 if we find the option, or 0 if it is
25313300Ssam  * not given.
25413300Ssam  */
25513300Ssam tgetflag(id)
25613300Ssam 	char *id;
25713300Ssam {
25813300Ssam 	register char *bp = tbuf;
25913300Ssam 
26013300Ssam 	for (;;) {
26113300Ssam 		bp = tskip(bp);
26213300Ssam 		if (!*bp)
26313300Ssam 			return (0);
26413300Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
26513300Ssam 			if (!*bp || *bp == ':')
26613300Ssam 				return (1);
26713300Ssam 			else if (*bp == '@')
26813300Ssam 				return(0);
26913300Ssam 		}
27013300Ssam 	}
27113300Ssam }
27213300Ssam 
27313300Ssam /*
27413300Ssam  * Get a string valued option.
27513300Ssam  * These are given as
27613300Ssam  *	cl=^Z
27713300Ssam  * Much decoding is done on the strings, and the strings are
27813300Ssam  * placed in area, which is a ref parameter which is updated.
27913300Ssam  * No checking on area overflow.
28013300Ssam  */
28113300Ssam char *
28213300Ssam tgetstr(id, area)
28313300Ssam 	char *id, **area;
28413300Ssam {
28513300Ssam 	register char *bp = tbuf;
28613300Ssam 
28713300Ssam 	for (;;) {
28813300Ssam 		bp = tskip(bp);
28913300Ssam 		if (!*bp)
29013300Ssam 			return (0);
29113300Ssam 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
29213300Ssam 			continue;
29313300Ssam 		if (*bp == '@')
29413300Ssam 			return(0);
29513300Ssam 		if (*bp != '=')
29613300Ssam 			continue;
29713300Ssam 		bp++;
29813300Ssam 		return (tdecode(bp, area));
29913300Ssam 	}
30013300Ssam }
30113300Ssam 
30213300Ssam /*
30313300Ssam  * Tdecode does the grung work to decode the
30413300Ssam  * string capability escapes.
30513300Ssam  */
30613300Ssam static char *
30713300Ssam tdecode(str, area)
30813300Ssam 	register char *str;
30913300Ssam 	char **area;
31013300Ssam {
31113300Ssam 	register char *cp;
31213300Ssam 	register int c;
31313300Ssam 	register char *dp;
31413300Ssam 	int i;
31513300Ssam 
31613300Ssam 	cp = *area;
31713300Ssam 	while ((c = *str++) && c != ':') {
31813300Ssam 		switch (c) {
31913300Ssam 
32013300Ssam 		case '^':
32113300Ssam 			c = *str++ & 037;
32213300Ssam 			break;
32313300Ssam 
32413300Ssam 		case '\\':
32513300Ssam 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
32613300Ssam 			c = *str++;
32713300Ssam nextc:
32813300Ssam 			if (*dp++ == c) {
32913300Ssam 				c = *dp++;
33013300Ssam 				break;
33113300Ssam 			}
33213300Ssam 			dp++;
33313300Ssam 			if (*dp)
33413300Ssam 				goto nextc;
33513300Ssam 			if (isdigit(c)) {
33613300Ssam 				c -= '0', i = 2;
33713300Ssam 				do
33813300Ssam 					c <<= 3, c |= *str++ - '0';
33913300Ssam 				while (--i && isdigit(*str));
34013300Ssam 			}
34113300Ssam 			break;
34213300Ssam 		}
34313300Ssam 		*cp++ = c;
34413300Ssam 	}
34513300Ssam 	*cp++ = 0;
34613300Ssam 	str = *area;
34713300Ssam 	*area = cp;
34813300Ssam 	return (str);
34913300Ssam }
350