xref: /csrg-svn/usr.bin/tip/remcap.c (revision 13278)
1 #ifndef lint
2 static char sccsid[] = "@(#)remcap.c	4.8 (Berkeley) 06/25/83";
3 #endif
4 
5 /*
6  * remcap - routines for dealing with the remote host data base
7  *
8  * derived from termcap
9  */
10 #include <sys/file.h>
11 #include <ctype.h>
12 
13 #ifndef BUFSIZ
14 #define	BUFSIZ		1024
15 #endif
16 #define MAXHOP		32		/* max number of tc= indirections */
17 #define SYSREMOTE	"/etc/remote"	/* system remote file */
18 
19 #define	tgetent		rgetent
20 #define	tnchktc		rnchktc
21 #define	tnamatch	rnamatch
22 #define	tgetnum		rgetnum
23 #define	tgetflag	rgetflag
24 #define	tgetstr		rgetstr
25 #define	E_TERMCAP	RM = SYSREMOTE
26 #define V_TERMCAP	"REMOTE"
27 #define V_TERM		"HOST"
28 
29 char	*RM;
30 
31 /*
32  * termcap - routines for dealing with the terminal capability data base
33  *
34  * BUG:		Should use a "last" pointer in tbuf, so that searching
35  *		for capabilities alphabetically would not be a n**2/2
36  *		process when large numbers of capabilities are given.
37  * Note:	If we add a last pointer now we will screw up the
38  *		tc capability. We really should compile termcap.
39  *
40  * Essentially all the work here is scanning and decoding escapes
41  * in string capabilities.  We don't use stdio because the editor
42  * doesn't, and because living w/o it is not hard.
43  */
44 
45 static	char *tbuf;
46 static	int hopcount;	/* detect infinite loops in termcap, init 0 */
47 char	*tskip();
48 char	*tgetstr();
49 char	*tdecode();
50 char	*getenv();
51 static	char *remotefile;
52 
53 /*
54  * Get an entry for terminal name in buffer bp,
55  * from the termcap file.  Parse is very rudimentary;
56  * we just notice escaped newlines.
57  */
58 tgetent(bp, name)
59 	char *bp, *name;
60 {
61 	char lbuf[BUFSIZ], *cp, *p;
62 	int rc1, rc2;
63 
64 	remotefile = cp = getenv(V_TERMCAP);
65 	if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) {
66 		remotefile = cp = SYSREMOTE;
67 		return (getent(bp, name, cp));
68 	} else {
69 		if ((rc1 = getent(bp, name, cp)) != 1)
70 			*bp = '\0';
71 		remotefile = cp = SYSREMOTE;
72 		rc2 = getent(lbuf, name, cp);
73 		if (rc1 != 1 && rc2 != 1)
74 			return (rc2);
75 		if (rc2 == 1) {
76 			p = lbuf;
77 			if (rc1 == 1)
78 				while (*p++ != ':')
79 					;
80 			if (strlen(bp) + strlen(p) > BUFSIZ) {
81 				write(2, "Remcap entry too long\n", 23);
82 				return (-1);
83 			}
84 			strcat(bp, p);
85 		}
86 		tbuf = bp;
87 		return (1);
88 	}
89 }
90 
91 getent(bp, name, cp)
92 	char *bp, *name, *cp;
93 {
94 	register int c;
95 	register int i = 0, cnt = 0;
96 	char ibuf[BUFSIZ], *cp2;
97 	int tf;
98 
99 	tbuf = bp;
100 	tf = 0;
101 	/*
102 	 * TERMCAP can have one of two things in it. It can be the
103 	 * name of a file to use instead of /etc/termcap. In this
104 	 * case it better start with a "/". Or it can be an entry to
105 	 * use so we don't have to read the file. In this case it
106 	 * has to already have the newlines crunched out.
107 	 */
108 	if (cp && *cp) {
109 		if (*cp!='/') {
110 			cp2 = getenv(V_TERM);
111 			if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
112 				strcpy(bp,cp);
113 				return (tnchktc());
114 			} else
115 				tf = open(E_TERMCAP, O_RDONLY);
116 		} else
117 			tf = open(RM = cp, O_RDONLY);
118 	}
119 	if (tf == 0)
120 		tf = open(E_TERMCAP, O_RDONLY);
121 	if (tf < 0)
122 		return (-1);
123 	for (;;) {
124 		cp = bp;
125 		for (;;) {
126 			if (i == cnt) {
127 				cnt = read(tf, ibuf, BUFSIZ);
128 				if (cnt <= 0) {
129 					close(tf);
130 					return (0);
131 				}
132 				i = 0;
133 			}
134 			c = ibuf[i++];
135 			if (c == '\n') {
136 				if (cp > bp && cp[-1] == '\\') {
137 					cp--;
138 					continue;
139 				}
140 				break;
141 			}
142 			if (cp >= bp+BUFSIZ) {
143 				write(2,"Remcap entry too long\n", 23);
144 				break;
145 			} else
146 				*cp++ = c;
147 		}
148 		*cp = 0;
149 
150 		/*
151 		 * The real work for the match.
152 		 */
153 		if (tnamatch(name)) {
154 			close(tf);
155 			return (tnchktc());
156 		}
157 	}
158 }
159 
160 /*
161  * tnchktc: check the last entry, see if it's tc=xxx. If so,
162  * recursively find xxx and append that entry (minus the names)
163  * to take the place of the tc=xxx entry. This allows termcap
164  * entries to say "like an HP2621 but doesn't turn on the labels".
165  * Note that this works because of the left to right scan.
166  */
167 tnchktc()
168 {
169 	register char *p, *q;
170 	char tcname[16];	/* name of similar terminal */
171 	char tcbuf[BUFSIZ];
172 	char *holdtbuf = tbuf;
173 	int l;
174 	char *cp;
175 
176 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
177 	while (*--p != ':')
178 		if (p<tbuf) {
179 			write(2, "Bad remcap entry\n", 18);
180 			return (0);
181 		}
182 	p++;
183 	/* p now points to beginning of last field */
184 	if (p[0] != 't' || p[1] != 'c')
185 		return (1);
186 	strcpy(tcname, p+3);
187 	q = tcname;
188 	while (*q && *q != ':')
189 		q++;
190 	*q = 0;
191 	if (++hopcount > MAXHOP) {
192 		write(2, "Infinite tc= loop\n", 18);
193 		return (0);
194 	}
195 	if (getent(tcbuf, tcname, remotefile) != 1) {
196 		if (strcmp(remotefile, SYSREMOTE) == 0)
197 			return (0);
198 		else if (getent(tcbuf, tcname, SYSREMOTE) != 1)
199 			return (0);
200 	}
201 	for (q = tcbuf; *q++ != ':'; )
202 		;
203 	l = p - holdtbuf + strlen(q);
204 	if (l > BUFSIZ) {
205 		write(2, "Remcap entry too long\n", 23);
206 		q[BUFSIZ - (p-holdtbuf)] = 0;
207 	}
208 	strcpy(p, q);
209 	tbuf = holdtbuf;
210 	return (1);
211 }
212 
213 /*
214  * Tnamatch deals with name matching.  The first field of the termcap
215  * entry is a sequence of names separated by |'s, so we compare
216  * against each such name.  The normal : terminator after the last
217  * name (before the first field) stops us.
218  */
219 tnamatch(np)
220 	char *np;
221 {
222 	register char *Np, *Bp;
223 
224 	Bp = tbuf;
225 	if (*Bp == '#')
226 		return (0);
227 	for (;;) {
228 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
229 			continue;
230 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
231 			return (1);
232 		while (*Bp && *Bp != ':' && *Bp != '|')
233 			Bp++;
234 		if (*Bp == 0 || *Bp == ':')
235 			return (0);
236 		Bp++;
237 	}
238 }
239 
240 /*
241  * Skip to the next field.  Notice that this is very dumb, not
242  * knowing about \: escapes or any such.  If necessary, :'s can be put
243  * into the termcap file in octal.
244  */
245 static char *
246 tskip(bp)
247 	register char *bp;
248 {
249 
250 	while (*bp && *bp != ':')
251 		bp++;
252 	if (*bp == ':')
253 		bp++;
254 	return (bp);
255 }
256 
257 /*
258  * Return the (numeric) option id.
259  * Numeric options look like
260  *	li#80
261  * i.e. the option string is separated from the numeric value by
262  * a # character.  If the option is not found we return -1.
263  * Note that we handle octal numbers beginning with 0.
264  */
265 tgetnum(id)
266 	char *id;
267 {
268 	register int i, base;
269 	register char *bp = tbuf;
270 
271 	for (;;) {
272 		bp = tskip(bp);
273 		if (*bp == 0)
274 			return (-1);
275 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
276 			continue;
277 		if (*bp == '@')
278 			return (-1);
279 		if (*bp != '#')
280 			continue;
281 		bp++;
282 		base = 10;
283 		if (*bp == '0')
284 			base = 8;
285 		i = 0;
286 		while (isdigit(*bp))
287 			i *= base, i += *bp++ - '0';
288 		return (i);
289 	}
290 }
291 
292 /*
293  * Handle a flag option.
294  * Flag options are given "naked", i.e. followed by a : or the end
295  * of the buffer.  Return 1 if we find the option, or 0 if it is
296  * not given.
297  */
298 tgetflag(id)
299 	char *id;
300 {
301 	register char *bp = tbuf;
302 
303 	for (;;) {
304 		bp = tskip(bp);
305 		if (!*bp)
306 			return (0);
307 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
308 			if (!*bp || *bp == ':')
309 				return (1);
310 			else if (*bp == '@')
311 				return (0);
312 		}
313 	}
314 }
315 
316 /*
317  * Get a string valued option.
318  * These are given as
319  *	cl=^Z
320  * Much decoding is done on the strings, and the strings are
321  * placed in area, which is a ref parameter which is updated.
322  * No checking on area overflow.
323  */
324 char *
325 tgetstr(id, area)
326 	char *id, **area;
327 {
328 	register char *bp = tbuf;
329 
330 	for (;;) {
331 		bp = tskip(bp);
332 		if (!*bp)
333 			return (0);
334 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
335 			continue;
336 		if (*bp == '@')
337 			return (0);
338 		if (*bp != '=')
339 			continue;
340 		bp++;
341 		return (tdecode(bp, area));
342 	}
343 }
344 
345 /*
346  * Tdecode does the grung work to decode the
347  * string capability escapes.
348  */
349 static char *
350 tdecode(str, area)
351 	register char *str;
352 	char **area;
353 {
354 	register char *cp;
355 	register int c;
356 	register char *dp;
357 	int i;
358 
359 	cp = *area;
360 	while ((c = *str++) && c != ':') {
361 		switch (c) {
362 
363 		case '^':
364 			c = *str++ & 037;
365 			break;
366 
367 		case '\\':
368 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
369 			c = *str++;
370 nextc:
371 			if (*dp++ == c) {
372 				c = *dp++;
373 				break;
374 			}
375 			dp++;
376 			if (*dp)
377 				goto nextc;
378 			if (isdigit(c)) {
379 				c -= '0', i = 2;
380 				do
381 					c <<= 3, c |= *str++ - '0';
382 				while (--i && isdigit(*str));
383 			}
384 			break;
385 		}
386 		*cp++ = c;
387 	}
388 	*cp++ = 0;
389 	str = *area;
390 	*area = cp;
391 	return (str);
392 }
393