119808Sdist /*
2*62315Sbostic * Copyright (c) 1983, 1993
3*62315Sbostic * The Regents of the University of California. All rights reserved.
435464Sbostic *
556641Sralph * %sccs.include.redist.c%
619808Sdist */
719808Sdist
813278Ssam #ifndef lint
9*62315Sbostic static char sccsid[] = "@(#)remcap.c 8.1 (Berkeley) 06/06/93";
1035464Sbostic #endif /* not lint */
113694Sroot
123694Sroot /*
133805Ssam * remcap - routines for dealing with the remote host data base
143694Sroot *
1513278Ssam * derived from termcap
163805Ssam */
1746231Storek #include <sys/types.h>
1846231Storek #include <fcntl.h>
1913278Ssam #include <ctype.h>
2056641Sralph #include <stdlib.h>
2137857Sbostic #include "pathnames.h"
223805Ssam
2313278Ssam #ifndef BUFSIZ
2413278Ssam #define BUFSIZ 1024
2513278Ssam #endif
2613278Ssam #define MAXHOP 32 /* max number of tc= indirections */
2713278Ssam
283805Ssam #define tgetent rgetent
293805Ssam #define tnchktc rnchktc
303805Ssam #define tnamatch rnamatch
313805Ssam #define tgetnum rgetnum
323805Ssam #define tgetflag rgetflag
333805Ssam #define tgetstr rgetstr
3437857Sbostic #define E_TERMCAP RM = _PATH_REMOTE
353808Ssam #define V_TERMCAP "REMOTE"
363808Ssam #define V_TERM "HOST"
373808Ssam
383805Ssam char *RM;
393805Ssam
403805Ssam /*
413805Ssam * termcap - routines for dealing with the terminal capability data base
423805Ssam *
433694Sroot * BUG: Should use a "last" pointer in tbuf, so that searching
443694Sroot * for capabilities alphabetically would not be a n**2/2
453694Sroot * process when large numbers of capabilities are given.
463805Ssam * Note: If we add a last pointer now we will screw up the
473805Ssam * tc capability. We really should compile termcap.
483694Sroot *
493694Sroot * Essentially all the work here is scanning and decoding escapes
503805Ssam * in string capabilities. We don't use stdio because the editor
513805Ssam * doesn't, and because living w/o it is not hard.
523805Ssam */
533694Sroot
543805Ssam static char *tbuf;
553805Ssam static int hopcount; /* detect infinite loops in termcap, init 0 */
5656641Sralph static char *tskip();
573805Ssam char *tgetstr();
5856641Sralph static char *tdecode();
5913278Ssam static char *remotefile;
603694Sroot
613694Sroot /*
623805Ssam * Get an entry for terminal name in buffer bp,
633805Ssam * from the termcap file. Parse is very rudimentary;
643694Sroot * we just notice escaped newlines.
653694Sroot */
tgetent(bp,name)663805Ssam tgetent(bp, name)
673694Sroot char *bp, *name;
683694Sroot {
6913278Ssam char lbuf[BUFSIZ], *cp, *p;
7013278Ssam int rc1, rc2;
7113138Sralph
7213138Sralph remotefile = cp = getenv(V_TERMCAP);
7337857Sbostic if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) {
7437857Sbostic remotefile = cp = _PATH_REMOTE;
7513278Ssam return (getent(bp, name, cp));
7613138Sralph } else {
7713138Sralph if ((rc1 = getent(bp, name, cp)) != 1)
7813138Sralph *bp = '\0';
7937857Sbostic remotefile = cp = _PATH_REMOTE;
8013138Sralph rc2 = getent(lbuf, name, cp);
8113138Sralph if (rc1 != 1 && rc2 != 1)
8213278Ssam return (rc2);
8313138Sralph if (rc2 == 1) {
8413138Sralph p = lbuf;
8513138Sralph if (rc1 == 1)
8613138Sralph while (*p++ != ':')
8713138Sralph ;
8813138Sralph if (strlen(bp) + strlen(p) > BUFSIZ) {
8913138Sralph write(2, "Remcap entry too long\n", 23);
9013278Ssam return (-1);
9113138Sralph }
9213138Sralph strcat(bp, p);
9313138Sralph }
9413138Sralph tbuf = bp;
9513278Ssam return (1);
9613138Sralph }
9713138Sralph }
9813138Sralph
getent(bp,name,cp)9913138Sralph getent(bp, name, cp)
10013138Sralph char *bp, *name, *cp;
10113138Sralph {
1023694Sroot register int c;
1033694Sroot register int i = 0, cnt = 0;
10413278Ssam char ibuf[BUFSIZ], *cp2;
1053694Sroot int tf;
1063694Sroot
1073805Ssam tbuf = bp;
1083805Ssam tf = 0;
1093805Ssam /*
1103805Ssam * TERMCAP can have one of two things in it. It can be the
1113805Ssam * name of a file to use instead of /etc/termcap. In this
1123805Ssam * case it better start with a "/". Or it can be an entry to
1133805Ssam * use so we don't have to read the file. In this case it
1143805Ssam * has to already have the newlines crunched out.
1153805Ssam */
1163805Ssam if (cp && *cp) {
1173805Ssam if (*cp!='/') {
1183808Ssam cp2 = getenv(V_TERM);
11913278Ssam if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
1203805Ssam strcpy(bp,cp);
12113278Ssam return (tnchktc());
12213278Ssam } else
12313278Ssam tf = open(E_TERMCAP, O_RDONLY);
1243805Ssam } else
12513278Ssam tf = open(RM = cp, O_RDONLY);
1263805Ssam }
12713278Ssam if (tf == 0)
12813278Ssam tf = open(E_TERMCAP, O_RDONLY);
1293805Ssam if (tf < 0)
1303694Sroot return (-1);
1313694Sroot for (;;) {
1323694Sroot cp = bp;
1333694Sroot for (;;) {
1343694Sroot if (i == cnt) {
1353694Sroot cnt = read(tf, ibuf, BUFSIZ);
1363694Sroot if (cnt <= 0) {
1373694Sroot close(tf);
1383694Sroot return (0);
1393694Sroot }
1403694Sroot i = 0;
1413694Sroot }
1423694Sroot c = ibuf[i++];
1433694Sroot if (c == '\n') {
14413138Sralph if (cp > bp && cp[-1] == '\\') {
1453694Sroot cp--;
1463694Sroot continue;
1473694Sroot }
1483694Sroot break;
1493694Sroot }
1503805Ssam if (cp >= bp+BUFSIZ) {
15113138Sralph write(2,"Remcap entry too long\n", 23);
1523805Ssam break;
1533805Ssam } else
1543805Ssam *cp++ = c;
1553694Sroot }
1563694Sroot *cp = 0;
1573694Sroot
1583694Sroot /*
1593694Sroot * The real work for the match.
1603694Sroot */
1613805Ssam if (tnamatch(name)) {
1623694Sroot close(tf);
16313278Ssam return (tnchktc());
1643694Sroot }
1653694Sroot }
1663694Sroot }
1673694Sroot
1683694Sroot /*
1693805Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so,
1703805Ssam * recursively find xxx and append that entry (minus the names)
1713805Ssam * to take the place of the tc=xxx entry. This allows termcap
1723805Ssam * entries to say "like an HP2621 but doesn't turn on the labels".
1733805Ssam * Note that this works because of the left to right scan.
1743805Ssam */
tnchktc()1753805Ssam tnchktc()
1763805Ssam {
1773805Ssam register char *p, *q;
1783805Ssam char tcname[16]; /* name of similar terminal */
1793805Ssam char tcbuf[BUFSIZ];
1803805Ssam char *holdtbuf = tbuf;
1813805Ssam int l;
18213138Sralph char *cp;
1833805Ssam
1843805Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */
1853805Ssam while (*--p != ':')
1863805Ssam if (p<tbuf) {
18713138Sralph write(2, "Bad remcap entry\n", 18);
1883805Ssam return (0);
1893805Ssam }
1903805Ssam p++;
1913805Ssam /* p now points to beginning of last field */
1923805Ssam if (p[0] != 't' || p[1] != 'c')
19313278Ssam return (1);
19413138Sralph strcpy(tcname, p+3);
1953805Ssam q = tcname;
19613138Sralph while (*q && *q != ':')
1973805Ssam q++;
1983805Ssam *q = 0;
1993805Ssam if (++hopcount > MAXHOP) {
2003805Ssam write(2, "Infinite tc= loop\n", 18);
2013805Ssam return (0);
2023805Ssam }
20313138Sralph if (getent(tcbuf, tcname, remotefile) != 1) {
20437857Sbostic if (strcmp(remotefile, _PATH_REMOTE) == 0)
20513278Ssam return (0);
20637857Sbostic else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1)
20713278Ssam return (0);
20813138Sralph }
20913138Sralph for (q = tcbuf; *q++ != ':'; )
2103805Ssam ;
2113805Ssam l = p - holdtbuf + strlen(q);
2123805Ssam if (l > BUFSIZ) {
21313138Sralph write(2, "Remcap entry too long\n", 23);
21413138Sralph q[BUFSIZ - (p-holdtbuf)] = 0;
2153805Ssam }
21613138Sralph strcpy(p, q);
2173805Ssam tbuf = holdtbuf;
21813278Ssam return (1);
2193805Ssam }
2203805Ssam
2213805Ssam /*
2223805Ssam * Tnamatch deals with name matching. The first field of the termcap
2233694Sroot * entry is a sequence of names separated by |'s, so we compare
2243694Sroot * against each such name. The normal : terminator after the last
2253694Sroot * name (before the first field) stops us.
2263694Sroot */
tnamatch(np)2273805Ssam tnamatch(np)
2283694Sroot char *np;
2293694Sroot {
2303694Sroot register char *Np, *Bp;
2313694Sroot
2323805Ssam Bp = tbuf;
2333805Ssam if (*Bp == '#')
23413278Ssam return (0);
2353694Sroot for (;;) {
2363694Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
2373694Sroot continue;
2383694Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
2393694Sroot return (1);
2403694Sroot while (*Bp && *Bp != ':' && *Bp != '|')
2413694Sroot Bp++;
2423694Sroot if (*Bp == 0 || *Bp == ':')
2433694Sroot return (0);
2443694Sroot Bp++;
2453694Sroot }
2463694Sroot }
2473694Sroot
2483694Sroot /*
2493694Sroot * Skip to the next field. Notice that this is very dumb, not
2503694Sroot * knowing about \: escapes or any such. If necessary, :'s can be put
2513805Ssam * into the termcap file in octal.
2523694Sroot */
2533694Sroot static char *
tskip(bp)2543805Ssam tskip(bp)
2553694Sroot register char *bp;
2563694Sroot {
2573694Sroot
2583694Sroot while (*bp && *bp != ':')
2593694Sroot bp++;
2603694Sroot if (*bp == ':')
2613694Sroot bp++;
2623694Sroot return (bp);
2633694Sroot }
2643694Sroot
2653694Sroot /*
2663694Sroot * Return the (numeric) option id.
2673694Sroot * Numeric options look like
2683694Sroot * li#80
2693694Sroot * i.e. the option string is separated from the numeric value by
2703694Sroot * a # character. If the option is not found we return -1.
2713694Sroot * Note that we handle octal numbers beginning with 0.
2723694Sroot */
tgetnum(id)2733805Ssam tgetnum(id)
2743694Sroot char *id;
2753694Sroot {
2763694Sroot register int i, base;
2773805Ssam register char *bp = tbuf;
2783694Sroot
2793694Sroot for (;;) {
2803805Ssam bp = tskip(bp);
2813694Sroot if (*bp == 0)
2823694Sroot return (-1);
2833694Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
2843694Sroot continue;
2853805Ssam if (*bp == '@')
28613278Ssam return (-1);
2873694Sroot if (*bp != '#')
2883694Sroot continue;
2893694Sroot bp++;
2903694Sroot base = 10;
2913694Sroot if (*bp == '0')
2923694Sroot base = 8;
2933694Sroot i = 0;
2943694Sroot while (isdigit(*bp))
2953694Sroot i *= base, i += *bp++ - '0';
2963694Sroot return (i);
2973694Sroot }
2983694Sroot }
2993694Sroot
3003694Sroot /*
3013694Sroot * Handle a flag option.
3023694Sroot * Flag options are given "naked", i.e. followed by a : or the end
3033694Sroot * of the buffer. Return 1 if we find the option, or 0 if it is
3043694Sroot * not given.
3053694Sroot */
tgetflag(id)3063805Ssam tgetflag(id)
3073694Sroot char *id;
3083694Sroot {
3093805Ssam register char *bp = tbuf;
3103694Sroot
3113694Sroot for (;;) {
3123805Ssam bp = tskip(bp);
3133694Sroot if (!*bp)
3143694Sroot return (0);
3153805Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
3163805Ssam if (!*bp || *bp == ':')
3173805Ssam return (1);
3183805Ssam else if (*bp == '@')
31913278Ssam return (0);
3203805Ssam }
3213694Sroot }
3223694Sroot }
3233694Sroot
3243694Sroot /*
3253694Sroot * Get a string valued option.
3263694Sroot * These are given as
3273694Sroot * cl=^Z
3283694Sroot * Much decoding is done on the strings, and the strings are
3293694Sroot * placed in area, which is a ref parameter which is updated.
3303694Sroot * No checking on area overflow.
3313694Sroot */
3323694Sroot char *
tgetstr(id,area)3333805Ssam tgetstr(id, area)
3343694Sroot char *id, **area;
3353694Sroot {
3363805Ssam register char *bp = tbuf;
3373694Sroot
3383694Sroot for (;;) {
3393805Ssam bp = tskip(bp);
3403694Sroot if (!*bp)
3413694Sroot return (0);
3423694Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
3433694Sroot continue;
3443805Ssam if (*bp == '@')
34513278Ssam return (0);
3463694Sroot if (*bp != '=')
3473694Sroot continue;
3483694Sroot bp++;
3493805Ssam return (tdecode(bp, area));
3503694Sroot }
3513694Sroot }
3523694Sroot
3533694Sroot /*
3543694Sroot * Tdecode does the grung work to decode the
3553694Sroot * string capability escapes.
3563694Sroot */
3573805Ssam static char *
tdecode(str,area)3583805Ssam tdecode(str, area)
3593694Sroot register char *str;
3603694Sroot char **area;
3613694Sroot {
3623694Sroot register char *cp;
3633694Sroot register int c;
3643694Sroot register char *dp;
3653694Sroot int i;
3663694Sroot
3673694Sroot cp = *area;
3683694Sroot while ((c = *str++) && c != ':') {
3693694Sroot switch (c) {
3703694Sroot
3713694Sroot case '^':
3723694Sroot c = *str++ & 037;
3733694Sroot break;
3743694Sroot
3753694Sroot case '\\':
3763694Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
3773694Sroot c = *str++;
3783694Sroot nextc:
3793694Sroot if (*dp++ == c) {
3803694Sroot c = *dp++;
3813694Sroot break;
3823694Sroot }
3833694Sroot dp++;
3843694Sroot if (*dp)
3853694Sroot goto nextc;
3863694Sroot if (isdigit(c)) {
3873694Sroot c -= '0', i = 2;
3883694Sroot do
3893694Sroot c <<= 3, c |= *str++ - '0';
3903694Sroot while (--i && isdigit(*str));
3913694Sroot }
3923694Sroot break;
3933694Sroot }
3943694Sroot *cp++ = c;
3953694Sroot }
3963694Sroot *cp++ = 0;
3973694Sroot str = *area;
3983694Sroot *area = cp;
3993694Sroot return (str);
4003694Sroot }
401