xref: /csrg-svn/usr.bin/tip/remcap.c (revision 19808)
1*19808Sdist /*
2*19808Sdist  * Copyright (c) 1983 Regents of the University of California.
3*19808Sdist  * All rights reserved.  The Berkeley software License Agreement
4*19808Sdist  * specifies the terms and conditions for redistribution.
5*19808Sdist  */
6*19808Sdist 
713278Ssam #ifndef lint
8*19808Sdist static char sccsid[] = "@(#)remcap.c	5.1 (Berkeley) 04/30/85";
9*19808Sdist #endif not lint
103694Sroot 
113694Sroot /*
123805Ssam  * remcap - routines for dealing with the remote host data base
133694Sroot  *
1413278Ssam  * derived from termcap
153805Ssam  */
1613278Ssam #include <sys/file.h>
1713278Ssam #include <ctype.h>
183805Ssam 
1913278Ssam #ifndef BUFSIZ
2013278Ssam #define	BUFSIZ		1024
2113278Ssam #endif
2213278Ssam #define MAXHOP		32		/* max number of tc= indirections */
2313278Ssam #define SYSREMOTE	"/etc/remote"	/* system remote file */
2413278Ssam 
253805Ssam #define	tgetent		rgetent
263805Ssam #define	tnchktc		rnchktc
273805Ssam #define	tnamatch	rnamatch
283805Ssam #define	tgetnum		rgetnum
293805Ssam #define	tgetflag	rgetflag
303805Ssam #define	tgetstr		rgetstr
3113138Sralph #define	E_TERMCAP	RM = SYSREMOTE
323808Ssam #define V_TERMCAP	"REMOTE"
333808Ssam #define V_TERM		"HOST"
343808Ssam 
353805Ssam char	*RM;
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();
5713278Ssam static	char *remotefile;
583694Sroot 
593694Sroot /*
603805Ssam  * Get an entry for terminal name in buffer bp,
613805Ssam  * from the termcap file.  Parse is very rudimentary;
623694Sroot  * we just notice escaped newlines.
633694Sroot  */
643805Ssam tgetent(bp, name)
653694Sroot 	char *bp, *name;
663694Sroot {
6713278Ssam 	char lbuf[BUFSIZ], *cp, *p;
6813278Ssam 	int rc1, rc2;
6913138Sralph 
7013138Sralph 	remotefile = cp = getenv(V_TERMCAP);
7113138Sralph 	if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) {
7213138Sralph 		remotefile = cp = SYSREMOTE;
7313278Ssam 		return (getent(bp, name, cp));
7413138Sralph 	} else {
7513138Sralph 		if ((rc1 = getent(bp, name, cp)) != 1)
7613138Sralph 			*bp = '\0';
7713138Sralph 		remotefile = cp = SYSREMOTE;
7813138Sralph 		rc2 = getent(lbuf, name, cp);
7913138Sralph 		if (rc1 != 1 && rc2 != 1)
8013278Ssam 			return (rc2);
8113138Sralph 		if (rc2 == 1) {
8213138Sralph 			p = lbuf;
8313138Sralph 			if (rc1 == 1)
8413138Sralph 				while (*p++ != ':')
8513138Sralph 					;
8613138Sralph 			if (strlen(bp) + strlen(p) > BUFSIZ) {
8713138Sralph 				write(2, "Remcap entry too long\n", 23);
8813278Ssam 				return (-1);
8913138Sralph 			}
9013138Sralph 			strcat(bp, p);
9113138Sralph 		}
9213138Sralph 		tbuf = bp;
9313278Ssam 		return (1);
9413138Sralph 	}
9513138Sralph }
9613138Sralph 
9713138Sralph getent(bp, name, cp)
9813138Sralph 	char *bp, *name, *cp;
9913138Sralph {
1003694Sroot 	register int c;
1013694Sroot 	register int i = 0, cnt = 0;
10213278Ssam 	char ibuf[BUFSIZ], *cp2;
1033694Sroot 	int tf;
1043694Sroot 
1053805Ssam 	tbuf = bp;
1063805Ssam 	tf = 0;
1073805Ssam 	/*
1083805Ssam 	 * TERMCAP can have one of two things in it. It can be the
1093805Ssam 	 * name of a file to use instead of /etc/termcap. In this
1103805Ssam 	 * case it better start with a "/". Or it can be an entry to
1113805Ssam 	 * use so we don't have to read the file. In this case it
1123805Ssam 	 * has to already have the newlines crunched out.
1133805Ssam 	 */
1143805Ssam 	if (cp && *cp) {
1153805Ssam 		if (*cp!='/') {
1163808Ssam 			cp2 = getenv(V_TERM);
11713278Ssam 			if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
1183805Ssam 				strcpy(bp,cp);
11913278Ssam 				return (tnchktc());
12013278Ssam 			} else
12113278Ssam 				tf = open(E_TERMCAP, O_RDONLY);
1223805Ssam 		} else
12313278Ssam 			tf = open(RM = cp, O_RDONLY);
1243805Ssam 	}
12513278Ssam 	if (tf == 0)
12613278Ssam 		tf = open(E_TERMCAP, O_RDONLY);
1273805Ssam 	if (tf < 0)
1283694Sroot 		return (-1);
1293694Sroot 	for (;;) {
1303694Sroot 		cp = bp;
1313694Sroot 		for (;;) {
1323694Sroot 			if (i == cnt) {
1333694Sroot 				cnt = read(tf, ibuf, BUFSIZ);
1343694Sroot 				if (cnt <= 0) {
1353694Sroot 					close(tf);
1363694Sroot 					return (0);
1373694Sroot 				}
1383694Sroot 				i = 0;
1393694Sroot 			}
1403694Sroot 			c = ibuf[i++];
1413694Sroot 			if (c == '\n') {
14213138Sralph 				if (cp > bp && cp[-1] == '\\') {
1433694Sroot 					cp--;
1443694Sroot 					continue;
1453694Sroot 				}
1463694Sroot 				break;
1473694Sroot 			}
1483805Ssam 			if (cp >= bp+BUFSIZ) {
14913138Sralph 				write(2,"Remcap entry too long\n", 23);
1503805Ssam 				break;
1513805Ssam 			} else
1523805Ssam 				*cp++ = c;
1533694Sroot 		}
1543694Sroot 		*cp = 0;
1553694Sroot 
1563694Sroot 		/*
1573694Sroot 		 * The real work for the match.
1583694Sroot 		 */
1593805Ssam 		if (tnamatch(name)) {
1603694Sroot 			close(tf);
16113278Ssam 			return (tnchktc());
1623694Sroot 		}
1633694Sroot 	}
1643694Sroot }
1653694Sroot 
1663694Sroot /*
1673805Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1683805Ssam  * recursively find xxx and append that entry (minus the names)
1693805Ssam  * to take the place of the tc=xxx entry. This allows termcap
1703805Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
1713805Ssam  * Note that this works because of the left to right scan.
1723805Ssam  */
1733805Ssam tnchktc()
1743805Ssam {
1753805Ssam 	register char *p, *q;
1763805Ssam 	char tcname[16];	/* name of similar terminal */
1773805Ssam 	char tcbuf[BUFSIZ];
1783805Ssam 	char *holdtbuf = tbuf;
1793805Ssam 	int l;
18013138Sralph 	char *cp;
1813805Ssam 
1823805Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
1833805Ssam 	while (*--p != ':')
1843805Ssam 		if (p<tbuf) {
18513138Sralph 			write(2, "Bad remcap entry\n", 18);
1863805Ssam 			return (0);
1873805Ssam 		}
1883805Ssam 	p++;
1893805Ssam 	/* p now points to beginning of last field */
1903805Ssam 	if (p[0] != 't' || p[1] != 'c')
19113278Ssam 		return (1);
19213138Sralph 	strcpy(tcname, p+3);
1933805Ssam 	q = tcname;
19413138Sralph 	while (*q && *q != ':')
1953805Ssam 		q++;
1963805Ssam 	*q = 0;
1973805Ssam 	if (++hopcount > MAXHOP) {
1983805Ssam 		write(2, "Infinite tc= loop\n", 18);
1993805Ssam 		return (0);
2003805Ssam 	}
20113138Sralph 	if (getent(tcbuf, tcname, remotefile) != 1) {
20213138Sralph 		if (strcmp(remotefile, SYSREMOTE) == 0)
20313278Ssam 			return (0);
20413138Sralph 		else if (getent(tcbuf, tcname, SYSREMOTE) != 1)
20513278Ssam 			return (0);
20613138Sralph 	}
20713138Sralph 	for (q = tcbuf; *q++ != ':'; )
2083805Ssam 		;
2093805Ssam 	l = p - holdtbuf + strlen(q);
2103805Ssam 	if (l > BUFSIZ) {
21113138Sralph 		write(2, "Remcap entry too long\n", 23);
21213138Sralph 		q[BUFSIZ - (p-holdtbuf)] = 0;
2133805Ssam 	}
21413138Sralph 	strcpy(p, q);
2153805Ssam 	tbuf = holdtbuf;
21613278Ssam 	return (1);
2173805Ssam }
2183805Ssam 
2193805Ssam /*
2203805Ssam  * Tnamatch deals with name matching.  The first field of the termcap
2213694Sroot  * entry is a sequence of names separated by |'s, so we compare
2223694Sroot  * against each such name.  The normal : terminator after the last
2233694Sroot  * name (before the first field) stops us.
2243694Sroot  */
2253805Ssam tnamatch(np)
2263694Sroot 	char *np;
2273694Sroot {
2283694Sroot 	register char *Np, *Bp;
2293694Sroot 
2303805Ssam 	Bp = tbuf;
2313805Ssam 	if (*Bp == '#')
23213278Ssam 		return (0);
2333694Sroot 	for (;;) {
2343694Sroot 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2353694Sroot 			continue;
2363694Sroot 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2373694Sroot 			return (1);
2383694Sroot 		while (*Bp && *Bp != ':' && *Bp != '|')
2393694Sroot 			Bp++;
2403694Sroot 		if (*Bp == 0 || *Bp == ':')
2413694Sroot 			return (0);
2423694Sroot 		Bp++;
2433694Sroot 	}
2443694Sroot }
2453694Sroot 
2463694Sroot /*
2473694Sroot  * Skip to the next field.  Notice that this is very dumb, not
2483694Sroot  * knowing about \: escapes or any such.  If necessary, :'s can be put
2493805Ssam  * into the termcap file in octal.
2503694Sroot  */
2513694Sroot static char *
2523805Ssam tskip(bp)
2533694Sroot 	register char *bp;
2543694Sroot {
2553694Sroot 
2563694Sroot 	while (*bp && *bp != ':')
2573694Sroot 		bp++;
2583694Sroot 	if (*bp == ':')
2593694Sroot 		bp++;
2603694Sroot 	return (bp);
2613694Sroot }
2623694Sroot 
2633694Sroot /*
2643694Sroot  * Return the (numeric) option id.
2653694Sroot  * Numeric options look like
2663694Sroot  *	li#80
2673694Sroot  * i.e. the option string is separated from the numeric value by
2683694Sroot  * a # character.  If the option is not found we return -1.
2693694Sroot  * Note that we handle octal numbers beginning with 0.
2703694Sroot  */
2713805Ssam tgetnum(id)
2723694Sroot 	char *id;
2733694Sroot {
2743694Sroot 	register int i, base;
2753805Ssam 	register char *bp = tbuf;
2763694Sroot 
2773694Sroot 	for (;;) {
2783805Ssam 		bp = tskip(bp);
2793694Sroot 		if (*bp == 0)
2803694Sroot 			return (-1);
2813694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2823694Sroot 			continue;
2833805Ssam 		if (*bp == '@')
28413278Ssam 			return (-1);
2853694Sroot 		if (*bp != '#')
2863694Sroot 			continue;
2873694Sroot 		bp++;
2883694Sroot 		base = 10;
2893694Sroot 		if (*bp == '0')
2903694Sroot 			base = 8;
2913694Sroot 		i = 0;
2923694Sroot 		while (isdigit(*bp))
2933694Sroot 			i *= base, i += *bp++ - '0';
2943694Sroot 		return (i);
2953694Sroot 	}
2963694Sroot }
2973694Sroot 
2983694Sroot /*
2993694Sroot  * Handle a flag option.
3003694Sroot  * Flag options are given "naked", i.e. followed by a : or the end
3013694Sroot  * of the buffer.  Return 1 if we find the option, or 0 if it is
3023694Sroot  * not given.
3033694Sroot  */
3043805Ssam tgetflag(id)
3053694Sroot 	char *id;
3063694Sroot {
3073805Ssam 	register char *bp = tbuf;
3083694Sroot 
3093694Sroot 	for (;;) {
3103805Ssam 		bp = tskip(bp);
3113694Sroot 		if (!*bp)
3123694Sroot 			return (0);
3133805Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3143805Ssam 			if (!*bp || *bp == ':')
3153805Ssam 				return (1);
3163805Ssam 			else if (*bp == '@')
31713278Ssam 				return (0);
3183805Ssam 		}
3193694Sroot 	}
3203694Sroot }
3213694Sroot 
3223694Sroot /*
3233694Sroot  * Get a string valued option.
3243694Sroot  * These are given as
3253694Sroot  *	cl=^Z
3263694Sroot  * Much decoding is done on the strings, and the strings are
3273694Sroot  * placed in area, which is a ref parameter which is updated.
3283694Sroot  * No checking on area overflow.
3293694Sroot  */
3303694Sroot char *
3313805Ssam tgetstr(id, area)
3323694Sroot 	char *id, **area;
3333694Sroot {
3343805Ssam 	register char *bp = tbuf;
3353694Sroot 
3363694Sroot 	for (;;) {
3373805Ssam 		bp = tskip(bp);
3383694Sroot 		if (!*bp)
3393694Sroot 			return (0);
3403694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3413694Sroot 			continue;
3423805Ssam 		if (*bp == '@')
34313278Ssam 			return (0);
3443694Sroot 		if (*bp != '=')
3453694Sroot 			continue;
3463694Sroot 		bp++;
3473805Ssam 		return (tdecode(bp, area));
3483694Sroot 	}
3493694Sroot }
3503694Sroot 
3513694Sroot /*
3523694Sroot  * Tdecode does the grung work to decode the
3533694Sroot  * string capability escapes.
3543694Sroot  */
3553805Ssam static char *
3563805Ssam tdecode(str, area)
3573694Sroot 	register char *str;
3583694Sroot 	char **area;
3593694Sroot {
3603694Sroot 	register char *cp;
3613694Sroot 	register int c;
3623694Sroot 	register char *dp;
3633694Sroot 	int i;
3643694Sroot 
3653694Sroot 	cp = *area;
3663694Sroot 	while ((c = *str++) && c != ':') {
3673694Sroot 		switch (c) {
3683694Sroot 
3693694Sroot 		case '^':
3703694Sroot 			c = *str++ & 037;
3713694Sroot 			break;
3723694Sroot 
3733694Sroot 		case '\\':
3743694Sroot 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
3753694Sroot 			c = *str++;
3763694Sroot nextc:
3773694Sroot 			if (*dp++ == c) {
3783694Sroot 				c = *dp++;
3793694Sroot 				break;
3803694Sroot 			}
3813694Sroot 			dp++;
3823694Sroot 			if (*dp)
3833694Sroot 				goto nextc;
3843694Sroot 			if (isdigit(c)) {
3853694Sroot 				c -= '0', i = 2;
3863694Sroot 				do
3873694Sroot 					c <<= 3, c |= *str++ - '0';
3883694Sroot 				while (--i && isdigit(*str));
3893694Sroot 			}
3903694Sroot 			break;
3913694Sroot 		}
3923694Sroot 		*cp++ = c;
3933694Sroot 	}
3943694Sroot 	*cp++ = 0;
3953694Sroot 	str = *area;
3963694Sroot 	*area = cp;
3973694Sroot 	return (str);
3983694Sroot }
399