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