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