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