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