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