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