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