xref: /csrg-svn/usr.bin/tip/remcap.c (revision 13138)
1*13138Sralph /*	remcap.c	4.7	83/06/15	*/
23694Sroot /* Copyright (c) 1979 Regents of the University of California */
3*13138Sralph /*
4*13138Sralph  *	Modified 9/27/82 - Michael Wendel
5*13138Sralph  *			   General Instrument R&D
6*13138Sralph  *		Looks in user Remote file first.
7*13138Sralph  *		Looks in system Remote file for each tc= entry
8*13138Sralph  *		that cannot be resolved in the user Remote file.
9*13138Sralph  *		Finally looks into the system Remote file to
10*13138Sralph  *		resolve remote name.
11*13138Sralph  *		User remote file will supplement the system file
12*13138Sralph  *		since all the entries in the user file occur
13*13138Sralph  *		ahead of duplicate entries from the system file.
14*13138Sralph  */
154003Ssam #ifndef BUFSIZ
163805Ssam #define	BUFSIZ	1024
174003Ssam #endif
183805Ssam #define MAXHOP	32	/* max number of tc= indirections */
193694Sroot 
203694Sroot #include <ctype.h>
214012Ssam #ifdef VMUNIX
223805Ssam #include "local/uparm.h"
234012Ssam #endif
243694Sroot /*
253805Ssam  * remcap - routines for dealing with the remote host data base
263694Sroot  *
273805Ssam  *	Made from termcap with the following defines.
283805Ssam  */
293805Ssam #define REMOTE		/* special for tip */
30*13138Sralph #define SYSREMOTE	"/etc/remote"  /* system remote file */
313805Ssam 
323805Ssam #ifdef REMOTE
333805Ssam #define	tgetent		rgetent
343805Ssam #define	tnchktc		rnchktc
353805Ssam #define	tnamatch	rnamatch
363805Ssam #define	tgetnum		rgetnum
373805Ssam #define	tgetflag	rgetflag
383805Ssam #define	tgetstr		rgetstr
393805Ssam #undef	E_TERMCAP
40*13138Sralph #define	E_TERMCAP	RM = SYSREMOTE
413808Ssam #define V_TERMCAP	"REMOTE"
423808Ssam #define V_TERM		"HOST"
433808Ssam 
443805Ssam char	*RM;
453808Ssam #else
463808Ssam #define	V_TERMCAP	"TERMCAP"
473808Ssam #define V_TERM		"TERM"
483805Ssam #endif
493805Ssam 
503805Ssam /*
513805Ssam  * termcap - routines for dealing with the terminal capability data base
523805Ssam  *
533694Sroot  * BUG:		Should use a "last" pointer in tbuf, so that searching
543694Sroot  *		for capabilities alphabetically would not be a n**2/2
553694Sroot  *		process when large numbers of capabilities are given.
563805Ssam  * Note:	If we add a last pointer now we will screw up the
573805Ssam  *		tc capability. We really should compile termcap.
583694Sroot  *
593694Sroot  * Essentially all the work here is scanning and decoding escapes
603805Ssam  * in string capabilities.  We don't use stdio because the editor
613805Ssam  * doesn't, and because living w/o it is not hard.
623805Ssam  */
633694Sroot 
64*13138Sralph static char *sccsid = "@(#)remcap.c	4.7 06/15/83";
653805Ssam static	char *tbuf;
663805Ssam static	int hopcount;	/* detect infinite loops in termcap, init 0 */
673805Ssam char	*tskip();
683805Ssam char	*tgetstr();
693805Ssam char	*tdecode();
703694Sroot char	*getenv();
71*13138Sralph static	char	*remotefile;
723694Sroot 
733694Sroot /*
743805Ssam  * Get an entry for terminal name in buffer bp,
753805Ssam  * from the termcap file.  Parse is very rudimentary;
763694Sroot  * we just notice escaped newlines.
773694Sroot  */
783805Ssam tgetent(bp, name)
793694Sroot 	char *bp, *name;
803694Sroot {
81*13138Sralph 	char lbuf[BUFSIZ];
82*13138Sralph 	int	rc1, rc2;
83*13138Sralph 	char *cp;
84*13138Sralph 	char *p;
85*13138Sralph 
86*13138Sralph 	remotefile = cp = getenv(V_TERMCAP);
87*13138Sralph 	if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) {
88*13138Sralph 		remotefile = cp = SYSREMOTE;
89*13138Sralph 		return(getent(bp, name, cp));
90*13138Sralph 	} else {
91*13138Sralph 		if ((rc1 = getent(bp, name, cp)) != 1)
92*13138Sralph 			*bp = '\0';
93*13138Sralph 		remotefile = cp = SYSREMOTE;
94*13138Sralph 		rc2 = getent(lbuf, name, cp);
95*13138Sralph 		if (rc1 != 1 && rc2 != 1)
96*13138Sralph 			return(rc2);
97*13138Sralph 		if (rc2 == 1) {
98*13138Sralph 			p = lbuf;
99*13138Sralph 			if (rc1 == 1)
100*13138Sralph 				while (*p++ != ':')
101*13138Sralph 					;
102*13138Sralph 			if (strlen(bp) + strlen(p) > BUFSIZ) {
103*13138Sralph 				write(2, "Remcap entry too long\n", 23);
104*13138Sralph 				return(-1);
105*13138Sralph 			}
106*13138Sralph 			strcat(bp, p);
107*13138Sralph 		}
108*13138Sralph 		tbuf = bp;
109*13138Sralph 		return(1);
110*13138Sralph 	}
111*13138Sralph }
112*13138Sralph 
113*13138Sralph getent(bp, name, cp)
114*13138Sralph 	char *bp, *name, *cp;
115*13138Sralph {
1163694Sroot 	register int c;
1173694Sroot 	register int i = 0, cnt = 0;
1183694Sroot 	char ibuf[BUFSIZ];
1193805Ssam 	char *cp2;
1203694Sroot 	int tf;
1213694Sroot 
1223805Ssam 	tbuf = bp;
1233805Ssam 	tf = 0;
1243805Ssam #ifndef V6
1253805Ssam 	/*
1263805Ssam 	 * TERMCAP can have one of two things in it. It can be the
1273805Ssam 	 * name of a file to use instead of /etc/termcap. In this
1283805Ssam 	 * case it better start with a "/". Or it can be an entry to
1293805Ssam 	 * use so we don't have to read the file. In this case it
1303805Ssam 	 * has to already have the newlines crunched out.
1313805Ssam 	 */
1323805Ssam 	if (cp && *cp) {
1333805Ssam 		if (*cp!='/') {
1343808Ssam 			cp2 = getenv(V_TERM);
1353805Ssam 			if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
1363805Ssam 				strcpy(bp,cp);
1373805Ssam 				return(tnchktc());
1383805Ssam 			} else {
1393805Ssam 				tf = open(E_TERMCAP, 0);
1403805Ssam 			}
1413805Ssam 		} else
1423808Ssam #ifdef REMOTE
1433808Ssam 			tf = open(RM = cp, 0);
1443808Ssam #else
1453805Ssam 			tf = open(cp, 0);
1463808Ssam #endif
1473805Ssam 	}
1483805Ssam 	if (tf==0)
1493805Ssam 		tf = open(E_TERMCAP, 0);
1503805Ssam #else
1513805Ssam 	tf = open(E_TERMCAP, 0);
1523805Ssam #endif
1533805Ssam 	if (tf < 0)
1543694Sroot 		return (-1);
1553694Sroot 	for (;;) {
1563694Sroot 		cp = bp;
1573694Sroot 		for (;;) {
1583694Sroot 			if (i == cnt) {
1593694Sroot 				cnt = read(tf, ibuf, BUFSIZ);
1603694Sroot 				if (cnt <= 0) {
1613694Sroot 					close(tf);
1623694Sroot 					return (0);
1633694Sroot 				}
1643694Sroot 				i = 0;
1653694Sroot 			}
1663694Sroot 			c = ibuf[i++];
1673694Sroot 			if (c == '\n') {
168*13138Sralph 				if (cp > bp && cp[-1] == '\\') {
1693694Sroot 					cp--;
1703694Sroot 					continue;
1713694Sroot 				}
1723694Sroot 				break;
1733694Sroot 			}
1743805Ssam 			if (cp >= bp+BUFSIZ) {
175*13138Sralph 				write(2,"Remcap entry too long\n", 23);
1763805Ssam 				break;
1773805Ssam 			} else
1783805Ssam 				*cp++ = c;
1793694Sroot 		}
1803694Sroot 		*cp = 0;
1813694Sroot 
1823694Sroot 		/*
1833694Sroot 		 * The real work for the match.
1843694Sroot 		 */
1853805Ssam 		if (tnamatch(name)) {
1863694Sroot 			close(tf);
1873805Ssam 			return(tnchktc());
1883694Sroot 		}
1893694Sroot 	}
1903694Sroot }
1913694Sroot 
1923694Sroot /*
1933805Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1943805Ssam  * recursively find xxx and append that entry (minus the names)
1953805Ssam  * to take the place of the tc=xxx entry. This allows termcap
1963805Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
1973805Ssam  * Note that this works because of the left to right scan.
1983805Ssam  */
1993805Ssam tnchktc()
2003805Ssam {
2013805Ssam 	register char *p, *q;
2023805Ssam 	char tcname[16];	/* name of similar terminal */
2033805Ssam 	char tcbuf[BUFSIZ];
2043805Ssam 	char *holdtbuf = tbuf;
2053805Ssam 	int l;
206*13138Sralph 	char *cp;
2073805Ssam 
2083805Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
2093805Ssam 	while (*--p != ':')
2103805Ssam 		if (p<tbuf) {
211*13138Sralph 			write(2, "Bad remcap entry\n", 18);
2123805Ssam 			return (0);
2133805Ssam 		}
2143805Ssam 	p++;
2153805Ssam 	/* p now points to beginning of last field */
2163805Ssam 	if (p[0] != 't' || p[1] != 'c')
2173805Ssam 		return(1);
218*13138Sralph 	strcpy(tcname, p+3);
2193805Ssam 	q = tcname;
220*13138Sralph 	while (*q && *q != ':')
2213805Ssam 		q++;
2223805Ssam 	*q = 0;
2233805Ssam 	if (++hopcount > MAXHOP) {
2243805Ssam 		write(2, "Infinite tc= loop\n", 18);
2253805Ssam 		return (0);
2263805Ssam 	}
227*13138Sralph 	if (getent(tcbuf, tcname, remotefile) != 1) {
228*13138Sralph 		if (strcmp(remotefile, SYSREMOTE) == 0)
229*13138Sralph 			return(0);
230*13138Sralph 		else if (getent(tcbuf, tcname, SYSREMOTE) != 1)
231*13138Sralph 			return(0);
232*13138Sralph 	}
233*13138Sralph 	for (q = tcbuf; *q++ != ':'; )
2343805Ssam 		;
2353805Ssam 	l = p - holdtbuf + strlen(q);
2363805Ssam 	if (l > BUFSIZ) {
237*13138Sralph 		write(2, "Remcap entry too long\n", 23);
238*13138Sralph 		q[BUFSIZ - (p-holdtbuf)] = 0;
2393805Ssam 	}
240*13138Sralph 	strcpy(p, q);
2413805Ssam 	tbuf = holdtbuf;
2423805Ssam 	return(1);
2433805Ssam }
2443805Ssam 
2453805Ssam /*
2463805Ssam  * Tnamatch deals with name matching.  The first field of the termcap
2473694Sroot  * entry is a sequence of names separated by |'s, so we compare
2483694Sroot  * against each such name.  The normal : terminator after the last
2493694Sroot  * name (before the first field) stops us.
2503694Sroot  */
2513805Ssam tnamatch(np)
2523694Sroot 	char *np;
2533694Sroot {
2543694Sroot 	register char *Np, *Bp;
2553694Sroot 
2563805Ssam 	Bp = tbuf;
2573805Ssam 	if (*Bp == '#')
2583805Ssam 		return(0);
2593694Sroot 	for (;;) {
2603694Sroot 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2613694Sroot 			continue;
2623694Sroot 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2633694Sroot 			return (1);
2643694Sroot 		while (*Bp && *Bp != ':' && *Bp != '|')
2653694Sroot 			Bp++;
2663694Sroot 		if (*Bp == 0 || *Bp == ':')
2673694Sroot 			return (0);
2683694Sroot 		Bp++;
2693694Sroot 	}
2703694Sroot }
2713694Sroot 
2723694Sroot /*
2733694Sroot  * Skip to the next field.  Notice that this is very dumb, not
2743694Sroot  * knowing about \: escapes or any such.  If necessary, :'s can be put
2753805Ssam  * into the termcap file in octal.
2763694Sroot  */
2773694Sroot static char *
2783805Ssam tskip(bp)
2793694Sroot 	register char *bp;
2803694Sroot {
2813694Sroot 
2823694Sroot 	while (*bp && *bp != ':')
2833694Sroot 		bp++;
2843694Sroot 	if (*bp == ':')
2853694Sroot 		bp++;
2863694Sroot 	return (bp);
2873694Sroot }
2883694Sroot 
2893694Sroot /*
2903694Sroot  * Return the (numeric) option id.
2913694Sroot  * Numeric options look like
2923694Sroot  *	li#80
2933694Sroot  * i.e. the option string is separated from the numeric value by
2943694Sroot  * a # character.  If the option is not found we return -1.
2953694Sroot  * Note that we handle octal numbers beginning with 0.
2963694Sroot  */
2973805Ssam tgetnum(id)
2983694Sroot 	char *id;
2993694Sroot {
3003694Sroot 	register int i, base;
3013805Ssam 	register char *bp = tbuf;
3023694Sroot 
3033694Sroot 	for (;;) {
3043805Ssam 		bp = tskip(bp);
3053694Sroot 		if (*bp == 0)
3063694Sroot 			return (-1);
3073694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3083694Sroot 			continue;
3093805Ssam 		if (*bp == '@')
3103805Ssam 			return(-1);
3113694Sroot 		if (*bp != '#')
3123694Sroot 			continue;
3133694Sroot 		bp++;
3143694Sroot 		base = 10;
3153694Sroot 		if (*bp == '0')
3163694Sroot 			base = 8;
3173694Sroot 		i = 0;
3183694Sroot 		while (isdigit(*bp))
3193694Sroot 			i *= base, i += *bp++ - '0';
3203694Sroot 		return (i);
3213694Sroot 	}
3223694Sroot }
3233694Sroot 
3243694Sroot /*
3253694Sroot  * Handle a flag option.
3263694Sroot  * Flag options are given "naked", i.e. followed by a : or the end
3273694Sroot  * of the buffer.  Return 1 if we find the option, or 0 if it is
3283694Sroot  * not given.
3293694Sroot  */
3303805Ssam tgetflag(id)
3313694Sroot 	char *id;
3323694Sroot {
3333805Ssam 	register char *bp = tbuf;
3343694Sroot 
3353694Sroot 	for (;;) {
3363805Ssam 		bp = tskip(bp);
3373694Sroot 		if (!*bp)
3383694Sroot 			return (0);
3393805Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3403805Ssam 			if (!*bp || *bp == ':')
3413805Ssam 				return (1);
3423805Ssam 			else if (*bp == '@')
3433805Ssam 				return(0);
3443805Ssam 		}
3453694Sroot 	}
3463694Sroot }
3473694Sroot 
3483694Sroot /*
3493694Sroot  * Get a string valued option.
3503694Sroot  * These are given as
3513694Sroot  *	cl=^Z
3523694Sroot  * Much decoding is done on the strings, and the strings are
3533694Sroot  * placed in area, which is a ref parameter which is updated.
3543694Sroot  * No checking on area overflow.
3553694Sroot  */
3563694Sroot char *
3573805Ssam tgetstr(id, area)
3583694Sroot 	char *id, **area;
3593694Sroot {
3603805Ssam 	register char *bp = tbuf;
3613694Sroot 
3623694Sroot 	for (;;) {
3633805Ssam 		bp = tskip(bp);
3643694Sroot 		if (!*bp)
3653694Sroot 			return (0);
3663694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3673694Sroot 			continue;
3683805Ssam 		if (*bp == '@')
3693805Ssam 			return(0);
3703694Sroot 		if (*bp != '=')
3713694Sroot 			continue;
3723694Sroot 		bp++;
3733805Ssam 		return (tdecode(bp, area));
3743694Sroot 	}
3753694Sroot }
3763694Sroot 
3773694Sroot /*
3783694Sroot  * Tdecode does the grung work to decode the
3793694Sroot  * string capability escapes.
3803694Sroot  */
3813805Ssam static char *
3823805Ssam tdecode(str, area)
3833694Sroot 	register char *str;
3843694Sroot 	char **area;
3853694Sroot {
3863694Sroot 	register char *cp;
3873694Sroot 	register int c;
3883694Sroot 	register char *dp;
3893694Sroot 	int i;
3903694Sroot 
3913694Sroot 	cp = *area;
3923694Sroot 	while ((c = *str++) && c != ':') {
3933694Sroot 		switch (c) {
3943694Sroot 
3953694Sroot 		case '^':
3963694Sroot 			c = *str++ & 037;
3973694Sroot 			break;
3983694Sroot 
3993694Sroot 		case '\\':
4003694Sroot 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
4013694Sroot 			c = *str++;
4023694Sroot nextc:
4033694Sroot 			if (*dp++ == c) {
4043694Sroot 				c = *dp++;
4053694Sroot 				break;
4063694Sroot 			}
4073694Sroot 			dp++;
4083694Sroot 			if (*dp)
4093694Sroot 				goto nextc;
4103694Sroot 			if (isdigit(c)) {
4113694Sroot 				c -= '0', i = 2;
4123694Sroot 				do
4133694Sroot 					c <<= 3, c |= *str++ - '0';
4143694Sroot 				while (--i && isdigit(*str));
4153694Sroot 			}
4163694Sroot 			break;
4173694Sroot 		}
4183694Sroot 		*cp++ = c;
4193694Sroot 	}
4203694Sroot 	*cp++ = 0;
4213694Sroot 	str = *area;
4223694Sroot 	*area = cp;
4233694Sroot 	return (str);
4243694Sroot }
425