xref: /onnv-gate/usr/src/cmd/tip/remcap.c (revision 549:9e644232f978)
10Sstevel@tonic-gate /*
2*549Smuffin  * Copyright 2001 Sun Microsystems, Inc.  All rights reserved.
3*549Smuffin  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*
70Sstevel@tonic-gate  * Copyright (c) 1983 Regents of the University of California.
80Sstevel@tonic-gate  * All rights reserved. The Berkeley software License Agreement
90Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
100Sstevel@tonic-gate  */
11*549Smuffin 
120Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
130Sstevel@tonic-gate 
140Sstevel@tonic-gate /*
150Sstevel@tonic-gate  * remcap - routines for dealing with the remote host data base
160Sstevel@tonic-gate  *
170Sstevel@tonic-gate  * derived from termcap
180Sstevel@tonic-gate  */
190Sstevel@tonic-gate #ifdef USG
200Sstevel@tonic-gate #include <sys/types.h>
210Sstevel@tonic-gate #include <fcntl.h>	/* for O_RDONLY */
220Sstevel@tonic-gate #else
230Sstevel@tonic-gate #include <sys/file.h>	/* for O_RDONLY */
240Sstevel@tonic-gate #include <ctype.h>
250Sstevel@tonic-gate #endif
260Sstevel@tonic-gate 
27*549Smuffin #include <stdlib.h>
28*549Smuffin #include <string.h>
29*549Smuffin #include <unistd.h>
30*549Smuffin #include <ctype.h>
31*549Smuffin 
320Sstevel@tonic-gate #ifndef BUFSIZ
330Sstevel@tonic-gate #define	BUFSIZ		1024
340Sstevel@tonic-gate #endif
350Sstevel@tonic-gate #define	MAXHOP		32		/* max number of tc= indirections */
360Sstevel@tonic-gate #define	SYSREMOTE	"/etc/remote"	/* system remote file */
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #define	tgetent		rgetent
390Sstevel@tonic-gate #define	tnchktc		rnchktc
400Sstevel@tonic-gate #define	tnamatch	rnamatch
410Sstevel@tonic-gate #define	tgetnum		rgetnum
420Sstevel@tonic-gate #define	tgetflag	rgetflag
430Sstevel@tonic-gate #define	tgetstr		rgetstr
440Sstevel@tonic-gate #define	E_TERMCAP	RM = SYSREMOTE
450Sstevel@tonic-gate #define	V_TERMCAP	"REMOTE"
460Sstevel@tonic-gate #define	V_TERM		"HOST"
470Sstevel@tonic-gate 
480Sstevel@tonic-gate char	*RM;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate  * termcap - routines for dealing with the terminal capability data base
520Sstevel@tonic-gate  *
530Sstevel@tonic-gate  * BUG:		Should use a "last" pointer in tbuf, so that searching
540Sstevel@tonic-gate  *		for capabilities alphabetically would not be a n**2/2
550Sstevel@tonic-gate  *		process when large numbers of capabilities are given.
560Sstevel@tonic-gate  * Note:	If we add a last pointer now we will screw up the
570Sstevel@tonic-gate  *		tc capability. We really should compile termcap.
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  * Essentially all the work here is scanning and decoding escapes
600Sstevel@tonic-gate  * in string capabilities.  We don't use stdio because the editor
610Sstevel@tonic-gate  * doesn't, and because living w/o it is not hard.
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate 
64*549Smuffin static char *tbuf;
65*549Smuffin static int hopcount;	/* detect infinite loops in termcap, init 0 */
66*549Smuffin static char *remotefile;
67*549Smuffin 
68*549Smuffin static char	*tskip(char *);
69*549Smuffin static char	*tdecode(char *, char **);
70*549Smuffin 
71*549Smuffin char	*tgetstr(char *, char **);
72*549Smuffin int	getent(char *, char *, char *, int);
73*549Smuffin int	tnchktc(void);
74*549Smuffin int	tnamatch(char *);
75*549Smuffin 
76*549Smuffin extern void	myperm(void);
77*549Smuffin extern void	userperm(void);
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate  * If we use a user specified entry to get the device name,
810Sstevel@tonic-gate  * we need to open the device as the user.
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate int trusted_device = 0;
840Sstevel@tonic-gate 
850Sstevel@tonic-gate /*
860Sstevel@tonic-gate  * Get an entry for terminal name in buffer bp,
870Sstevel@tonic-gate  * from the termcap file.  Parse is very rudimentary;
880Sstevel@tonic-gate  * we just notice escaped newlines.
890Sstevel@tonic-gate  */
90*549Smuffin int
tgetent(char * bp,char * name,int len)91*549Smuffin tgetent(char *bp, char *name, int len)
920Sstevel@tonic-gate {
930Sstevel@tonic-gate 	char lbuf[BUFSIZ], *cp, *p;
940Sstevel@tonic-gate 	int rc1, rc2;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	trusted_device = 1;
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 	remotefile = cp = getenv(V_TERMCAP);
990Sstevel@tonic-gate 	if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) {
1000Sstevel@tonic-gate 		remotefile = cp = SYSREMOTE;
1010Sstevel@tonic-gate 		return (getent(bp, name, cp, len));
1020Sstevel@tonic-gate 	} else {
1030Sstevel@tonic-gate 		if ((rc1 = getent(bp, name, cp, len)) != 1)
1040Sstevel@tonic-gate 			*bp = '\0';
1050Sstevel@tonic-gate 		remotefile = cp = SYSREMOTE;
1060Sstevel@tonic-gate 		rc2 = getent(lbuf, name, cp, sizeof (lbuf));
1070Sstevel@tonic-gate 		if (rc1 != 1 && rc2 != 1)
1080Sstevel@tonic-gate 			return (rc2);
1090Sstevel@tonic-gate 		if (rc2 == 1) {
1100Sstevel@tonic-gate 			p = lbuf;
1110Sstevel@tonic-gate 			if (rc1 == 1)
1120Sstevel@tonic-gate 				while (*p++ != ':')
1130Sstevel@tonic-gate 					;
1140Sstevel@tonic-gate 			if (strlen(bp) + strlen(p) >= len) {
115*549Smuffin 				(void) write(2, "Remcap entry too long\n", 23);
1160Sstevel@tonic-gate 				return (-1);
1170Sstevel@tonic-gate 			}
118*549Smuffin 			(void) strcat(bp, p);
1190Sstevel@tonic-gate 		}
1200Sstevel@tonic-gate 		tbuf = bp;
1210Sstevel@tonic-gate 		return (1);
1220Sstevel@tonic-gate 	}
1230Sstevel@tonic-gate }
1240Sstevel@tonic-gate 
125*549Smuffin int
getent(char * bp,char * name,char * cp,int len)126*549Smuffin getent(char *bp, char *name, char *cp, int len)
1270Sstevel@tonic-gate {
128*549Smuffin 	int c;
129*549Smuffin 	int i = 0, cnt = 0;
1300Sstevel@tonic-gate 	char ibuf[BUFSIZ], *cp2;
1310Sstevel@tonic-gate 	int tf;
1320Sstevel@tonic-gate 	int safe = 1; /* reset only when we open the user's $REMOTE */
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	tbuf = bp;
1350Sstevel@tonic-gate 	tf = 0;
1360Sstevel@tonic-gate 	/*
1370Sstevel@tonic-gate 	 * TERMCAP can have one of two things in it. It can be the
1380Sstevel@tonic-gate 	 * name of a file to use instead of /etc/termcap. In this
1390Sstevel@tonic-gate 	 * case it better start with a "/". Or it can be an entry to
1400Sstevel@tonic-gate 	 * use so we don't have to read the file. In this case it
1410Sstevel@tonic-gate 	 * has to already have the newlines crunched out.
1420Sstevel@tonic-gate 	 */
1430Sstevel@tonic-gate 	if (cp && *cp) {
1440Sstevel@tonic-gate 		if (*cp != '/') {
1450Sstevel@tonic-gate 			cp2 = getenv(V_TERM);
1460Sstevel@tonic-gate 			if (cp2 == (char *)0 || strcmp(name, cp2) == 0) {
1470Sstevel@tonic-gate 				if (strstr(cp, "dv=") != 0)
1480Sstevel@tonic-gate 					trusted_device = 0;
149*549Smuffin 				(void) strncpy(bp, cp, len-1);
1500Sstevel@tonic-gate 				bp[len-1] = '\0';
1510Sstevel@tonic-gate 				return (tnchktc());
1520Sstevel@tonic-gate 			} else
1530Sstevel@tonic-gate 				tf = open(E_TERMCAP, O_RDONLY);
1540Sstevel@tonic-gate 		} else {
1550Sstevel@tonic-gate 			/* open SYSREMOTE as uucp, other files as user */
1560Sstevel@tonic-gate 			safe = strcmp(cp, SYSREMOTE) == 0;
1570Sstevel@tonic-gate 			if (!safe)
1580Sstevel@tonic-gate 				userperm();
1590Sstevel@tonic-gate 			tf = open(RM = cp, O_RDONLY);
1600Sstevel@tonic-gate 			if (!safe)
1610Sstevel@tonic-gate 				myperm();
1620Sstevel@tonic-gate 		}
1630Sstevel@tonic-gate 	}
1640Sstevel@tonic-gate 	if (tf == 0)
1650Sstevel@tonic-gate 		tf = open(E_TERMCAP, O_RDONLY);
1660Sstevel@tonic-gate 	if (tf < 0)
1670Sstevel@tonic-gate 		return (-1);
1680Sstevel@tonic-gate 	for (;;) {
1690Sstevel@tonic-gate 		cp = bp;
1700Sstevel@tonic-gate 		for (;;) {
1710Sstevel@tonic-gate 			if (i == cnt) {
1720Sstevel@tonic-gate 				cnt = read(tf, ibuf, BUFSIZ);
1730Sstevel@tonic-gate 				if (cnt <= 0) {
174*549Smuffin 					(void) close(tf);
1750Sstevel@tonic-gate 					return (0);
1760Sstevel@tonic-gate 				}
1770Sstevel@tonic-gate 				i = 0;
1780Sstevel@tonic-gate 			}
1790Sstevel@tonic-gate 			c = ibuf[i++];
1800Sstevel@tonic-gate 			if (c == '\n') {
1810Sstevel@tonic-gate 				if (cp > bp && cp[-1] == '\\') {
1820Sstevel@tonic-gate 					cp--;
1830Sstevel@tonic-gate 					continue;
1840Sstevel@tonic-gate 				}
1850Sstevel@tonic-gate 				break;
1860Sstevel@tonic-gate 			}
1870Sstevel@tonic-gate 			if (cp >= bp+len) {
188*549Smuffin 				(void) write(2, "Remcap entry too long\n", 23);
1890Sstevel@tonic-gate 				break;
1900Sstevel@tonic-gate 			} else
1910Sstevel@tonic-gate 				*cp++ = c;
1920Sstevel@tonic-gate 		}
1930Sstevel@tonic-gate 		*cp = 0;
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 		/*
1960Sstevel@tonic-gate 		 * The real work for the match.
1970Sstevel@tonic-gate 		 */
1980Sstevel@tonic-gate 		if (tnamatch(name)) {
1990Sstevel@tonic-gate 			/*
2000Sstevel@tonic-gate 			 * if a dv= entry is obtained from $REMOTE,
2010Sstevel@tonic-gate 			 * switch off trusted_device status
2020Sstevel@tonic-gate 			 */
2030Sstevel@tonic-gate 			if (!safe && strstr(bp, "dv=") != 0)
2040Sstevel@tonic-gate 				trusted_device = 0;
205*549Smuffin 			(void) close(tf);
2060Sstevel@tonic-gate 			return (tnchktc());
2070Sstevel@tonic-gate 		}
2080Sstevel@tonic-gate 	}
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate /*
2120Sstevel@tonic-gate  * tnchktc: check the last entry, see if it's tc=xxx. If so,
2130Sstevel@tonic-gate  * recursively find xxx and append that entry (minus the names)
2140Sstevel@tonic-gate  * to take the place of the tc=xxx entry. This allows termcap
2150Sstevel@tonic-gate  * entries to say "like an HP2621 but doesn't turn on the labels".
2160Sstevel@tonic-gate  * Note that this works because of the left to right scan.
2170Sstevel@tonic-gate  */
218*549Smuffin int
tnchktc(void)219*549Smuffin tnchktc(void)
2200Sstevel@tonic-gate {
221*549Smuffin 	char *p, *q;
2220Sstevel@tonic-gate 	char tcname[64];	/* name of similar terminal */
2230Sstevel@tonic-gate 	char tcbuf[BUFSIZ];
2240Sstevel@tonic-gate 	char *holdtbuf = tbuf;
2250Sstevel@tonic-gate 	int l;
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
2280Sstevel@tonic-gate 	while (*--p != ':')
2290Sstevel@tonic-gate 		if (p < tbuf) {
230*549Smuffin 			(void) write(2, "Bad remcap entry\n", 18);
2310Sstevel@tonic-gate 			return (0);
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 	p++;
2340Sstevel@tonic-gate 	/* p now points to beginning of last field */
2350Sstevel@tonic-gate 	if (p[0] != 't' || p[1] != 'c')
2360Sstevel@tonic-gate 		return (1);
237*549Smuffin 	(void) strlcpy(tcname, p+3, sizeof (tcname));
2380Sstevel@tonic-gate 	q = tcname;
2390Sstevel@tonic-gate 	while (*q && *q != ':')
2400Sstevel@tonic-gate 		q++;
2410Sstevel@tonic-gate 	*q = 0;
2420Sstevel@tonic-gate 	if (++hopcount > MAXHOP) {
243*549Smuffin 		(void) write(2, "Infinite tc= loop\n", 18);
2440Sstevel@tonic-gate 		return (0);
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 	if (getent(tcbuf, tcname, remotefile, sizeof (tcbuf)) != 1) {
2470Sstevel@tonic-gate 		if (strcmp(remotefile, SYSREMOTE) == 0)
2480Sstevel@tonic-gate 			return (0);
2490Sstevel@tonic-gate 		else if (getent(tcbuf, tcname, SYSREMOTE, sizeof (tcbuf)) != 1)
2500Sstevel@tonic-gate 			return (0);
2510Sstevel@tonic-gate 	}
2520Sstevel@tonic-gate 	for (q = tcbuf; *q++ != ':'; )
2530Sstevel@tonic-gate 		;
2540Sstevel@tonic-gate 	l = p - holdtbuf + strlen(q);
2550Sstevel@tonic-gate 	if (l > BUFSIZ) {
256*549Smuffin 		(void) write(2, "Remcap entry too long\n", 23);
2570Sstevel@tonic-gate 		q[BUFSIZ - (p-holdtbuf)] = 0;
2580Sstevel@tonic-gate 	}
259*549Smuffin 	(void) strcpy(p, q);
2600Sstevel@tonic-gate 	tbuf = holdtbuf;
2610Sstevel@tonic-gate 	return (1);
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate /*
2650Sstevel@tonic-gate  * Tnamatch deals with name matching.  The first field of the termcap
2660Sstevel@tonic-gate  * entry is a sequence of names separated by |'s, so we compare
2670Sstevel@tonic-gate  * against each such name.  The normal : terminator after the last
2680Sstevel@tonic-gate  * name (before the first field) stops us.
2690Sstevel@tonic-gate  */
270*549Smuffin int
tnamatch(char * np)271*549Smuffin tnamatch(char *np)
2720Sstevel@tonic-gate {
273*549Smuffin 	char *Np, *Bp;
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	Bp = tbuf;
2760Sstevel@tonic-gate 	if (*Bp == '#')
2770Sstevel@tonic-gate 		return (0);
2780Sstevel@tonic-gate 	for (;;) {
2790Sstevel@tonic-gate 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2800Sstevel@tonic-gate 			continue;
2810Sstevel@tonic-gate 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2820Sstevel@tonic-gate 			return (1);
2830Sstevel@tonic-gate 		while (*Bp && *Bp != ':' && *Bp != '|')
2840Sstevel@tonic-gate 			Bp++;
2850Sstevel@tonic-gate 		if (*Bp == 0 || *Bp == ':')
2860Sstevel@tonic-gate 			return (0);
2870Sstevel@tonic-gate 		Bp++;
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate }
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate  * Skip to the next field.  Notice that this is very dumb, not
2930Sstevel@tonic-gate  * knowing about \: escapes or any such.  If necessary, :'s can be put
2940Sstevel@tonic-gate  * into the termcap file in octal.
2950Sstevel@tonic-gate  */
2960Sstevel@tonic-gate static char *
tskip(char * bp)297*549Smuffin tskip(char *bp)
2980Sstevel@tonic-gate {
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	while (*bp && *bp != ':')
3010Sstevel@tonic-gate 		bp++;
3020Sstevel@tonic-gate 	if (*bp == ':') {
3030Sstevel@tonic-gate 		do {
3040Sstevel@tonic-gate 			bp++;
3050Sstevel@tonic-gate 			while (isspace(*bp))
3060Sstevel@tonic-gate 				bp++;
3070Sstevel@tonic-gate 		} while (*bp == ':');
3080Sstevel@tonic-gate 	}
3090Sstevel@tonic-gate 	return (bp);
3100Sstevel@tonic-gate }
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate /*
3130Sstevel@tonic-gate  * Return the (numeric) option id.
3140Sstevel@tonic-gate  * Numeric options look like
3150Sstevel@tonic-gate  *	li#80
3160Sstevel@tonic-gate  * i.e. the option string is separated from the numeric value by
3170Sstevel@tonic-gate  * a # character.  If the option is not found we return -1.
3180Sstevel@tonic-gate  * Note that we handle octal numbers beginning with 0.
3190Sstevel@tonic-gate  */
320*549Smuffin int
tgetnum(char * id)321*549Smuffin tgetnum(char *id)
3220Sstevel@tonic-gate {
323*549Smuffin 	int i, base;
324*549Smuffin 	char *bp = tbuf;
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	for (;;) {
3270Sstevel@tonic-gate 		bp = tskip(bp);
3280Sstevel@tonic-gate 		if (*bp == 0)
3290Sstevel@tonic-gate 			return (-1);
3300Sstevel@tonic-gate 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3310Sstevel@tonic-gate 			continue;
3320Sstevel@tonic-gate 		if (*bp == '@')
3330Sstevel@tonic-gate 			return (-1);
3340Sstevel@tonic-gate 		if (*bp != '#')
3350Sstevel@tonic-gate 			continue;
3360Sstevel@tonic-gate 		bp++;
3370Sstevel@tonic-gate 		base = 10;
3380Sstevel@tonic-gate 		if (*bp == '0')
3390Sstevel@tonic-gate 			base = 8;
3400Sstevel@tonic-gate 		i = 0;
3410Sstevel@tonic-gate 		while (isdigit(*bp))
3420Sstevel@tonic-gate 			i *= base, i += *bp++ - '0';
3430Sstevel@tonic-gate 		return (i);
3440Sstevel@tonic-gate 	}
3450Sstevel@tonic-gate }
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate /*
3480Sstevel@tonic-gate  * Handle a flag option.
3490Sstevel@tonic-gate  * Flag options are given "naked", i.e. followed by a : or the end
3500Sstevel@tonic-gate  * of the buffer.  Return 1 if we find the option, or 0 if it is
3510Sstevel@tonic-gate  * not given.
3520Sstevel@tonic-gate  */
353*549Smuffin int
tgetflag(char * id)354*549Smuffin tgetflag(char *id)
3550Sstevel@tonic-gate {
356*549Smuffin 	char *bp = tbuf;
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	for (;;) {
3590Sstevel@tonic-gate 		bp = tskip(bp);
3600Sstevel@tonic-gate 		if (!*bp)
3610Sstevel@tonic-gate 			return (0);
3620Sstevel@tonic-gate 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3630Sstevel@tonic-gate 			if (!*bp || *bp == ':')
3640Sstevel@tonic-gate 				return (1);
3650Sstevel@tonic-gate 			else if (*bp == '@')
3660Sstevel@tonic-gate 				return (0);
3670Sstevel@tonic-gate 		}
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate }
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate /*
3720Sstevel@tonic-gate  * Get a string valued option.
3730Sstevel@tonic-gate  * These are given as
3740Sstevel@tonic-gate  *	cl=^Z
3750Sstevel@tonic-gate  * Much decoding is done on the strings, and the strings are
3760Sstevel@tonic-gate  * placed in area, which is a ref parameter which is updated.
3770Sstevel@tonic-gate  * No checking on area overflow.
3780Sstevel@tonic-gate  */
3790Sstevel@tonic-gate char *
tgetstr(char * id,char ** area)380*549Smuffin tgetstr(char *id, char **area)
3810Sstevel@tonic-gate {
382*549Smuffin 	char *bp = tbuf;
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	for (;;) {
3850Sstevel@tonic-gate 		bp = tskip(bp);
3860Sstevel@tonic-gate 		if (!*bp)
3870Sstevel@tonic-gate 			return (0);
3880Sstevel@tonic-gate 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3890Sstevel@tonic-gate 			continue;
3900Sstevel@tonic-gate 		if (*bp == '@')
3910Sstevel@tonic-gate 			return (0);
3920Sstevel@tonic-gate 		if (*bp != '=')
3930Sstevel@tonic-gate 			continue;
3940Sstevel@tonic-gate 		bp++;
3950Sstevel@tonic-gate 		return (tdecode(bp, area));
3960Sstevel@tonic-gate 	}
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate  * Tdecode does the grung work to decode the
4010Sstevel@tonic-gate  * string capability escapes.
4020Sstevel@tonic-gate  */
4030Sstevel@tonic-gate static char *
tdecode(char * str,char ** area)404*549Smuffin tdecode(char *str, char **area)
4050Sstevel@tonic-gate {
406*549Smuffin 	char *cp;
407*549Smuffin 	int c;
408*549Smuffin 	char *dp;
4090Sstevel@tonic-gate 	int i;
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	cp = *area;
412*549Smuffin 	while ((c = *str++) != 0 && c != ':') {
4130Sstevel@tonic-gate 		switch (c) {
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 		case '^':
4160Sstevel@tonic-gate 			c = *str++ & 037;
4170Sstevel@tonic-gate 			break;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 		case '\\':
4200Sstevel@tonic-gate 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
4210Sstevel@tonic-gate 			c = *str++;
4220Sstevel@tonic-gate nextc:
4230Sstevel@tonic-gate 			if (*dp++ == c) {
4240Sstevel@tonic-gate 				c = *dp++;
4250Sstevel@tonic-gate 				break;
4260Sstevel@tonic-gate 			}
4270Sstevel@tonic-gate 			dp++;
4280Sstevel@tonic-gate 			if (*dp)
4290Sstevel@tonic-gate 				goto nextc;
4300Sstevel@tonic-gate 			if (isdigit(c)) {
4310Sstevel@tonic-gate 				c -= '0', i = 2;
4320Sstevel@tonic-gate 				do
4330Sstevel@tonic-gate 					c <<= 3, c |= *str++ - '0';
434*549Smuffin 				while (--i && isdigit(*str))
435*549Smuffin 					;
4360Sstevel@tonic-gate 			}
4370Sstevel@tonic-gate 			break;
4380Sstevel@tonic-gate 		}
4390Sstevel@tonic-gate 		*cp++ = c;
4400Sstevel@tonic-gate 	}
4410Sstevel@tonic-gate 	*cp++ = 0;
4420Sstevel@tonic-gate 	str = *area;
4430Sstevel@tonic-gate 	*area = cp;
4440Sstevel@tonic-gate 	return (str);
4450Sstevel@tonic-gate }
446