xref: /csrg-svn/usr.bin/tip/remcap.c (revision 4012)
1*4012Ssam /*	remcap.c	4.5	81/07/13	*/
23694Sroot /* Copyright (c) 1979 Regents of the University of California */
34003Ssam #ifndef BUFSIZ
43805Ssam #define	BUFSIZ	1024
54003Ssam #endif
63805Ssam #define MAXHOP	32	/* max number of tc= indirections */
73694Sroot 
83694Sroot #include <ctype.h>
9*4012Ssam #ifdef VMUNIX
103805Ssam #include "local/uparm.h"
11*4012Ssam #endif
123694Sroot /*
133805Ssam  * remcap - routines for dealing with the remote host data base
143694Sroot  *
153805Ssam  *	Made from termcap with the following defines.
163805Ssam  */
173805Ssam #define REMOTE		/* special for tip */
183805Ssam 
193805Ssam #ifdef REMOTE
203805Ssam #define	tgetent		rgetent
213805Ssam #define	tnchktc		rnchktc
223805Ssam #define	tnamatch	rnamatch
233805Ssam #define	tgetnum		rgetnum
243805Ssam #define	tgetflag	rgetflag
253805Ssam #define	tgetstr		rgetstr
263805Ssam #undef	E_TERMCAP
273808Ssam #define	E_TERMCAP	RM = "/etc/remote"
283808Ssam #define V_TERMCAP	"REMOTE"
293808Ssam #define V_TERM		"HOST"
303808Ssam 
313805Ssam char	*RM;
323808Ssam #else
333808Ssam #define	V_TERMCAP	"TERMCAP"
343808Ssam #define V_TERM		"TERM"
353805Ssam #endif
363805Ssam 
373805Ssam /*
383805Ssam  * termcap - routines for dealing with the terminal capability data base
393805Ssam  *
403694Sroot  * BUG:		Should use a "last" pointer in tbuf, so that searching
413694Sroot  *		for capabilities alphabetically would not be a n**2/2
423694Sroot  *		process when large numbers of capabilities are given.
433805Ssam  * Note:	If we add a last pointer now we will screw up the
443805Ssam  *		tc capability. We really should compile termcap.
453694Sroot  *
463694Sroot  * Essentially all the work here is scanning and decoding escapes
473805Ssam  * in string capabilities.  We don't use stdio because the editor
483805Ssam  * doesn't, and because living w/o it is not hard.
493805Ssam  */
503694Sroot 
513805Ssam static	char *tbuf;
523805Ssam static	int hopcount;	/* detect infinite loops in termcap, init 0 */
533805Ssam char	*tskip();
543805Ssam char	*tgetstr();
553805Ssam char	*tdecode();
563694Sroot char	*getenv();
573694Sroot 
583694Sroot /*
593805Ssam  * Get an entry for terminal name in buffer bp,
603805Ssam  * from the termcap file.  Parse is very rudimentary;
613694Sroot  * we just notice escaped newlines.
623694Sroot  */
633805Ssam tgetent(bp, name)
643694Sroot 	char *bp, *name;
653694Sroot {
663694Sroot 	register char *cp;
673694Sroot 	register int c;
683694Sroot 	register int i = 0, cnt = 0;
693694Sroot 	char ibuf[BUFSIZ];
703805Ssam 	char *cp2;
713694Sroot 	int tf;
723694Sroot 
733805Ssam 	tbuf = bp;
743805Ssam 	tf = 0;
753805Ssam #ifndef V6
763808Ssam 	cp = getenv(V_TERMCAP);
773805Ssam 	/*
783805Ssam 	 * TERMCAP can have one of two things in it. It can be the
793805Ssam 	 * name of a file to use instead of /etc/termcap. In this
803805Ssam 	 * case it better start with a "/". Or it can be an entry to
813805Ssam 	 * use so we don't have to read the file. In this case it
823805Ssam 	 * has to already have the newlines crunched out.
833805Ssam 	 */
843805Ssam 	if (cp && *cp) {
853805Ssam 		if (*cp!='/') {
863808Ssam 			cp2 = getenv(V_TERM);
873805Ssam 			if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
883805Ssam 				strcpy(bp,cp);
893805Ssam 				return(tnchktc());
903805Ssam 			} else {
913805Ssam 				tf = open(E_TERMCAP, 0);
923805Ssam 			}
933805Ssam 		} else
943808Ssam #ifdef REMOTE
953808Ssam 			tf = open(RM = cp, 0);
963808Ssam #else
973805Ssam 			tf = open(cp, 0);
983808Ssam #endif
993805Ssam 	}
1003805Ssam 	if (tf==0)
1013805Ssam 		tf = open(E_TERMCAP, 0);
1023805Ssam #else
1033805Ssam 	tf = open(E_TERMCAP, 0);
1043805Ssam #endif
1053805Ssam 	if (tf < 0)
1063694Sroot 		return (-1);
1073694Sroot 	for (;;) {
1083694Sroot 		cp = bp;
1093694Sroot 		for (;;) {
1103694Sroot 			if (i == cnt) {
1113694Sroot 				cnt = read(tf, ibuf, BUFSIZ);
1123694Sroot 				if (cnt <= 0) {
1133694Sroot 					close(tf);
1143694Sroot 					return (0);
1153694Sroot 				}
1163694Sroot 				i = 0;
1173694Sroot 			}
1183694Sroot 			c = ibuf[i++];
1193694Sroot 			if (c == '\n') {
1203694Sroot 				if (cp > bp && cp[-1] == '\\'){
1213694Sroot 					cp--;
1223694Sroot 					continue;
1233694Sroot 				}
1243694Sroot 				break;
1253694Sroot 			}
1263805Ssam 			if (cp >= bp+BUFSIZ) {
1273805Ssam 				write(2,"Termcap entry too long\n", 23);
1283805Ssam 				break;
1293805Ssam 			} else
1303805Ssam 				*cp++ = c;
1313694Sroot 		}
1323694Sroot 		*cp = 0;
1333694Sroot 
1343694Sroot 		/*
1353694Sroot 		 * The real work for the match.
1363694Sroot 		 */
1373805Ssam 		if (tnamatch(name)) {
1383694Sroot 			close(tf);
1393805Ssam 			return(tnchktc());
1403694Sroot 		}
1413694Sroot 	}
1423694Sroot }
1433694Sroot 
1443694Sroot /*
1453805Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1463805Ssam  * recursively find xxx and append that entry (minus the names)
1473805Ssam  * to take the place of the tc=xxx entry. This allows termcap
1483805Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
1493805Ssam  * Note that this works because of the left to right scan.
1503805Ssam  */
1513805Ssam tnchktc()
1523805Ssam {
1533805Ssam 	register char *p, *q;
1543805Ssam 	char tcname[16];	/* name of similar terminal */
1553805Ssam 	char tcbuf[BUFSIZ];
1563805Ssam 	char *holdtbuf = tbuf;
1573805Ssam 	int l;
1583805Ssam 
1593805Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
1603805Ssam 	while (*--p != ':')
1613805Ssam 		if (p<tbuf) {
1623805Ssam 			write(2, "Bad termcap entry\n", 18);
1633805Ssam 			return (0);
1643805Ssam 		}
1653805Ssam 	p++;
1663805Ssam 	/* p now points to beginning of last field */
1673805Ssam 	if (p[0] != 't' || p[1] != 'c')
1683805Ssam 		return(1);
1693805Ssam 	strcpy(tcname,p+3);
1703805Ssam 	q = tcname;
1713805Ssam 	while (q && *q != ':')
1723805Ssam 		q++;
1733805Ssam 	*q = 0;
1743805Ssam 	if (++hopcount > MAXHOP) {
1753805Ssam 		write(2, "Infinite tc= loop\n", 18);
1763805Ssam 		return (0);
1773805Ssam 	}
1783805Ssam 	if (tgetent(tcbuf, tcname) != 1)
1793805Ssam 		return(0);
1803805Ssam 	for (q=tcbuf; *q != ':'; q++)
1813805Ssam 		;
1823805Ssam 	l = p - holdtbuf + strlen(q);
1833805Ssam 	if (l > BUFSIZ) {
1843805Ssam 		write(2, "Termcap entry too long\n", 23);
1853805Ssam 		q[BUFSIZ - (p-tbuf)] = 0;
1863805Ssam 	}
1873805Ssam 	strcpy(p, q+1);
1883805Ssam 	tbuf = holdtbuf;
1893805Ssam 	return(1);
1903805Ssam }
1913805Ssam 
1923805Ssam /*
1933805Ssam  * Tnamatch deals with name matching.  The first field of the termcap
1943694Sroot  * entry is a sequence of names separated by |'s, so we compare
1953694Sroot  * against each such name.  The normal : terminator after the last
1963694Sroot  * name (before the first field) stops us.
1973694Sroot  */
1983805Ssam tnamatch(np)
1993694Sroot 	char *np;
2003694Sroot {
2013694Sroot 	register char *Np, *Bp;
2023694Sroot 
2033805Ssam 	Bp = tbuf;
2043805Ssam 	if (*Bp == '#')
2053805Ssam 		return(0);
2063694Sroot 	for (;;) {
2073694Sroot 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2083694Sroot 			continue;
2093694Sroot 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2103694Sroot 			return (1);
2113694Sroot 		while (*Bp && *Bp != ':' && *Bp != '|')
2123694Sroot 			Bp++;
2133694Sroot 		if (*Bp == 0 || *Bp == ':')
2143694Sroot 			return (0);
2153694Sroot 		Bp++;
2163694Sroot 	}
2173694Sroot }
2183694Sroot 
2193694Sroot /*
2203694Sroot  * Skip to the next field.  Notice that this is very dumb, not
2213694Sroot  * knowing about \: escapes or any such.  If necessary, :'s can be put
2223805Ssam  * into the termcap file in octal.
2233694Sroot  */
2243694Sroot static char *
2253805Ssam tskip(bp)
2263694Sroot 	register char *bp;
2273694Sroot {
2283694Sroot 
2293694Sroot 	while (*bp && *bp != ':')
2303694Sroot 		bp++;
2313694Sroot 	if (*bp == ':')
2323694Sroot 		bp++;
2333694Sroot 	return (bp);
2343694Sroot }
2353694Sroot 
2363694Sroot /*
2373694Sroot  * Return the (numeric) option id.
2383694Sroot  * Numeric options look like
2393694Sroot  *	li#80
2403694Sroot  * i.e. the option string is separated from the numeric value by
2413694Sroot  * a # character.  If the option is not found we return -1.
2423694Sroot  * Note that we handle octal numbers beginning with 0.
2433694Sroot  */
2443805Ssam tgetnum(id)
2453694Sroot 	char *id;
2463694Sroot {
2473694Sroot 	register int i, base;
2483805Ssam 	register char *bp = tbuf;
2493694Sroot 
2503694Sroot 	for (;;) {
2513805Ssam 		bp = tskip(bp);
2523694Sroot 		if (*bp == 0)
2533694Sroot 			return (-1);
2543694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2553694Sroot 			continue;
2563805Ssam 		if (*bp == '@')
2573805Ssam 			return(-1);
2583694Sroot 		if (*bp != '#')
2593694Sroot 			continue;
2603694Sroot 		bp++;
2613694Sroot 		base = 10;
2623694Sroot 		if (*bp == '0')
2633694Sroot 			base = 8;
2643694Sroot 		i = 0;
2653694Sroot 		while (isdigit(*bp))
2663694Sroot 			i *= base, i += *bp++ - '0';
2673694Sroot 		return (i);
2683694Sroot 	}
2693694Sroot }
2703694Sroot 
2713694Sroot /*
2723694Sroot  * Handle a flag option.
2733694Sroot  * Flag options are given "naked", i.e. followed by a : or the end
2743694Sroot  * of the buffer.  Return 1 if we find the option, or 0 if it is
2753694Sroot  * not given.
2763694Sroot  */
2773805Ssam tgetflag(id)
2783694Sroot 	char *id;
2793694Sroot {
2803805Ssam 	register char *bp = tbuf;
2813694Sroot 
2823694Sroot 	for (;;) {
2833805Ssam 		bp = tskip(bp);
2843694Sroot 		if (!*bp)
2853694Sroot 			return (0);
2863805Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
2873805Ssam 			if (!*bp || *bp == ':')
2883805Ssam 				return (1);
2893805Ssam 			else if (*bp == '@')
2903805Ssam 				return(0);
2913805Ssam 		}
2923694Sroot 	}
2933694Sroot }
2943694Sroot 
2953694Sroot /*
2963694Sroot  * Get a string valued option.
2973694Sroot  * These are given as
2983694Sroot  *	cl=^Z
2993694Sroot  * Much decoding is done on the strings, and the strings are
3003694Sroot  * placed in area, which is a ref parameter which is updated.
3013694Sroot  * No checking on area overflow.
3023694Sroot  */
3033694Sroot char *
3043805Ssam tgetstr(id, area)
3053694Sroot 	char *id, **area;
3063694Sroot {
3073805Ssam 	register char *bp = tbuf;
3083694Sroot 
3093694Sroot 	for (;;) {
3103805Ssam 		bp = tskip(bp);
3113694Sroot 		if (!*bp)
3123694Sroot 			return (0);
3133694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3143694Sroot 			continue;
3153805Ssam 		if (*bp == '@')
3163805Ssam 			return(0);
3173694Sroot 		if (*bp != '=')
3183694Sroot 			continue;
3193694Sroot 		bp++;
3203805Ssam 		return (tdecode(bp, area));
3213694Sroot 	}
3223694Sroot }
3233694Sroot 
3243694Sroot /*
3253694Sroot  * Tdecode does the grung work to decode the
3263694Sroot  * string capability escapes.
3273694Sroot  */
3283805Ssam static char *
3293805Ssam tdecode(str, area)
3303694Sroot 	register char *str;
3313694Sroot 	char **area;
3323694Sroot {
3333694Sroot 	register char *cp;
3343694Sroot 	register int c;
3353694Sroot 	register char *dp;
3363694Sroot 	int i;
3373694Sroot 
3383694Sroot 	cp = *area;
3393694Sroot 	while ((c = *str++) && c != ':') {
3403694Sroot 		switch (c) {
3413694Sroot 
3423694Sroot 		case '^':
3433694Sroot 			c = *str++ & 037;
3443694Sroot 			break;
3453694Sroot 
3463694Sroot 		case '\\':
3473694Sroot 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
3483694Sroot 			c = *str++;
3493694Sroot nextc:
3503694Sroot 			if (*dp++ == c) {
3513694Sroot 				c = *dp++;
3523694Sroot 				break;
3533694Sroot 			}
3543694Sroot 			dp++;
3553694Sroot 			if (*dp)
3563694Sroot 				goto nextc;
3573694Sroot 			if (isdigit(c)) {
3583694Sroot 				c -= '0', i = 2;
3593694Sroot 				do
3603694Sroot 					c <<= 3, c |= *str++ - '0';
3613694Sroot 				while (--i && isdigit(*str));
3623694Sroot 			}
3633694Sroot 			break;
3643694Sroot 		}
3653694Sroot 		*cp++ = c;
3663694Sroot 	}
3673694Sroot 	*cp++ = 0;
3683694Sroot 	str = *area;
3693694Sroot 	*area = cp;
3703694Sroot 	return (str);
3713694Sroot }
372