xref: /csrg-svn/usr.bin/tip/remcap.c (revision 62315)
119808Sdist /*
2*62315Sbostic  * Copyright (c) 1983, 1993
3*62315Sbostic  *	The Regents of the University of California.  All rights reserved.
435464Sbostic  *
556641Sralph  * %sccs.include.redist.c%
619808Sdist  */
719808Sdist 
813278Ssam #ifndef lint
9*62315Sbostic static char sccsid[] = "@(#)remcap.c	8.1 (Berkeley) 06/06/93";
1035464Sbostic #endif /* not lint */
113694Sroot 
123694Sroot /*
133805Ssam  * remcap - routines for dealing with the remote host data base
143694Sroot  *
1513278Ssam  * derived from termcap
163805Ssam  */
1746231Storek #include <sys/types.h>
1846231Storek #include <fcntl.h>
1913278Ssam #include <ctype.h>
2056641Sralph #include <stdlib.h>
2137857Sbostic #include "pathnames.h"
223805Ssam 
2313278Ssam #ifndef BUFSIZ
2413278Ssam #define	BUFSIZ		1024
2513278Ssam #endif
2613278Ssam #define MAXHOP		32		/* max number of tc= indirections */
2713278Ssam 
283805Ssam #define	tgetent		rgetent
293805Ssam #define	tnchktc		rnchktc
303805Ssam #define	tnamatch	rnamatch
313805Ssam #define	tgetnum		rgetnum
323805Ssam #define	tgetflag	rgetflag
333805Ssam #define	tgetstr		rgetstr
3437857Sbostic #define	E_TERMCAP	RM = _PATH_REMOTE
353808Ssam #define V_TERMCAP	"REMOTE"
363808Ssam #define V_TERM		"HOST"
373808Ssam 
383805Ssam char	*RM;
393805Ssam 
403805Ssam /*
413805Ssam  * termcap - routines for dealing with the terminal capability data base
423805Ssam  *
433694Sroot  * BUG:		Should use a "last" pointer in tbuf, so that searching
443694Sroot  *		for capabilities alphabetically would not be a n**2/2
453694Sroot  *		process when large numbers of capabilities are given.
463805Ssam  * Note:	If we add a last pointer now we will screw up the
473805Ssam  *		tc capability. We really should compile termcap.
483694Sroot  *
493694Sroot  * Essentially all the work here is scanning and decoding escapes
503805Ssam  * in string capabilities.  We don't use stdio because the editor
513805Ssam  * doesn't, and because living w/o it is not hard.
523805Ssam  */
533694Sroot 
543805Ssam static	char *tbuf;
553805Ssam static	int hopcount;	/* detect infinite loops in termcap, init 0 */
5656641Sralph static	char *tskip();
573805Ssam char	*tgetstr();
5856641Sralph static	char *tdecode();
5913278Ssam static	char *remotefile;
603694Sroot 
613694Sroot /*
623805Ssam  * Get an entry for terminal name in buffer bp,
633805Ssam  * from the termcap file.  Parse is very rudimentary;
643694Sroot  * we just notice escaped newlines.
653694Sroot  */
tgetent(bp,name)663805Ssam tgetent(bp, name)
673694Sroot 	char *bp, *name;
683694Sroot {
6913278Ssam 	char lbuf[BUFSIZ], *cp, *p;
7013278Ssam 	int rc1, rc2;
7113138Sralph 
7213138Sralph 	remotefile = cp = getenv(V_TERMCAP);
7337857Sbostic 	if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) {
7437857Sbostic 		remotefile = cp = _PATH_REMOTE;
7513278Ssam 		return (getent(bp, name, cp));
7613138Sralph 	} else {
7713138Sralph 		if ((rc1 = getent(bp, name, cp)) != 1)
7813138Sralph 			*bp = '\0';
7937857Sbostic 		remotefile = cp = _PATH_REMOTE;
8013138Sralph 		rc2 = getent(lbuf, name, cp);
8113138Sralph 		if (rc1 != 1 && rc2 != 1)
8213278Ssam 			return (rc2);
8313138Sralph 		if (rc2 == 1) {
8413138Sralph 			p = lbuf;
8513138Sralph 			if (rc1 == 1)
8613138Sralph 				while (*p++ != ':')
8713138Sralph 					;
8813138Sralph 			if (strlen(bp) + strlen(p) > BUFSIZ) {
8913138Sralph 				write(2, "Remcap entry too long\n", 23);
9013278Ssam 				return (-1);
9113138Sralph 			}
9213138Sralph 			strcat(bp, p);
9313138Sralph 		}
9413138Sralph 		tbuf = bp;
9513278Ssam 		return (1);
9613138Sralph 	}
9713138Sralph }
9813138Sralph 
getent(bp,name,cp)9913138Sralph getent(bp, name, cp)
10013138Sralph 	char *bp, *name, *cp;
10113138Sralph {
1023694Sroot 	register int c;
1033694Sroot 	register int i = 0, cnt = 0;
10413278Ssam 	char ibuf[BUFSIZ], *cp2;
1053694Sroot 	int tf;
1063694Sroot 
1073805Ssam 	tbuf = bp;
1083805Ssam 	tf = 0;
1093805Ssam 	/*
1103805Ssam 	 * TERMCAP can have one of two things in it. It can be the
1113805Ssam 	 * name of a file to use instead of /etc/termcap. In this
1123805Ssam 	 * case it better start with a "/". Or it can be an entry to
1133805Ssam 	 * use so we don't have to read the file. In this case it
1143805Ssam 	 * has to already have the newlines crunched out.
1153805Ssam 	 */
1163805Ssam 	if (cp && *cp) {
1173805Ssam 		if (*cp!='/') {
1183808Ssam 			cp2 = getenv(V_TERM);
11913278Ssam 			if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
1203805Ssam 				strcpy(bp,cp);
12113278Ssam 				return (tnchktc());
12213278Ssam 			} else
12313278Ssam 				tf = open(E_TERMCAP, O_RDONLY);
1243805Ssam 		} else
12513278Ssam 			tf = open(RM = cp, O_RDONLY);
1263805Ssam 	}
12713278Ssam 	if (tf == 0)
12813278Ssam 		tf = open(E_TERMCAP, O_RDONLY);
1293805Ssam 	if (tf < 0)
1303694Sroot 		return (-1);
1313694Sroot 	for (;;) {
1323694Sroot 		cp = bp;
1333694Sroot 		for (;;) {
1343694Sroot 			if (i == cnt) {
1353694Sroot 				cnt = read(tf, ibuf, BUFSIZ);
1363694Sroot 				if (cnt <= 0) {
1373694Sroot 					close(tf);
1383694Sroot 					return (0);
1393694Sroot 				}
1403694Sroot 				i = 0;
1413694Sroot 			}
1423694Sroot 			c = ibuf[i++];
1433694Sroot 			if (c == '\n') {
14413138Sralph 				if (cp > bp && cp[-1] == '\\') {
1453694Sroot 					cp--;
1463694Sroot 					continue;
1473694Sroot 				}
1483694Sroot 				break;
1493694Sroot 			}
1503805Ssam 			if (cp >= bp+BUFSIZ) {
15113138Sralph 				write(2,"Remcap entry too long\n", 23);
1523805Ssam 				break;
1533805Ssam 			} else
1543805Ssam 				*cp++ = c;
1553694Sroot 		}
1563694Sroot 		*cp = 0;
1573694Sroot 
1583694Sroot 		/*
1593694Sroot 		 * The real work for the match.
1603694Sroot 		 */
1613805Ssam 		if (tnamatch(name)) {
1623694Sroot 			close(tf);
16313278Ssam 			return (tnchktc());
1643694Sroot 		}
1653694Sroot 	}
1663694Sroot }
1673694Sroot 
1683694Sroot /*
1693805Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1703805Ssam  * recursively find xxx and append that entry (minus the names)
1713805Ssam  * to take the place of the tc=xxx entry. This allows termcap
1723805Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
1733805Ssam  * Note that this works because of the left to right scan.
1743805Ssam  */
tnchktc()1753805Ssam tnchktc()
1763805Ssam {
1773805Ssam 	register char *p, *q;
1783805Ssam 	char tcname[16];	/* name of similar terminal */
1793805Ssam 	char tcbuf[BUFSIZ];
1803805Ssam 	char *holdtbuf = tbuf;
1813805Ssam 	int l;
18213138Sralph 	char *cp;
1833805Ssam 
1843805Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
1853805Ssam 	while (*--p != ':')
1863805Ssam 		if (p<tbuf) {
18713138Sralph 			write(2, "Bad remcap entry\n", 18);
1883805Ssam 			return (0);
1893805Ssam 		}
1903805Ssam 	p++;
1913805Ssam 	/* p now points to beginning of last field */
1923805Ssam 	if (p[0] != 't' || p[1] != 'c')
19313278Ssam 		return (1);
19413138Sralph 	strcpy(tcname, p+3);
1953805Ssam 	q = tcname;
19613138Sralph 	while (*q && *q != ':')
1973805Ssam 		q++;
1983805Ssam 	*q = 0;
1993805Ssam 	if (++hopcount > MAXHOP) {
2003805Ssam 		write(2, "Infinite tc= loop\n", 18);
2013805Ssam 		return (0);
2023805Ssam 	}
20313138Sralph 	if (getent(tcbuf, tcname, remotefile) != 1) {
20437857Sbostic 		if (strcmp(remotefile, _PATH_REMOTE) == 0)
20513278Ssam 			return (0);
20637857Sbostic 		else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1)
20713278Ssam 			return (0);
20813138Sralph 	}
20913138Sralph 	for (q = tcbuf; *q++ != ':'; )
2103805Ssam 		;
2113805Ssam 	l = p - holdtbuf + strlen(q);
2123805Ssam 	if (l > BUFSIZ) {
21313138Sralph 		write(2, "Remcap entry too long\n", 23);
21413138Sralph 		q[BUFSIZ - (p-holdtbuf)] = 0;
2153805Ssam 	}
21613138Sralph 	strcpy(p, q);
2173805Ssam 	tbuf = holdtbuf;
21813278Ssam 	return (1);
2193805Ssam }
2203805Ssam 
2213805Ssam /*
2223805Ssam  * Tnamatch deals with name matching.  The first field of the termcap
2233694Sroot  * entry is a sequence of names separated by |'s, so we compare
2243694Sroot  * against each such name.  The normal : terminator after the last
2253694Sroot  * name (before the first field) stops us.
2263694Sroot  */
tnamatch(np)2273805Ssam tnamatch(np)
2283694Sroot 	char *np;
2293694Sroot {
2303694Sroot 	register char *Np, *Bp;
2313694Sroot 
2323805Ssam 	Bp = tbuf;
2333805Ssam 	if (*Bp == '#')
23413278Ssam 		return (0);
2353694Sroot 	for (;;) {
2363694Sroot 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2373694Sroot 			continue;
2383694Sroot 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2393694Sroot 			return (1);
2403694Sroot 		while (*Bp && *Bp != ':' && *Bp != '|')
2413694Sroot 			Bp++;
2423694Sroot 		if (*Bp == 0 || *Bp == ':')
2433694Sroot 			return (0);
2443694Sroot 		Bp++;
2453694Sroot 	}
2463694Sroot }
2473694Sroot 
2483694Sroot /*
2493694Sroot  * Skip to the next field.  Notice that this is very dumb, not
2503694Sroot  * knowing about \: escapes or any such.  If necessary, :'s can be put
2513805Ssam  * into the termcap file in octal.
2523694Sroot  */
2533694Sroot static char *
tskip(bp)2543805Ssam tskip(bp)
2553694Sroot 	register char *bp;
2563694Sroot {
2573694Sroot 
2583694Sroot 	while (*bp && *bp != ':')
2593694Sroot 		bp++;
2603694Sroot 	if (*bp == ':')
2613694Sroot 		bp++;
2623694Sroot 	return (bp);
2633694Sroot }
2643694Sroot 
2653694Sroot /*
2663694Sroot  * Return the (numeric) option id.
2673694Sroot  * Numeric options look like
2683694Sroot  *	li#80
2693694Sroot  * i.e. the option string is separated from the numeric value by
2703694Sroot  * a # character.  If the option is not found we return -1.
2713694Sroot  * Note that we handle octal numbers beginning with 0.
2723694Sroot  */
tgetnum(id)2733805Ssam tgetnum(id)
2743694Sroot 	char *id;
2753694Sroot {
2763694Sroot 	register int i, base;
2773805Ssam 	register char *bp = tbuf;
2783694Sroot 
2793694Sroot 	for (;;) {
2803805Ssam 		bp = tskip(bp);
2813694Sroot 		if (*bp == 0)
2823694Sroot 			return (-1);
2833694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2843694Sroot 			continue;
2853805Ssam 		if (*bp == '@')
28613278Ssam 			return (-1);
2873694Sroot 		if (*bp != '#')
2883694Sroot 			continue;
2893694Sroot 		bp++;
2903694Sroot 		base = 10;
2913694Sroot 		if (*bp == '0')
2923694Sroot 			base = 8;
2933694Sroot 		i = 0;
2943694Sroot 		while (isdigit(*bp))
2953694Sroot 			i *= base, i += *bp++ - '0';
2963694Sroot 		return (i);
2973694Sroot 	}
2983694Sroot }
2993694Sroot 
3003694Sroot /*
3013694Sroot  * Handle a flag option.
3023694Sroot  * Flag options are given "naked", i.e. followed by a : or the end
3033694Sroot  * of the buffer.  Return 1 if we find the option, or 0 if it is
3043694Sroot  * not given.
3053694Sroot  */
tgetflag(id)3063805Ssam tgetflag(id)
3073694Sroot 	char *id;
3083694Sroot {
3093805Ssam 	register char *bp = tbuf;
3103694Sroot 
3113694Sroot 	for (;;) {
3123805Ssam 		bp = tskip(bp);
3133694Sroot 		if (!*bp)
3143694Sroot 			return (0);
3153805Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3163805Ssam 			if (!*bp || *bp == ':')
3173805Ssam 				return (1);
3183805Ssam 			else if (*bp == '@')
31913278Ssam 				return (0);
3203805Ssam 		}
3213694Sroot 	}
3223694Sroot }
3233694Sroot 
3243694Sroot /*
3253694Sroot  * Get a string valued option.
3263694Sroot  * These are given as
3273694Sroot  *	cl=^Z
3283694Sroot  * Much decoding is done on the strings, and the strings are
3293694Sroot  * placed in area, which is a ref parameter which is updated.
3303694Sroot  * No checking on area overflow.
3313694Sroot  */
3323694Sroot char *
tgetstr(id,area)3333805Ssam tgetstr(id, area)
3343694Sroot 	char *id, **area;
3353694Sroot {
3363805Ssam 	register char *bp = tbuf;
3373694Sroot 
3383694Sroot 	for (;;) {
3393805Ssam 		bp = tskip(bp);
3403694Sroot 		if (!*bp)
3413694Sroot 			return (0);
3423694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3433694Sroot 			continue;
3443805Ssam 		if (*bp == '@')
34513278Ssam 			return (0);
3463694Sroot 		if (*bp != '=')
3473694Sroot 			continue;
3483694Sroot 		bp++;
3493805Ssam 		return (tdecode(bp, area));
3503694Sroot 	}
3513694Sroot }
3523694Sroot 
3533694Sroot /*
3543694Sroot  * Tdecode does the grung work to decode the
3553694Sroot  * string capability escapes.
3563694Sroot  */
3573805Ssam static char *
tdecode(str,area)3583805Ssam tdecode(str, area)
3593694Sroot 	register char *str;
3603694Sroot 	char **area;
3613694Sroot {
3623694Sroot 	register char *cp;
3633694Sroot 	register int c;
3643694Sroot 	register char *dp;
3653694Sroot 	int i;
3663694Sroot 
3673694Sroot 	cp = *area;
3683694Sroot 	while ((c = *str++) && c != ':') {
3693694Sroot 		switch (c) {
3703694Sroot 
3713694Sroot 		case '^':
3723694Sroot 			c = *str++ & 037;
3733694Sroot 			break;
3743694Sroot 
3753694Sroot 		case '\\':
3763694Sroot 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
3773694Sroot 			c = *str++;
3783694Sroot nextc:
3793694Sroot 			if (*dp++ == c) {
3803694Sroot 				c = *dp++;
3813694Sroot 				break;
3823694Sroot 			}
3833694Sroot 			dp++;
3843694Sroot 			if (*dp)
3853694Sroot 				goto nextc;
3863694Sroot 			if (isdigit(c)) {
3873694Sroot 				c -= '0', i = 2;
3883694Sroot 				do
3893694Sroot 					c <<= 3, c |= *str++ - '0';
3903694Sroot 				while (--i && isdigit(*str));
3913694Sroot 			}
3923694Sroot 			break;
3933694Sroot 		}
3943694Sroot 		*cp++ = c;
3953694Sroot 	}
3963694Sroot 	*cp++ = 0;
3973694Sroot 	str = *area;
3983694Sroot 	*area = cp;
3993694Sroot 	return (str);
4003694Sroot }
401