xref: /csrg-svn/usr.bin/tip/remcap.c (revision 13278)
1*13278Ssam #ifndef lint
2*13278Ssam static char sccsid[] = "@(#)remcap.c	4.8 (Berkeley) 06/25/83";
34003Ssam #endif
43694Sroot 
53694Sroot /*
63805Ssam  * remcap - routines for dealing with the remote host data base
73694Sroot  *
8*13278Ssam  * derived from termcap
93805Ssam  */
10*13278Ssam #include <sys/file.h>
11*13278Ssam #include <ctype.h>
123805Ssam 
13*13278Ssam #ifndef BUFSIZ
14*13278Ssam #define	BUFSIZ		1024
15*13278Ssam #endif
16*13278Ssam #define MAXHOP		32		/* max number of tc= indirections */
17*13278Ssam #define SYSREMOTE	"/etc/remote"	/* system remote file */
18*13278Ssam 
193805Ssam #define	tgetent		rgetent
203805Ssam #define	tnchktc		rnchktc
213805Ssam #define	tnamatch	rnamatch
223805Ssam #define	tgetnum		rgetnum
233805Ssam #define	tgetflag	rgetflag
243805Ssam #define	tgetstr		rgetstr
2513138Sralph #define	E_TERMCAP	RM = SYSREMOTE
263808Ssam #define V_TERMCAP	"REMOTE"
273808Ssam #define V_TERM		"HOST"
283808Ssam 
293805Ssam char	*RM;
303805Ssam 
313805Ssam /*
323805Ssam  * termcap - routines for dealing with the terminal capability data base
333805Ssam  *
343694Sroot  * BUG:		Should use a "last" pointer in tbuf, so that searching
353694Sroot  *		for capabilities alphabetically would not be a n**2/2
363694Sroot  *		process when large numbers of capabilities are given.
373805Ssam  * Note:	If we add a last pointer now we will screw up the
383805Ssam  *		tc capability. We really should compile termcap.
393694Sroot  *
403694Sroot  * Essentially all the work here is scanning and decoding escapes
413805Ssam  * in string capabilities.  We don't use stdio because the editor
423805Ssam  * doesn't, and because living w/o it is not hard.
433805Ssam  */
443694Sroot 
453805Ssam static	char *tbuf;
463805Ssam static	int hopcount;	/* detect infinite loops in termcap, init 0 */
473805Ssam char	*tskip();
483805Ssam char	*tgetstr();
493805Ssam char	*tdecode();
503694Sroot char	*getenv();
51*13278Ssam static	char *remotefile;
523694Sroot 
533694Sroot /*
543805Ssam  * Get an entry for terminal name in buffer bp,
553805Ssam  * from the termcap file.  Parse is very rudimentary;
563694Sroot  * we just notice escaped newlines.
573694Sroot  */
583805Ssam tgetent(bp, name)
593694Sroot 	char *bp, *name;
603694Sroot {
61*13278Ssam 	char lbuf[BUFSIZ], *cp, *p;
62*13278Ssam 	int rc1, rc2;
6313138Sralph 
6413138Sralph 	remotefile = cp = getenv(V_TERMCAP);
6513138Sralph 	if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) {
6613138Sralph 		remotefile = cp = SYSREMOTE;
67*13278Ssam 		return (getent(bp, name, cp));
6813138Sralph 	} else {
6913138Sralph 		if ((rc1 = getent(bp, name, cp)) != 1)
7013138Sralph 			*bp = '\0';
7113138Sralph 		remotefile = cp = SYSREMOTE;
7213138Sralph 		rc2 = getent(lbuf, name, cp);
7313138Sralph 		if (rc1 != 1 && rc2 != 1)
74*13278Ssam 			return (rc2);
7513138Sralph 		if (rc2 == 1) {
7613138Sralph 			p = lbuf;
7713138Sralph 			if (rc1 == 1)
7813138Sralph 				while (*p++ != ':')
7913138Sralph 					;
8013138Sralph 			if (strlen(bp) + strlen(p) > BUFSIZ) {
8113138Sralph 				write(2, "Remcap entry too long\n", 23);
82*13278Ssam 				return (-1);
8313138Sralph 			}
8413138Sralph 			strcat(bp, p);
8513138Sralph 		}
8613138Sralph 		tbuf = bp;
87*13278Ssam 		return (1);
8813138Sralph 	}
8913138Sralph }
9013138Sralph 
9113138Sralph getent(bp, name, cp)
9213138Sralph 	char *bp, *name, *cp;
9313138Sralph {
943694Sroot 	register int c;
953694Sroot 	register int i = 0, cnt = 0;
96*13278Ssam 	char ibuf[BUFSIZ], *cp2;
973694Sroot 	int tf;
983694Sroot 
993805Ssam 	tbuf = bp;
1003805Ssam 	tf = 0;
1013805Ssam 	/*
1023805Ssam 	 * TERMCAP can have one of two things in it. It can be the
1033805Ssam 	 * name of a file to use instead of /etc/termcap. In this
1043805Ssam 	 * case it better start with a "/". Or it can be an entry to
1053805Ssam 	 * use so we don't have to read the file. In this case it
1063805Ssam 	 * has to already have the newlines crunched out.
1073805Ssam 	 */
1083805Ssam 	if (cp && *cp) {
1093805Ssam 		if (*cp!='/') {
1103808Ssam 			cp2 = getenv(V_TERM);
111*13278Ssam 			if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
1123805Ssam 				strcpy(bp,cp);
113*13278Ssam 				return (tnchktc());
114*13278Ssam 			} else
115*13278Ssam 				tf = open(E_TERMCAP, O_RDONLY);
1163805Ssam 		} else
117*13278Ssam 			tf = open(RM = cp, O_RDONLY);
1183805Ssam 	}
119*13278Ssam 	if (tf == 0)
120*13278Ssam 		tf = open(E_TERMCAP, O_RDONLY);
1213805Ssam 	if (tf < 0)
1223694Sroot 		return (-1);
1233694Sroot 	for (;;) {
1243694Sroot 		cp = bp;
1253694Sroot 		for (;;) {
1263694Sroot 			if (i == cnt) {
1273694Sroot 				cnt = read(tf, ibuf, BUFSIZ);
1283694Sroot 				if (cnt <= 0) {
1293694Sroot 					close(tf);
1303694Sroot 					return (0);
1313694Sroot 				}
1323694Sroot 				i = 0;
1333694Sroot 			}
1343694Sroot 			c = ibuf[i++];
1353694Sroot 			if (c == '\n') {
13613138Sralph 				if (cp > bp && cp[-1] == '\\') {
1373694Sroot 					cp--;
1383694Sroot 					continue;
1393694Sroot 				}
1403694Sroot 				break;
1413694Sroot 			}
1423805Ssam 			if (cp >= bp+BUFSIZ) {
14313138Sralph 				write(2,"Remcap entry too long\n", 23);
1443805Ssam 				break;
1453805Ssam 			} else
1463805Ssam 				*cp++ = c;
1473694Sroot 		}
1483694Sroot 		*cp = 0;
1493694Sroot 
1503694Sroot 		/*
1513694Sroot 		 * The real work for the match.
1523694Sroot 		 */
1533805Ssam 		if (tnamatch(name)) {
1543694Sroot 			close(tf);
155*13278Ssam 			return (tnchktc());
1563694Sroot 		}
1573694Sroot 	}
1583694Sroot }
1593694Sroot 
1603694Sroot /*
1613805Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1623805Ssam  * recursively find xxx and append that entry (minus the names)
1633805Ssam  * to take the place of the tc=xxx entry. This allows termcap
1643805Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
1653805Ssam  * Note that this works because of the left to right scan.
1663805Ssam  */
1673805Ssam tnchktc()
1683805Ssam {
1693805Ssam 	register char *p, *q;
1703805Ssam 	char tcname[16];	/* name of similar terminal */
1713805Ssam 	char tcbuf[BUFSIZ];
1723805Ssam 	char *holdtbuf = tbuf;
1733805Ssam 	int l;
17413138Sralph 	char *cp;
1753805Ssam 
1763805Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
1773805Ssam 	while (*--p != ':')
1783805Ssam 		if (p<tbuf) {
17913138Sralph 			write(2, "Bad remcap entry\n", 18);
1803805Ssam 			return (0);
1813805Ssam 		}
1823805Ssam 	p++;
1833805Ssam 	/* p now points to beginning of last field */
1843805Ssam 	if (p[0] != 't' || p[1] != 'c')
185*13278Ssam 		return (1);
18613138Sralph 	strcpy(tcname, p+3);
1873805Ssam 	q = tcname;
18813138Sralph 	while (*q && *q != ':')
1893805Ssam 		q++;
1903805Ssam 	*q = 0;
1913805Ssam 	if (++hopcount > MAXHOP) {
1923805Ssam 		write(2, "Infinite tc= loop\n", 18);
1933805Ssam 		return (0);
1943805Ssam 	}
19513138Sralph 	if (getent(tcbuf, tcname, remotefile) != 1) {
19613138Sralph 		if (strcmp(remotefile, SYSREMOTE) == 0)
197*13278Ssam 			return (0);
19813138Sralph 		else if (getent(tcbuf, tcname, SYSREMOTE) != 1)
199*13278Ssam 			return (0);
20013138Sralph 	}
20113138Sralph 	for (q = tcbuf; *q++ != ':'; )
2023805Ssam 		;
2033805Ssam 	l = p - holdtbuf + strlen(q);
2043805Ssam 	if (l > BUFSIZ) {
20513138Sralph 		write(2, "Remcap entry too long\n", 23);
20613138Sralph 		q[BUFSIZ - (p-holdtbuf)] = 0;
2073805Ssam 	}
20813138Sralph 	strcpy(p, q);
2093805Ssam 	tbuf = holdtbuf;
210*13278Ssam 	return (1);
2113805Ssam }
2123805Ssam 
2133805Ssam /*
2143805Ssam  * Tnamatch deals with name matching.  The first field of the termcap
2153694Sroot  * entry is a sequence of names separated by |'s, so we compare
2163694Sroot  * against each such name.  The normal : terminator after the last
2173694Sroot  * name (before the first field) stops us.
2183694Sroot  */
2193805Ssam tnamatch(np)
2203694Sroot 	char *np;
2213694Sroot {
2223694Sroot 	register char *Np, *Bp;
2233694Sroot 
2243805Ssam 	Bp = tbuf;
2253805Ssam 	if (*Bp == '#')
226*13278Ssam 		return (0);
2273694Sroot 	for (;;) {
2283694Sroot 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2293694Sroot 			continue;
2303694Sroot 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2313694Sroot 			return (1);
2323694Sroot 		while (*Bp && *Bp != ':' && *Bp != '|')
2333694Sroot 			Bp++;
2343694Sroot 		if (*Bp == 0 || *Bp == ':')
2353694Sroot 			return (0);
2363694Sroot 		Bp++;
2373694Sroot 	}
2383694Sroot }
2393694Sroot 
2403694Sroot /*
2413694Sroot  * Skip to the next field.  Notice that this is very dumb, not
2423694Sroot  * knowing about \: escapes or any such.  If necessary, :'s can be put
2433805Ssam  * into the termcap file in octal.
2443694Sroot  */
2453694Sroot static char *
2463805Ssam tskip(bp)
2473694Sroot 	register char *bp;
2483694Sroot {
2493694Sroot 
2503694Sroot 	while (*bp && *bp != ':')
2513694Sroot 		bp++;
2523694Sroot 	if (*bp == ':')
2533694Sroot 		bp++;
2543694Sroot 	return (bp);
2553694Sroot }
2563694Sroot 
2573694Sroot /*
2583694Sroot  * Return the (numeric) option id.
2593694Sroot  * Numeric options look like
2603694Sroot  *	li#80
2613694Sroot  * i.e. the option string is separated from the numeric value by
2623694Sroot  * a # character.  If the option is not found we return -1.
2633694Sroot  * Note that we handle octal numbers beginning with 0.
2643694Sroot  */
2653805Ssam tgetnum(id)
2663694Sroot 	char *id;
2673694Sroot {
2683694Sroot 	register int i, base;
2693805Ssam 	register char *bp = tbuf;
2703694Sroot 
2713694Sroot 	for (;;) {
2723805Ssam 		bp = tskip(bp);
2733694Sroot 		if (*bp == 0)
2743694Sroot 			return (-1);
2753694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2763694Sroot 			continue;
2773805Ssam 		if (*bp == '@')
278*13278Ssam 			return (-1);
2793694Sroot 		if (*bp != '#')
2803694Sroot 			continue;
2813694Sroot 		bp++;
2823694Sroot 		base = 10;
2833694Sroot 		if (*bp == '0')
2843694Sroot 			base = 8;
2853694Sroot 		i = 0;
2863694Sroot 		while (isdigit(*bp))
2873694Sroot 			i *= base, i += *bp++ - '0';
2883694Sroot 		return (i);
2893694Sroot 	}
2903694Sroot }
2913694Sroot 
2923694Sroot /*
2933694Sroot  * Handle a flag option.
2943694Sroot  * Flag options are given "naked", i.e. followed by a : or the end
2953694Sroot  * of the buffer.  Return 1 if we find the option, or 0 if it is
2963694Sroot  * not given.
2973694Sroot  */
2983805Ssam tgetflag(id)
2993694Sroot 	char *id;
3003694Sroot {
3013805Ssam 	register char *bp = tbuf;
3023694Sroot 
3033694Sroot 	for (;;) {
3043805Ssam 		bp = tskip(bp);
3053694Sroot 		if (!*bp)
3063694Sroot 			return (0);
3073805Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3083805Ssam 			if (!*bp || *bp == ':')
3093805Ssam 				return (1);
3103805Ssam 			else if (*bp == '@')
311*13278Ssam 				return (0);
3123805Ssam 		}
3133694Sroot 	}
3143694Sroot }
3153694Sroot 
3163694Sroot /*
3173694Sroot  * Get a string valued option.
3183694Sroot  * These are given as
3193694Sroot  *	cl=^Z
3203694Sroot  * Much decoding is done on the strings, and the strings are
3213694Sroot  * placed in area, which is a ref parameter which is updated.
3223694Sroot  * No checking on area overflow.
3233694Sroot  */
3243694Sroot char *
3253805Ssam tgetstr(id, area)
3263694Sroot 	char *id, **area;
3273694Sroot {
3283805Ssam 	register char *bp = tbuf;
3293694Sroot 
3303694Sroot 	for (;;) {
3313805Ssam 		bp = tskip(bp);
3323694Sroot 		if (!*bp)
3333694Sroot 			return (0);
3343694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3353694Sroot 			continue;
3363805Ssam 		if (*bp == '@')
337*13278Ssam 			return (0);
3383694Sroot 		if (*bp != '=')
3393694Sroot 			continue;
3403694Sroot 		bp++;
3413805Ssam 		return (tdecode(bp, area));
3423694Sroot 	}
3433694Sroot }
3443694Sroot 
3453694Sroot /*
3463694Sroot  * Tdecode does the grung work to decode the
3473694Sroot  * string capability escapes.
3483694Sroot  */
3493805Ssam static char *
3503805Ssam tdecode(str, area)
3513694Sroot 	register char *str;
3523694Sroot 	char **area;
3533694Sroot {
3543694Sroot 	register char *cp;
3553694Sroot 	register int c;
3563694Sroot 	register char *dp;
3573694Sroot 	int i;
3583694Sroot 
3593694Sroot 	cp = *area;
3603694Sroot 	while ((c = *str++) && c != ':') {
3613694Sroot 		switch (c) {
3623694Sroot 
3633694Sroot 		case '^':
3643694Sroot 			c = *str++ & 037;
3653694Sroot 			break;
3663694Sroot 
3673694Sroot 		case '\\':
3683694Sroot 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
3693694Sroot 			c = *str++;
3703694Sroot nextc:
3713694Sroot 			if (*dp++ == c) {
3723694Sroot 				c = *dp++;
3733694Sroot 				break;
3743694Sroot 			}
3753694Sroot 			dp++;
3763694Sroot 			if (*dp)
3773694Sroot 				goto nextc;
3783694Sroot 			if (isdigit(c)) {
3793694Sroot 				c -= '0', i = 2;
3803694Sroot 				do
3813694Sroot 					c <<= 3, c |= *str++ - '0';
3823694Sroot 				while (--i && isdigit(*str));
3833694Sroot 			}
3843694Sroot 			break;
3853694Sroot 		}
3863694Sroot 		*cp++ = c;
3873694Sroot 	}
3883694Sroot 	*cp++ = 0;
3893694Sroot 	str = *area;
3903694Sroot 	*area = cp;
3913694Sroot 	return (str);
3923694Sroot }
393