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