1*13138Sralph /* remcap.c 4.7 83/06/15 */ 23694Sroot /* Copyright (c) 1979 Regents of the University of California */ 3*13138Sralph /* 4*13138Sralph * Modified 9/27/82 - Michael Wendel 5*13138Sralph * General Instrument R&D 6*13138Sralph * Looks in user Remote file first. 7*13138Sralph * Looks in system Remote file for each tc= entry 8*13138Sralph * that cannot be resolved in the user Remote file. 9*13138Sralph * Finally looks into the system Remote file to 10*13138Sralph * resolve remote name. 11*13138Sralph * User remote file will supplement the system file 12*13138Sralph * since all the entries in the user file occur 13*13138Sralph * ahead of duplicate entries from the system file. 14*13138Sralph */ 154003Ssam #ifndef BUFSIZ 163805Ssam #define BUFSIZ 1024 174003Ssam #endif 183805Ssam #define MAXHOP 32 /* max number of tc= indirections */ 193694Sroot 203694Sroot #include <ctype.h> 214012Ssam #ifdef VMUNIX 223805Ssam #include "local/uparm.h" 234012Ssam #endif 243694Sroot /* 253805Ssam * remcap - routines for dealing with the remote host data base 263694Sroot * 273805Ssam * Made from termcap with the following defines. 283805Ssam */ 293805Ssam #define REMOTE /* special for tip */ 30*13138Sralph #define SYSREMOTE "/etc/remote" /* system remote file */ 313805Ssam 323805Ssam #ifdef REMOTE 333805Ssam #define tgetent rgetent 343805Ssam #define tnchktc rnchktc 353805Ssam #define tnamatch rnamatch 363805Ssam #define tgetnum rgetnum 373805Ssam #define tgetflag rgetflag 383805Ssam #define tgetstr rgetstr 393805Ssam #undef E_TERMCAP 40*13138Sralph #define E_TERMCAP RM = SYSREMOTE 413808Ssam #define V_TERMCAP "REMOTE" 423808Ssam #define V_TERM "HOST" 433808Ssam 443805Ssam char *RM; 453808Ssam #else 463808Ssam #define V_TERMCAP "TERMCAP" 473808Ssam #define V_TERM "TERM" 483805Ssam #endif 493805Ssam 503805Ssam /* 513805Ssam * termcap - routines for dealing with the terminal capability data base 523805Ssam * 533694Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 543694Sroot * for capabilities alphabetically would not be a n**2/2 553694Sroot * process when large numbers of capabilities are given. 563805Ssam * Note: If we add a last pointer now we will screw up the 573805Ssam * tc capability. We really should compile termcap. 583694Sroot * 593694Sroot * Essentially all the work here is scanning and decoding escapes 603805Ssam * in string capabilities. We don't use stdio because the editor 613805Ssam * doesn't, and because living w/o it is not hard. 623805Ssam */ 633694Sroot 64*13138Sralph static char *sccsid = "@(#)remcap.c 4.7 06/15/83"; 653805Ssam static char *tbuf; 663805Ssam static int hopcount; /* detect infinite loops in termcap, init 0 */ 673805Ssam char *tskip(); 683805Ssam char *tgetstr(); 693805Ssam char *tdecode(); 703694Sroot char *getenv(); 71*13138Sralph static char *remotefile; 723694Sroot 733694Sroot /* 743805Ssam * Get an entry for terminal name in buffer bp, 753805Ssam * from the termcap file. Parse is very rudimentary; 763694Sroot * we just notice escaped newlines. 773694Sroot */ 783805Ssam tgetent(bp, name) 793694Sroot char *bp, *name; 803694Sroot { 81*13138Sralph char lbuf[BUFSIZ]; 82*13138Sralph int rc1, rc2; 83*13138Sralph char *cp; 84*13138Sralph char *p; 85*13138Sralph 86*13138Sralph remotefile = cp = getenv(V_TERMCAP); 87*13138Sralph if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) { 88*13138Sralph remotefile = cp = SYSREMOTE; 89*13138Sralph return(getent(bp, name, cp)); 90*13138Sralph } else { 91*13138Sralph if ((rc1 = getent(bp, name, cp)) != 1) 92*13138Sralph *bp = '\0'; 93*13138Sralph remotefile = cp = SYSREMOTE; 94*13138Sralph rc2 = getent(lbuf, name, cp); 95*13138Sralph if (rc1 != 1 && rc2 != 1) 96*13138Sralph return(rc2); 97*13138Sralph if (rc2 == 1) { 98*13138Sralph p = lbuf; 99*13138Sralph if (rc1 == 1) 100*13138Sralph while (*p++ != ':') 101*13138Sralph ; 102*13138Sralph if (strlen(bp) + strlen(p) > BUFSIZ) { 103*13138Sralph write(2, "Remcap entry too long\n", 23); 104*13138Sralph return(-1); 105*13138Sralph } 106*13138Sralph strcat(bp, p); 107*13138Sralph } 108*13138Sralph tbuf = bp; 109*13138Sralph return(1); 110*13138Sralph } 111*13138Sralph } 112*13138Sralph 113*13138Sralph getent(bp, name, cp) 114*13138Sralph char *bp, *name, *cp; 115*13138Sralph { 1163694Sroot register int c; 1173694Sroot register int i = 0, cnt = 0; 1183694Sroot char ibuf[BUFSIZ]; 1193805Ssam char *cp2; 1203694Sroot int tf; 1213694Sroot 1223805Ssam tbuf = bp; 1233805Ssam tf = 0; 1243805Ssam #ifndef V6 1253805Ssam /* 1263805Ssam * TERMCAP can have one of two things in it. It can be the 1273805Ssam * name of a file to use instead of /etc/termcap. In this 1283805Ssam * case it better start with a "/". Or it can be an entry to 1293805Ssam * use so we don't have to read the file. In this case it 1303805Ssam * has to already have the newlines crunched out. 1313805Ssam */ 1323805Ssam if (cp && *cp) { 1333805Ssam if (*cp!='/') { 1343808Ssam cp2 = getenv(V_TERM); 1353805Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1363805Ssam strcpy(bp,cp); 1373805Ssam return(tnchktc()); 1383805Ssam } else { 1393805Ssam tf = open(E_TERMCAP, 0); 1403805Ssam } 1413805Ssam } else 1423808Ssam #ifdef REMOTE 1433808Ssam tf = open(RM = cp, 0); 1443808Ssam #else 1453805Ssam tf = open(cp, 0); 1463808Ssam #endif 1473805Ssam } 1483805Ssam if (tf==0) 1493805Ssam tf = open(E_TERMCAP, 0); 1503805Ssam #else 1513805Ssam tf = open(E_TERMCAP, 0); 1523805Ssam #endif 1533805Ssam if (tf < 0) 1543694Sroot return (-1); 1553694Sroot for (;;) { 1563694Sroot cp = bp; 1573694Sroot for (;;) { 1583694Sroot if (i == cnt) { 1593694Sroot cnt = read(tf, ibuf, BUFSIZ); 1603694Sroot if (cnt <= 0) { 1613694Sroot close(tf); 1623694Sroot return (0); 1633694Sroot } 1643694Sroot i = 0; 1653694Sroot } 1663694Sroot c = ibuf[i++]; 1673694Sroot if (c == '\n') { 168*13138Sralph if (cp > bp && cp[-1] == '\\') { 1693694Sroot cp--; 1703694Sroot continue; 1713694Sroot } 1723694Sroot break; 1733694Sroot } 1743805Ssam if (cp >= bp+BUFSIZ) { 175*13138Sralph write(2,"Remcap entry too long\n", 23); 1763805Ssam break; 1773805Ssam } else 1783805Ssam *cp++ = c; 1793694Sroot } 1803694Sroot *cp = 0; 1813694Sroot 1823694Sroot /* 1833694Sroot * The real work for the match. 1843694Sroot */ 1853805Ssam if (tnamatch(name)) { 1863694Sroot close(tf); 1873805Ssam return(tnchktc()); 1883694Sroot } 1893694Sroot } 1903694Sroot } 1913694Sroot 1923694Sroot /* 1933805Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1943805Ssam * recursively find xxx and append that entry (minus the names) 1953805Ssam * to take the place of the tc=xxx entry. This allows termcap 1963805Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1973805Ssam * Note that this works because of the left to right scan. 1983805Ssam */ 1993805Ssam tnchktc() 2003805Ssam { 2013805Ssam register char *p, *q; 2023805Ssam char tcname[16]; /* name of similar terminal */ 2033805Ssam char tcbuf[BUFSIZ]; 2043805Ssam char *holdtbuf = tbuf; 2053805Ssam int l; 206*13138Sralph char *cp; 2073805Ssam 2083805Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2093805Ssam while (*--p != ':') 2103805Ssam if (p<tbuf) { 211*13138Sralph write(2, "Bad remcap entry\n", 18); 2123805Ssam return (0); 2133805Ssam } 2143805Ssam p++; 2153805Ssam /* p now points to beginning of last field */ 2163805Ssam if (p[0] != 't' || p[1] != 'c') 2173805Ssam return(1); 218*13138Sralph strcpy(tcname, p+3); 2193805Ssam q = tcname; 220*13138Sralph while (*q && *q != ':') 2213805Ssam q++; 2223805Ssam *q = 0; 2233805Ssam if (++hopcount > MAXHOP) { 2243805Ssam write(2, "Infinite tc= loop\n", 18); 2253805Ssam return (0); 2263805Ssam } 227*13138Sralph if (getent(tcbuf, tcname, remotefile) != 1) { 228*13138Sralph if (strcmp(remotefile, SYSREMOTE) == 0) 229*13138Sralph return(0); 230*13138Sralph else if (getent(tcbuf, tcname, SYSREMOTE) != 1) 231*13138Sralph return(0); 232*13138Sralph } 233*13138Sralph for (q = tcbuf; *q++ != ':'; ) 2343805Ssam ; 2353805Ssam l = p - holdtbuf + strlen(q); 2363805Ssam if (l > BUFSIZ) { 237*13138Sralph write(2, "Remcap entry too long\n", 23); 238*13138Sralph q[BUFSIZ - (p-holdtbuf)] = 0; 2393805Ssam } 240*13138Sralph strcpy(p, q); 2413805Ssam tbuf = holdtbuf; 2423805Ssam return(1); 2433805Ssam } 2443805Ssam 2453805Ssam /* 2463805Ssam * Tnamatch deals with name matching. The first field of the termcap 2473694Sroot * entry is a sequence of names separated by |'s, so we compare 2483694Sroot * against each such name. The normal : terminator after the last 2493694Sroot * name (before the first field) stops us. 2503694Sroot */ 2513805Ssam tnamatch(np) 2523694Sroot char *np; 2533694Sroot { 2543694Sroot register char *Np, *Bp; 2553694Sroot 2563805Ssam Bp = tbuf; 2573805Ssam if (*Bp == '#') 2583805Ssam return(0); 2593694Sroot for (;;) { 2603694Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2613694Sroot continue; 2623694Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2633694Sroot return (1); 2643694Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2653694Sroot Bp++; 2663694Sroot if (*Bp == 0 || *Bp == ':') 2673694Sroot return (0); 2683694Sroot Bp++; 2693694Sroot } 2703694Sroot } 2713694Sroot 2723694Sroot /* 2733694Sroot * Skip to the next field. Notice that this is very dumb, not 2743694Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2753805Ssam * into the termcap file in octal. 2763694Sroot */ 2773694Sroot static char * 2783805Ssam tskip(bp) 2793694Sroot register char *bp; 2803694Sroot { 2813694Sroot 2823694Sroot while (*bp && *bp != ':') 2833694Sroot bp++; 2843694Sroot if (*bp == ':') 2853694Sroot bp++; 2863694Sroot return (bp); 2873694Sroot } 2883694Sroot 2893694Sroot /* 2903694Sroot * Return the (numeric) option id. 2913694Sroot * Numeric options look like 2923694Sroot * li#80 2933694Sroot * i.e. the option string is separated from the numeric value by 2943694Sroot * a # character. If the option is not found we return -1. 2953694Sroot * Note that we handle octal numbers beginning with 0. 2963694Sroot */ 2973805Ssam tgetnum(id) 2983694Sroot char *id; 2993694Sroot { 3003694Sroot register int i, base; 3013805Ssam register char *bp = tbuf; 3023694Sroot 3033694Sroot for (;;) { 3043805Ssam bp = tskip(bp); 3053694Sroot if (*bp == 0) 3063694Sroot return (-1); 3073694Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3083694Sroot continue; 3093805Ssam if (*bp == '@') 3103805Ssam return(-1); 3113694Sroot if (*bp != '#') 3123694Sroot continue; 3133694Sroot bp++; 3143694Sroot base = 10; 3153694Sroot if (*bp == '0') 3163694Sroot base = 8; 3173694Sroot i = 0; 3183694Sroot while (isdigit(*bp)) 3193694Sroot i *= base, i += *bp++ - '0'; 3203694Sroot return (i); 3213694Sroot } 3223694Sroot } 3233694Sroot 3243694Sroot /* 3253694Sroot * Handle a flag option. 3263694Sroot * Flag options are given "naked", i.e. followed by a : or the end 3273694Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3283694Sroot * not given. 3293694Sroot */ 3303805Ssam tgetflag(id) 3313694Sroot char *id; 3323694Sroot { 3333805Ssam register char *bp = tbuf; 3343694Sroot 3353694Sroot for (;;) { 3363805Ssam bp = tskip(bp); 3373694Sroot if (!*bp) 3383694Sroot return (0); 3393805Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3403805Ssam if (!*bp || *bp == ':') 3413805Ssam return (1); 3423805Ssam else if (*bp == '@') 3433805Ssam return(0); 3443805Ssam } 3453694Sroot } 3463694Sroot } 3473694Sroot 3483694Sroot /* 3493694Sroot * Get a string valued option. 3503694Sroot * These are given as 3513694Sroot * cl=^Z 3523694Sroot * Much decoding is done on the strings, and the strings are 3533694Sroot * placed in area, which is a ref parameter which is updated. 3543694Sroot * No checking on area overflow. 3553694Sroot */ 3563694Sroot char * 3573805Ssam tgetstr(id, area) 3583694Sroot char *id, **area; 3593694Sroot { 3603805Ssam register char *bp = tbuf; 3613694Sroot 3623694Sroot for (;;) { 3633805Ssam bp = tskip(bp); 3643694Sroot if (!*bp) 3653694Sroot return (0); 3663694Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3673694Sroot continue; 3683805Ssam if (*bp == '@') 3693805Ssam return(0); 3703694Sroot if (*bp != '=') 3713694Sroot continue; 3723694Sroot bp++; 3733805Ssam return (tdecode(bp, area)); 3743694Sroot } 3753694Sroot } 3763694Sroot 3773694Sroot /* 3783694Sroot * Tdecode does the grung work to decode the 3793694Sroot * string capability escapes. 3803694Sroot */ 3813805Ssam static char * 3823805Ssam tdecode(str, area) 3833694Sroot register char *str; 3843694Sroot char **area; 3853694Sroot { 3863694Sroot register char *cp; 3873694Sroot register int c; 3883694Sroot register char *dp; 3893694Sroot int i; 3903694Sroot 3913694Sroot cp = *area; 3923694Sroot while ((c = *str++) && c != ':') { 3933694Sroot switch (c) { 3943694Sroot 3953694Sroot case '^': 3963694Sroot c = *str++ & 037; 3973694Sroot break; 3983694Sroot 3993694Sroot case '\\': 4003694Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 4013694Sroot c = *str++; 4023694Sroot nextc: 4033694Sroot if (*dp++ == c) { 4043694Sroot c = *dp++; 4053694Sroot break; 4063694Sroot } 4073694Sroot dp++; 4083694Sroot if (*dp) 4093694Sroot goto nextc; 4103694Sroot if (isdigit(c)) { 4113694Sroot c -= '0', i = 2; 4123694Sroot do 4133694Sroot c <<= 3, c |= *str++ - '0'; 4143694Sroot while (--i && isdigit(*str)); 4153694Sroot } 4163694Sroot break; 4173694Sroot } 4183694Sroot *cp++ = c; 4193694Sroot } 4203694Sroot *cp++ = 0; 4213694Sroot str = *area; 4223694Sroot *area = cp; 4233694Sroot return (str); 4243694Sroot } 425