xref: /csrg-svn/usr.bin/tip/remcap.c (revision 37857)
119808Sdist /*
235464Sbostic  * Copyright (c) 1983 The Regents of the University of California.
335464Sbostic  * All rights reserved.
435464Sbostic  *
535464Sbostic  * Redistribution and use in source and binary forms are permitted
635464Sbostic  * provided that the above copyright notice and this paragraph are
735464Sbostic  * duplicated in all such forms and that any documentation,
835464Sbostic  * advertising materials, and other materials related to such
935464Sbostic  * distribution and use acknowledge that the software was developed
1035464Sbostic  * by the University of California, Berkeley.  The name of the
1135464Sbostic  * University may not be used to endorse or promote products derived
1235464Sbostic  * from this software without specific prior written permission.
1335464Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1435464Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1535464Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1619808Sdist  */
1719808Sdist 
1813278Ssam #ifndef lint
19*37857Sbostic static char sccsid[] = "@(#)remcap.c	5.3 (Berkeley) 05/11/89";
2035464Sbostic #endif /* not lint */
213694Sroot 
223694Sroot /*
233805Ssam  * remcap - routines for dealing with the remote host data base
243694Sroot  *
2513278Ssam  * derived from termcap
263805Ssam  */
2713278Ssam #include <sys/file.h>
2813278Ssam #include <ctype.h>
29*37857Sbostic #include "pathnames.h"
303805Ssam 
3113278Ssam #ifndef BUFSIZ
3213278Ssam #define	BUFSIZ		1024
3313278Ssam #endif
3413278Ssam #define MAXHOP		32		/* max number of tc= indirections */
3513278Ssam 
363805Ssam #define	tgetent		rgetent
373805Ssam #define	tnchktc		rnchktc
383805Ssam #define	tnamatch	rnamatch
393805Ssam #define	tgetnum		rgetnum
403805Ssam #define	tgetflag	rgetflag
413805Ssam #define	tgetstr		rgetstr
42*37857Sbostic #define	E_TERMCAP	RM = _PATH_REMOTE
433808Ssam #define V_TERMCAP	"REMOTE"
443808Ssam #define V_TERM		"HOST"
453808Ssam 
463805Ssam char	*RM;
473805Ssam 
483805Ssam /*
493805Ssam  * termcap - routines for dealing with the terminal capability data base
503805Ssam  *
513694Sroot  * BUG:		Should use a "last" pointer in tbuf, so that searching
523694Sroot  *		for capabilities alphabetically would not be a n**2/2
533694Sroot  *		process when large numbers of capabilities are given.
543805Ssam  * Note:	If we add a last pointer now we will screw up the
553805Ssam  *		tc capability. We really should compile termcap.
563694Sroot  *
573694Sroot  * Essentially all the work here is scanning and decoding escapes
583805Ssam  * in string capabilities.  We don't use stdio because the editor
593805Ssam  * doesn't, and because living w/o it is not hard.
603805Ssam  */
613694Sroot 
623805Ssam static	char *tbuf;
633805Ssam static	int hopcount;	/* detect infinite loops in termcap, init 0 */
643805Ssam char	*tskip();
653805Ssam char	*tgetstr();
663805Ssam char	*tdecode();
673694Sroot char	*getenv();
6813278Ssam static	char *remotefile;
693694Sroot 
703694Sroot /*
713805Ssam  * Get an entry for terminal name in buffer bp,
723805Ssam  * from the termcap file.  Parse is very rudimentary;
733694Sroot  * we just notice escaped newlines.
743694Sroot  */
753805Ssam tgetent(bp, name)
763694Sroot 	char *bp, *name;
773694Sroot {
7813278Ssam 	char lbuf[BUFSIZ], *cp, *p;
7913278Ssam 	int rc1, rc2;
8013138Sralph 
8113138Sralph 	remotefile = cp = getenv(V_TERMCAP);
82*37857Sbostic 	if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) {
83*37857Sbostic 		remotefile = cp = _PATH_REMOTE;
8413278Ssam 		return (getent(bp, name, cp));
8513138Sralph 	} else {
8613138Sralph 		if ((rc1 = getent(bp, name, cp)) != 1)
8713138Sralph 			*bp = '\0';
88*37857Sbostic 		remotefile = cp = _PATH_REMOTE;
8913138Sralph 		rc2 = getent(lbuf, name, cp);
9013138Sralph 		if (rc1 != 1 && rc2 != 1)
9113278Ssam 			return (rc2);
9213138Sralph 		if (rc2 == 1) {
9313138Sralph 			p = lbuf;
9413138Sralph 			if (rc1 == 1)
9513138Sralph 				while (*p++ != ':')
9613138Sralph 					;
9713138Sralph 			if (strlen(bp) + strlen(p) > BUFSIZ) {
9813138Sralph 				write(2, "Remcap entry too long\n", 23);
9913278Ssam 				return (-1);
10013138Sralph 			}
10113138Sralph 			strcat(bp, p);
10213138Sralph 		}
10313138Sralph 		tbuf = bp;
10413278Ssam 		return (1);
10513138Sralph 	}
10613138Sralph }
10713138Sralph 
10813138Sralph getent(bp, name, cp)
10913138Sralph 	char *bp, *name, *cp;
11013138Sralph {
1113694Sroot 	register int c;
1123694Sroot 	register int i = 0, cnt = 0;
11313278Ssam 	char ibuf[BUFSIZ], *cp2;
1143694Sroot 	int tf;
1153694Sroot 
1163805Ssam 	tbuf = bp;
1173805Ssam 	tf = 0;
1183805Ssam 	/*
1193805Ssam 	 * TERMCAP can have one of two things in it. It can be the
1203805Ssam 	 * name of a file to use instead of /etc/termcap. In this
1213805Ssam 	 * case it better start with a "/". Or it can be an entry to
1223805Ssam 	 * use so we don't have to read the file. In this case it
1233805Ssam 	 * has to already have the newlines crunched out.
1243805Ssam 	 */
1253805Ssam 	if (cp && *cp) {
1263805Ssam 		if (*cp!='/') {
1273808Ssam 			cp2 = getenv(V_TERM);
12813278Ssam 			if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
1293805Ssam 				strcpy(bp,cp);
13013278Ssam 				return (tnchktc());
13113278Ssam 			} else
13213278Ssam 				tf = open(E_TERMCAP, O_RDONLY);
1333805Ssam 		} else
13413278Ssam 			tf = open(RM = cp, O_RDONLY);
1353805Ssam 	}
13613278Ssam 	if (tf == 0)
13713278Ssam 		tf = open(E_TERMCAP, O_RDONLY);
1383805Ssam 	if (tf < 0)
1393694Sroot 		return (-1);
1403694Sroot 	for (;;) {
1413694Sroot 		cp = bp;
1423694Sroot 		for (;;) {
1433694Sroot 			if (i == cnt) {
1443694Sroot 				cnt = read(tf, ibuf, BUFSIZ);
1453694Sroot 				if (cnt <= 0) {
1463694Sroot 					close(tf);
1473694Sroot 					return (0);
1483694Sroot 				}
1493694Sroot 				i = 0;
1503694Sroot 			}
1513694Sroot 			c = ibuf[i++];
1523694Sroot 			if (c == '\n') {
15313138Sralph 				if (cp > bp && cp[-1] == '\\') {
1543694Sroot 					cp--;
1553694Sroot 					continue;
1563694Sroot 				}
1573694Sroot 				break;
1583694Sroot 			}
1593805Ssam 			if (cp >= bp+BUFSIZ) {
16013138Sralph 				write(2,"Remcap entry too long\n", 23);
1613805Ssam 				break;
1623805Ssam 			} else
1633805Ssam 				*cp++ = c;
1643694Sroot 		}
1653694Sroot 		*cp = 0;
1663694Sroot 
1673694Sroot 		/*
1683694Sroot 		 * The real work for the match.
1693694Sroot 		 */
1703805Ssam 		if (tnamatch(name)) {
1713694Sroot 			close(tf);
17213278Ssam 			return (tnchktc());
1733694Sroot 		}
1743694Sroot 	}
1753694Sroot }
1763694Sroot 
1773694Sroot /*
1783805Ssam  * tnchktc: check the last entry, see if it's tc=xxx. If so,
1793805Ssam  * recursively find xxx and append that entry (minus the names)
1803805Ssam  * to take the place of the tc=xxx entry. This allows termcap
1813805Ssam  * entries to say "like an HP2621 but doesn't turn on the labels".
1823805Ssam  * Note that this works because of the left to right scan.
1833805Ssam  */
1843805Ssam tnchktc()
1853805Ssam {
1863805Ssam 	register char *p, *q;
1873805Ssam 	char tcname[16];	/* name of similar terminal */
1883805Ssam 	char tcbuf[BUFSIZ];
1893805Ssam 	char *holdtbuf = tbuf;
1903805Ssam 	int l;
19113138Sralph 	char *cp;
1923805Ssam 
1933805Ssam 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
1943805Ssam 	while (*--p != ':')
1953805Ssam 		if (p<tbuf) {
19613138Sralph 			write(2, "Bad remcap entry\n", 18);
1973805Ssam 			return (0);
1983805Ssam 		}
1993805Ssam 	p++;
2003805Ssam 	/* p now points to beginning of last field */
2013805Ssam 	if (p[0] != 't' || p[1] != 'c')
20213278Ssam 		return (1);
20313138Sralph 	strcpy(tcname, p+3);
2043805Ssam 	q = tcname;
20513138Sralph 	while (*q && *q != ':')
2063805Ssam 		q++;
2073805Ssam 	*q = 0;
2083805Ssam 	if (++hopcount > MAXHOP) {
2093805Ssam 		write(2, "Infinite tc= loop\n", 18);
2103805Ssam 		return (0);
2113805Ssam 	}
21213138Sralph 	if (getent(tcbuf, tcname, remotefile) != 1) {
213*37857Sbostic 		if (strcmp(remotefile, _PATH_REMOTE) == 0)
21413278Ssam 			return (0);
215*37857Sbostic 		else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1)
21613278Ssam 			return (0);
21713138Sralph 	}
21813138Sralph 	for (q = tcbuf; *q++ != ':'; )
2193805Ssam 		;
2203805Ssam 	l = p - holdtbuf + strlen(q);
2213805Ssam 	if (l > BUFSIZ) {
22213138Sralph 		write(2, "Remcap entry too long\n", 23);
22313138Sralph 		q[BUFSIZ - (p-holdtbuf)] = 0;
2243805Ssam 	}
22513138Sralph 	strcpy(p, q);
2263805Ssam 	tbuf = holdtbuf;
22713278Ssam 	return (1);
2283805Ssam }
2293805Ssam 
2303805Ssam /*
2313805Ssam  * Tnamatch deals with name matching.  The first field of the termcap
2323694Sroot  * entry is a sequence of names separated by |'s, so we compare
2333694Sroot  * against each such name.  The normal : terminator after the last
2343694Sroot  * name (before the first field) stops us.
2353694Sroot  */
2363805Ssam tnamatch(np)
2373694Sroot 	char *np;
2383694Sroot {
2393694Sroot 	register char *Np, *Bp;
2403694Sroot 
2413805Ssam 	Bp = tbuf;
2423805Ssam 	if (*Bp == '#')
24313278Ssam 		return (0);
2443694Sroot 	for (;;) {
2453694Sroot 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2463694Sroot 			continue;
2473694Sroot 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2483694Sroot 			return (1);
2493694Sroot 		while (*Bp && *Bp != ':' && *Bp != '|')
2503694Sroot 			Bp++;
2513694Sroot 		if (*Bp == 0 || *Bp == ':')
2523694Sroot 			return (0);
2533694Sroot 		Bp++;
2543694Sroot 	}
2553694Sroot }
2563694Sroot 
2573694Sroot /*
2583694Sroot  * Skip to the next field.  Notice that this is very dumb, not
2593694Sroot  * knowing about \: escapes or any such.  If necessary, :'s can be put
2603805Ssam  * into the termcap file in octal.
2613694Sroot  */
2623694Sroot static char *
2633805Ssam tskip(bp)
2643694Sroot 	register char *bp;
2653694Sroot {
2663694Sroot 
2673694Sroot 	while (*bp && *bp != ':')
2683694Sroot 		bp++;
2693694Sroot 	if (*bp == ':')
2703694Sroot 		bp++;
2713694Sroot 	return (bp);
2723694Sroot }
2733694Sroot 
2743694Sroot /*
2753694Sroot  * Return the (numeric) option id.
2763694Sroot  * Numeric options look like
2773694Sroot  *	li#80
2783694Sroot  * i.e. the option string is separated from the numeric value by
2793694Sroot  * a # character.  If the option is not found we return -1.
2803694Sroot  * Note that we handle octal numbers beginning with 0.
2813694Sroot  */
2823805Ssam tgetnum(id)
2833694Sroot 	char *id;
2843694Sroot {
2853694Sroot 	register int i, base;
2863805Ssam 	register char *bp = tbuf;
2873694Sroot 
2883694Sroot 	for (;;) {
2893805Ssam 		bp = tskip(bp);
2903694Sroot 		if (*bp == 0)
2913694Sroot 			return (-1);
2923694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2933694Sroot 			continue;
2943805Ssam 		if (*bp == '@')
29513278Ssam 			return (-1);
2963694Sroot 		if (*bp != '#')
2973694Sroot 			continue;
2983694Sroot 		bp++;
2993694Sroot 		base = 10;
3003694Sroot 		if (*bp == '0')
3013694Sroot 			base = 8;
3023694Sroot 		i = 0;
3033694Sroot 		while (isdigit(*bp))
3043694Sroot 			i *= base, i += *bp++ - '0';
3053694Sroot 		return (i);
3063694Sroot 	}
3073694Sroot }
3083694Sroot 
3093694Sroot /*
3103694Sroot  * Handle a flag option.
3113694Sroot  * Flag options are given "naked", i.e. followed by a : or the end
3123694Sroot  * of the buffer.  Return 1 if we find the option, or 0 if it is
3133694Sroot  * not given.
3143694Sroot  */
3153805Ssam tgetflag(id)
3163694Sroot 	char *id;
3173694Sroot {
3183805Ssam 	register char *bp = tbuf;
3193694Sroot 
3203694Sroot 	for (;;) {
3213805Ssam 		bp = tskip(bp);
3223694Sroot 		if (!*bp)
3233694Sroot 			return (0);
3243805Ssam 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3253805Ssam 			if (!*bp || *bp == ':')
3263805Ssam 				return (1);
3273805Ssam 			else if (*bp == '@')
32813278Ssam 				return (0);
3293805Ssam 		}
3303694Sroot 	}
3313694Sroot }
3323694Sroot 
3333694Sroot /*
3343694Sroot  * Get a string valued option.
3353694Sroot  * These are given as
3363694Sroot  *	cl=^Z
3373694Sroot  * Much decoding is done on the strings, and the strings are
3383694Sroot  * placed in area, which is a ref parameter which is updated.
3393694Sroot  * No checking on area overflow.
3403694Sroot  */
3413694Sroot char *
3423805Ssam tgetstr(id, area)
3433694Sroot 	char *id, **area;
3443694Sroot {
3453805Ssam 	register char *bp = tbuf;
3463694Sroot 
3473694Sroot 	for (;;) {
3483805Ssam 		bp = tskip(bp);
3493694Sroot 		if (!*bp)
3503694Sroot 			return (0);
3513694Sroot 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3523694Sroot 			continue;
3533805Ssam 		if (*bp == '@')
35413278Ssam 			return (0);
3553694Sroot 		if (*bp != '=')
3563694Sroot 			continue;
3573694Sroot 		bp++;
3583805Ssam 		return (tdecode(bp, area));
3593694Sroot 	}
3603694Sroot }
3613694Sroot 
3623694Sroot /*
3633694Sroot  * Tdecode does the grung work to decode the
3643694Sroot  * string capability escapes.
3653694Sroot  */
3663805Ssam static char *
3673805Ssam tdecode(str, area)
3683694Sroot 	register char *str;
3693694Sroot 	char **area;
3703694Sroot {
3713694Sroot 	register char *cp;
3723694Sroot 	register int c;
3733694Sroot 	register char *dp;
3743694Sroot 	int i;
3753694Sroot 
3763694Sroot 	cp = *area;
3773694Sroot 	while ((c = *str++) && c != ':') {
3783694Sroot 		switch (c) {
3793694Sroot 
3803694Sroot 		case '^':
3813694Sroot 			c = *str++ & 037;
3823694Sroot 			break;
3833694Sroot 
3843694Sroot 		case '\\':
3853694Sroot 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
3863694Sroot 			c = *str++;
3873694Sroot nextc:
3883694Sroot 			if (*dp++ == c) {
3893694Sroot 				c = *dp++;
3903694Sroot 				break;
3913694Sroot 			}
3923694Sroot 			dp++;
3933694Sroot 			if (*dp)
3943694Sroot 				goto nextc;
3953694Sroot 			if (isdigit(c)) {
3963694Sroot 				c -= '0', i = 2;
3973694Sroot 				do
3983694Sroot 					c <<= 3, c |= *str++ - '0';
3993694Sroot 				while (--i && isdigit(*str));
4003694Sroot 			}
4013694Sroot 			break;
4023694Sroot 		}
4033694Sroot 		*cp++ = c;
4043694Sroot 	}
4053694Sroot 	*cp++ = 0;
4063694Sroot 	str = *area;
4073694Sroot 	*area = cp;
4083694Sroot 	return (str);
4093694Sroot }
410