119808Sdist /* 235464Sbostic * Copyright (c) 1983 The Regents of the University of California. 335464Sbostic * All rights reserved. 435464Sbostic * 535464Sbostic * Redistribution and use in source and binary forms are permitted 635464Sbostic * provided that the above copyright notice and this paragraph are 735464Sbostic * duplicated in all such forms and that any documentation, 835464Sbostic * advertising materials, and other materials related to such 935464Sbostic * distribution and use acknowledge that the software was developed 1035464Sbostic * by the University of California, Berkeley. The name of the 1135464Sbostic * University may not be used to endorse or promote products derived 1235464Sbostic * from this software without specific prior written permission. 1335464Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1435464Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1535464Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1619808Sdist */ 1719808Sdist 1813278Ssam #ifndef lint 19*37857Sbostic static char sccsid[] = "@(#)remcap.c 5.3 (Berkeley) 05/11/89"; 2035464Sbostic #endif /* not lint */ 213694Sroot 223694Sroot /* 233805Ssam * remcap - routines for dealing with the remote host data base 243694Sroot * 2513278Ssam * derived from termcap 263805Ssam */ 2713278Ssam #include <sys/file.h> 2813278Ssam #include <ctype.h> 29*37857Sbostic #include "pathnames.h" 303805Ssam 3113278Ssam #ifndef BUFSIZ 3213278Ssam #define BUFSIZ 1024 3313278Ssam #endif 3413278Ssam #define MAXHOP 32 /* max number of tc= indirections */ 3513278Ssam 363805Ssam #define tgetent rgetent 373805Ssam #define tnchktc rnchktc 383805Ssam #define tnamatch rnamatch 393805Ssam #define tgetnum rgetnum 403805Ssam #define tgetflag rgetflag 413805Ssam #define tgetstr rgetstr 42*37857Sbostic #define E_TERMCAP RM = _PATH_REMOTE 433808Ssam #define V_TERMCAP "REMOTE" 443808Ssam #define V_TERM "HOST" 453808Ssam 463805Ssam char *RM; 473805Ssam 483805Ssam /* 493805Ssam * termcap - routines for dealing with the terminal capability data base 503805Ssam * 513694Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 523694Sroot * for capabilities alphabetically would not be a n**2/2 533694Sroot * process when large numbers of capabilities are given. 543805Ssam * Note: If we add a last pointer now we will screw up the 553805Ssam * tc capability. We really should compile termcap. 563694Sroot * 573694Sroot * Essentially all the work here is scanning and decoding escapes 583805Ssam * in string capabilities. We don't use stdio because the editor 593805Ssam * doesn't, and because living w/o it is not hard. 603805Ssam */ 613694Sroot 623805Ssam static char *tbuf; 633805Ssam static int hopcount; /* detect infinite loops in termcap, init 0 */ 643805Ssam char *tskip(); 653805Ssam char *tgetstr(); 663805Ssam char *tdecode(); 673694Sroot char *getenv(); 6813278Ssam static char *remotefile; 693694Sroot 703694Sroot /* 713805Ssam * Get an entry for terminal name in buffer bp, 723805Ssam * from the termcap file. Parse is very rudimentary; 733694Sroot * we just notice escaped newlines. 743694Sroot */ 753805Ssam tgetent(bp, name) 763694Sroot char *bp, *name; 773694Sroot { 7813278Ssam char lbuf[BUFSIZ], *cp, *p; 7913278Ssam int rc1, rc2; 8013138Sralph 8113138Sralph remotefile = cp = getenv(V_TERMCAP); 82*37857Sbostic if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) { 83*37857Sbostic remotefile = cp = _PATH_REMOTE; 8413278Ssam return (getent(bp, name, cp)); 8513138Sralph } else { 8613138Sralph if ((rc1 = getent(bp, name, cp)) != 1) 8713138Sralph *bp = '\0'; 88*37857Sbostic remotefile = cp = _PATH_REMOTE; 8913138Sralph rc2 = getent(lbuf, name, cp); 9013138Sralph if (rc1 != 1 && rc2 != 1) 9113278Ssam return (rc2); 9213138Sralph if (rc2 == 1) { 9313138Sralph p = lbuf; 9413138Sralph if (rc1 == 1) 9513138Sralph while (*p++ != ':') 9613138Sralph ; 9713138Sralph if (strlen(bp) + strlen(p) > BUFSIZ) { 9813138Sralph write(2, "Remcap entry too long\n", 23); 9913278Ssam return (-1); 10013138Sralph } 10113138Sralph strcat(bp, p); 10213138Sralph } 10313138Sralph tbuf = bp; 10413278Ssam return (1); 10513138Sralph } 10613138Sralph } 10713138Sralph 10813138Sralph getent(bp, name, cp) 10913138Sralph char *bp, *name, *cp; 11013138Sralph { 1113694Sroot register int c; 1123694Sroot register int i = 0, cnt = 0; 11313278Ssam char ibuf[BUFSIZ], *cp2; 1143694Sroot int tf; 1153694Sroot 1163805Ssam tbuf = bp; 1173805Ssam tf = 0; 1183805Ssam /* 1193805Ssam * TERMCAP can have one of two things in it. It can be the 1203805Ssam * name of a file to use instead of /etc/termcap. In this 1213805Ssam * case it better start with a "/". Or it can be an entry to 1223805Ssam * use so we don't have to read the file. In this case it 1233805Ssam * has to already have the newlines crunched out. 1243805Ssam */ 1253805Ssam if (cp && *cp) { 1263805Ssam if (*cp!='/') { 1273808Ssam cp2 = getenv(V_TERM); 12813278Ssam if (cp2 == (char *)0 || strcmp(name,cp2) == 0) { 1293805Ssam strcpy(bp,cp); 13013278Ssam return (tnchktc()); 13113278Ssam } else 13213278Ssam tf = open(E_TERMCAP, O_RDONLY); 1333805Ssam } else 13413278Ssam tf = open(RM = cp, O_RDONLY); 1353805Ssam } 13613278Ssam if (tf == 0) 13713278Ssam tf = open(E_TERMCAP, O_RDONLY); 1383805Ssam if (tf < 0) 1393694Sroot return (-1); 1403694Sroot for (;;) { 1413694Sroot cp = bp; 1423694Sroot for (;;) { 1433694Sroot if (i == cnt) { 1443694Sroot cnt = read(tf, ibuf, BUFSIZ); 1453694Sroot if (cnt <= 0) { 1463694Sroot close(tf); 1473694Sroot return (0); 1483694Sroot } 1493694Sroot i = 0; 1503694Sroot } 1513694Sroot c = ibuf[i++]; 1523694Sroot if (c == '\n') { 15313138Sralph if (cp > bp && cp[-1] == '\\') { 1543694Sroot cp--; 1553694Sroot continue; 1563694Sroot } 1573694Sroot break; 1583694Sroot } 1593805Ssam if (cp >= bp+BUFSIZ) { 16013138Sralph write(2,"Remcap entry too long\n", 23); 1613805Ssam break; 1623805Ssam } else 1633805Ssam *cp++ = c; 1643694Sroot } 1653694Sroot *cp = 0; 1663694Sroot 1673694Sroot /* 1683694Sroot * The real work for the match. 1693694Sroot */ 1703805Ssam if (tnamatch(name)) { 1713694Sroot close(tf); 17213278Ssam return (tnchktc()); 1733694Sroot } 1743694Sroot } 1753694Sroot } 1763694Sroot 1773694Sroot /* 1783805Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1793805Ssam * recursively find xxx and append that entry (minus the names) 1803805Ssam * to take the place of the tc=xxx entry. This allows termcap 1813805Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1823805Ssam * Note that this works because of the left to right scan. 1833805Ssam */ 1843805Ssam tnchktc() 1853805Ssam { 1863805Ssam register char *p, *q; 1873805Ssam char tcname[16]; /* name of similar terminal */ 1883805Ssam char tcbuf[BUFSIZ]; 1893805Ssam char *holdtbuf = tbuf; 1903805Ssam int l; 19113138Sralph char *cp; 1923805Ssam 1933805Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 1943805Ssam while (*--p != ':') 1953805Ssam if (p<tbuf) { 19613138Sralph write(2, "Bad remcap entry\n", 18); 1973805Ssam return (0); 1983805Ssam } 1993805Ssam p++; 2003805Ssam /* p now points to beginning of last field */ 2013805Ssam if (p[0] != 't' || p[1] != 'c') 20213278Ssam return (1); 20313138Sralph strcpy(tcname, p+3); 2043805Ssam q = tcname; 20513138Sralph while (*q && *q != ':') 2063805Ssam q++; 2073805Ssam *q = 0; 2083805Ssam if (++hopcount > MAXHOP) { 2093805Ssam write(2, "Infinite tc= loop\n", 18); 2103805Ssam return (0); 2113805Ssam } 21213138Sralph if (getent(tcbuf, tcname, remotefile) != 1) { 213*37857Sbostic if (strcmp(remotefile, _PATH_REMOTE) == 0) 21413278Ssam return (0); 215*37857Sbostic else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1) 21613278Ssam return (0); 21713138Sralph } 21813138Sralph for (q = tcbuf; *q++ != ':'; ) 2193805Ssam ; 2203805Ssam l = p - holdtbuf + strlen(q); 2213805Ssam if (l > BUFSIZ) { 22213138Sralph write(2, "Remcap entry too long\n", 23); 22313138Sralph q[BUFSIZ - (p-holdtbuf)] = 0; 2243805Ssam } 22513138Sralph strcpy(p, q); 2263805Ssam tbuf = holdtbuf; 22713278Ssam return (1); 2283805Ssam } 2293805Ssam 2303805Ssam /* 2313805Ssam * Tnamatch deals with name matching. The first field of the termcap 2323694Sroot * entry is a sequence of names separated by |'s, so we compare 2333694Sroot * against each such name. The normal : terminator after the last 2343694Sroot * name (before the first field) stops us. 2353694Sroot */ 2363805Ssam tnamatch(np) 2373694Sroot char *np; 2383694Sroot { 2393694Sroot register char *Np, *Bp; 2403694Sroot 2413805Ssam Bp = tbuf; 2423805Ssam if (*Bp == '#') 24313278Ssam return (0); 2443694Sroot for (;;) { 2453694Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2463694Sroot continue; 2473694Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2483694Sroot return (1); 2493694Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2503694Sroot Bp++; 2513694Sroot if (*Bp == 0 || *Bp == ':') 2523694Sroot return (0); 2533694Sroot Bp++; 2543694Sroot } 2553694Sroot } 2563694Sroot 2573694Sroot /* 2583694Sroot * Skip to the next field. Notice that this is very dumb, not 2593694Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2603805Ssam * into the termcap file in octal. 2613694Sroot */ 2623694Sroot static char * 2633805Ssam tskip(bp) 2643694Sroot register char *bp; 2653694Sroot { 2663694Sroot 2673694Sroot while (*bp && *bp != ':') 2683694Sroot bp++; 2693694Sroot if (*bp == ':') 2703694Sroot bp++; 2713694Sroot return (bp); 2723694Sroot } 2733694Sroot 2743694Sroot /* 2753694Sroot * Return the (numeric) option id. 2763694Sroot * Numeric options look like 2773694Sroot * li#80 2783694Sroot * i.e. the option string is separated from the numeric value by 2793694Sroot * a # character. If the option is not found we return -1. 2803694Sroot * Note that we handle octal numbers beginning with 0. 2813694Sroot */ 2823805Ssam tgetnum(id) 2833694Sroot char *id; 2843694Sroot { 2853694Sroot register int i, base; 2863805Ssam register char *bp = tbuf; 2873694Sroot 2883694Sroot for (;;) { 2893805Ssam bp = tskip(bp); 2903694Sroot if (*bp == 0) 2913694Sroot return (-1); 2923694Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 2933694Sroot continue; 2943805Ssam if (*bp == '@') 29513278Ssam return (-1); 2963694Sroot if (*bp != '#') 2973694Sroot continue; 2983694Sroot bp++; 2993694Sroot base = 10; 3003694Sroot if (*bp == '0') 3013694Sroot base = 8; 3023694Sroot i = 0; 3033694Sroot while (isdigit(*bp)) 3043694Sroot i *= base, i += *bp++ - '0'; 3053694Sroot return (i); 3063694Sroot } 3073694Sroot } 3083694Sroot 3093694Sroot /* 3103694Sroot * Handle a flag option. 3113694Sroot * Flag options are given "naked", i.e. followed by a : or the end 3123694Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3133694Sroot * not given. 3143694Sroot */ 3153805Ssam tgetflag(id) 3163694Sroot char *id; 3173694Sroot { 3183805Ssam register char *bp = tbuf; 3193694Sroot 3203694Sroot for (;;) { 3213805Ssam bp = tskip(bp); 3223694Sroot if (!*bp) 3233694Sroot return (0); 3243805Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3253805Ssam if (!*bp || *bp == ':') 3263805Ssam return (1); 3273805Ssam else if (*bp == '@') 32813278Ssam return (0); 3293805Ssam } 3303694Sroot } 3313694Sroot } 3323694Sroot 3333694Sroot /* 3343694Sroot * Get a string valued option. 3353694Sroot * These are given as 3363694Sroot * cl=^Z 3373694Sroot * Much decoding is done on the strings, and the strings are 3383694Sroot * placed in area, which is a ref parameter which is updated. 3393694Sroot * No checking on area overflow. 3403694Sroot */ 3413694Sroot char * 3423805Ssam tgetstr(id, area) 3433694Sroot char *id, **area; 3443694Sroot { 3453805Ssam register char *bp = tbuf; 3463694Sroot 3473694Sroot for (;;) { 3483805Ssam bp = tskip(bp); 3493694Sroot if (!*bp) 3503694Sroot return (0); 3513694Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3523694Sroot continue; 3533805Ssam if (*bp == '@') 35413278Ssam return (0); 3553694Sroot if (*bp != '=') 3563694Sroot continue; 3573694Sroot bp++; 3583805Ssam return (tdecode(bp, area)); 3593694Sroot } 3603694Sroot } 3613694Sroot 3623694Sroot /* 3633694Sroot * Tdecode does the grung work to decode the 3643694Sroot * string capability escapes. 3653694Sroot */ 3663805Ssam static char * 3673805Ssam tdecode(str, area) 3683694Sroot register char *str; 3693694Sroot char **area; 3703694Sroot { 3713694Sroot register char *cp; 3723694Sroot register int c; 3733694Sroot register char *dp; 3743694Sroot int i; 3753694Sroot 3763694Sroot cp = *area; 3773694Sroot while ((c = *str++) && c != ':') { 3783694Sroot switch (c) { 3793694Sroot 3803694Sroot case '^': 3813694Sroot c = *str++ & 037; 3823694Sroot break; 3833694Sroot 3843694Sroot case '\\': 3853694Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3863694Sroot c = *str++; 3873694Sroot nextc: 3883694Sroot if (*dp++ == c) { 3893694Sroot c = *dp++; 3903694Sroot break; 3913694Sroot } 3923694Sroot dp++; 3933694Sroot if (*dp) 3943694Sroot goto nextc; 3953694Sroot if (isdigit(c)) { 3963694Sroot c -= '0', i = 2; 3973694Sroot do 3983694Sroot c <<= 3, c |= *str++ - '0'; 3993694Sroot while (--i && isdigit(*str)); 4003694Sroot } 4013694Sroot break; 4023694Sroot } 4033694Sroot *cp++ = c; 4043694Sroot } 4053694Sroot *cp++ = 0; 4063694Sroot str = *area; 4073694Sroot *area = cp; 4083694Sroot return (str); 4093694Sroot } 410