113300Ssam #ifndef lint 2*17338Sralph static char sccsid[] = "@(#)termcap.c 4.2 (Berkeley) 11/06/84"; 313300Ssam #endif 413300Ssam 513300Ssam #define BUFSIZ 1024 613300Ssam #define MAXHOP 32 /* max number of tc= indirections */ 713300Ssam #define E_TERMCAP "/etc/termcap" 813300Ssam 913300Ssam #include <ctype.h> 1013300Ssam /* 1113300Ssam * termcap - routines for dealing with the terminal capability data base 1213300Ssam * 1313300Ssam * BUG: Should use a "last" pointer in tbuf, so that searching 1413300Ssam * for capabilities alphabetically would not be a n**2/2 1513300Ssam * process when large numbers of capabilities are given. 1613300Ssam * Note: If we add a last pointer now we will screw up the 1713300Ssam * tc capability. We really should compile termcap. 1813300Ssam * 1913300Ssam * Essentially all the work here is scanning and decoding escapes 2013300Ssam * in string capabilities. We don't use stdio because the editor 2113300Ssam * doesn't, and because living w/o it is not hard. 2213300Ssam */ 2313300Ssam 2413300Ssam static char *tbuf; 2513300Ssam static int hopcount; /* detect infinite loops in termcap, init 0 */ 2613300Ssam char *tskip(); 2713300Ssam char *tgetstr(); 2813300Ssam char *tdecode(); 2913300Ssam char *getenv(); 3013300Ssam 3113300Ssam /* 3213300Ssam * Get an entry for terminal name in buffer bp, 3313300Ssam * from the termcap file. Parse is very rudimentary; 3413300Ssam * we just notice escaped newlines. 3513300Ssam */ 3613300Ssam tgetent(bp, name) 3713300Ssam char *bp, *name; 3813300Ssam { 3913300Ssam register char *cp; 4013300Ssam register int c; 4113300Ssam register int i = 0, cnt = 0; 4213300Ssam char ibuf[BUFSIZ]; 4313300Ssam char *cp2; 4413300Ssam int tf; 4513300Ssam 4613300Ssam tbuf = bp; 4713300Ssam tf = 0; 4813300Ssam #ifndef V6 4913300Ssam cp = getenv("TERMCAP"); 5013300Ssam /* 5113300Ssam * TERMCAP can have one of two things in it. It can be the 5213300Ssam * name of a file to use instead of /etc/termcap. In this 5313300Ssam * case it better start with a "/". Or it can be an entry to 5413300Ssam * use so we don't have to read the file. In this case it 5513300Ssam * has to already have the newlines crunched out. 5613300Ssam */ 5713300Ssam if (cp && *cp) { 5813300Ssam if (*cp!='/') { 5913300Ssam cp2 = getenv("TERM"); 6013300Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 6113300Ssam strcpy(bp,cp); 6213300Ssam return(tnchktc()); 6313300Ssam } else { 6413300Ssam tf = open(E_TERMCAP, 0); 6513300Ssam } 6613300Ssam } else 6713300Ssam tf = open(cp, 0); 6813300Ssam } 6913300Ssam if (tf==0) 7013300Ssam tf = open(E_TERMCAP, 0); 7113300Ssam #else 7213300Ssam tf = open(E_TERMCAP, 0); 7313300Ssam #endif 7413300Ssam if (tf < 0) 7513300Ssam return (-1); 7613300Ssam for (;;) { 7713300Ssam cp = bp; 7813300Ssam for (;;) { 7913300Ssam if (i == cnt) { 8013300Ssam cnt = read(tf, ibuf, BUFSIZ); 8113300Ssam if (cnt <= 0) { 8213300Ssam close(tf); 8313300Ssam return (0); 8413300Ssam } 8513300Ssam i = 0; 8613300Ssam } 8713300Ssam c = ibuf[i++]; 8813300Ssam if (c == '\n') { 8913300Ssam if (cp > bp && cp[-1] == '\\'){ 9013300Ssam cp--; 9113300Ssam continue; 9213300Ssam } 9313300Ssam break; 9413300Ssam } 9513300Ssam if (cp >= bp+BUFSIZ) { 9613300Ssam write(2,"Termcap entry too long\n", 23); 9713300Ssam break; 9813300Ssam } else 9913300Ssam *cp++ = c; 10013300Ssam } 10113300Ssam *cp = 0; 10213300Ssam 10313300Ssam /* 10413300Ssam * The real work for the match. 10513300Ssam */ 10613300Ssam if (tnamatch(name)) { 10713300Ssam close(tf); 10813300Ssam return(tnchktc()); 10913300Ssam } 11013300Ssam } 11113300Ssam } 11213300Ssam 11313300Ssam /* 11413300Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 11513300Ssam * recursively find xxx and append that entry (minus the names) 11613300Ssam * to take the place of the tc=xxx entry. This allows termcap 11713300Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 11813300Ssam * Note that this works because of the left to right scan. 11913300Ssam */ 12013300Ssam tnchktc() 12113300Ssam { 12213300Ssam register char *p, *q; 12313300Ssam char tcname[16]; /* name of similar terminal */ 12413300Ssam char tcbuf[BUFSIZ]; 12513300Ssam char *holdtbuf = tbuf; 12613300Ssam int l; 12713300Ssam 12813300Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 12913300Ssam while (*--p != ':') 13013300Ssam if (p<tbuf) { 13113300Ssam write(2, "Bad termcap entry\n", 18); 13213300Ssam return (0); 13313300Ssam } 13413300Ssam p++; 13513300Ssam /* p now points to beginning of last field */ 13613300Ssam if (p[0] != 't' || p[1] != 'c') 13713300Ssam return(1); 13813300Ssam strcpy(tcname,p+3); 13913300Ssam q = tcname; 140*17338Sralph while (*q && *q != ':') 14113300Ssam q++; 14213300Ssam *q = 0; 14313300Ssam if (++hopcount > MAXHOP) { 14413300Ssam write(2, "Infinite tc= loop\n", 18); 14513300Ssam return (0); 14613300Ssam } 14713300Ssam if (tgetent(tcbuf, tcname) != 1) 14813300Ssam return(0); 14913300Ssam for (q=tcbuf; *q != ':'; q++) 15013300Ssam ; 15113300Ssam l = p - holdtbuf + strlen(q); 15213300Ssam if (l > BUFSIZ) { 15313300Ssam write(2, "Termcap entry too long\n", 23); 15413300Ssam q[BUFSIZ - (p-tbuf)] = 0; 15513300Ssam } 15613300Ssam strcpy(p, q+1); 15713300Ssam tbuf = holdtbuf; 15813300Ssam return(1); 15913300Ssam } 16013300Ssam 16113300Ssam /* 16213300Ssam * Tnamatch deals with name matching. The first field of the termcap 16313300Ssam * entry is a sequence of names separated by |'s, so we compare 16413300Ssam * against each such name. The normal : terminator after the last 16513300Ssam * name (before the first field) stops us. 16613300Ssam */ 16713300Ssam tnamatch(np) 16813300Ssam char *np; 16913300Ssam { 17013300Ssam register char *Np, *Bp; 17113300Ssam 17213300Ssam Bp = tbuf; 17313300Ssam if (*Bp == '#') 17413300Ssam return(0); 17513300Ssam for (;;) { 17613300Ssam for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 17713300Ssam continue; 17813300Ssam if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 17913300Ssam return (1); 18013300Ssam while (*Bp && *Bp != ':' && *Bp != '|') 18113300Ssam Bp++; 18213300Ssam if (*Bp == 0 || *Bp == ':') 18313300Ssam return (0); 18413300Ssam Bp++; 18513300Ssam } 18613300Ssam } 18713300Ssam 18813300Ssam /* 18913300Ssam * Skip to the next field. Notice that this is very dumb, not 19013300Ssam * knowing about \: escapes or any such. If necessary, :'s can be put 19113300Ssam * into the termcap file in octal. 19213300Ssam */ 19313300Ssam static char * 19413300Ssam tskip(bp) 19513300Ssam register char *bp; 19613300Ssam { 19713300Ssam 19813300Ssam while (*bp && *bp != ':') 19913300Ssam bp++; 20013300Ssam if (*bp == ':') 20113300Ssam bp++; 20213300Ssam return (bp); 20313300Ssam } 20413300Ssam 20513300Ssam /* 20613300Ssam * Return the (numeric) option id. 20713300Ssam * Numeric options look like 20813300Ssam * li#80 20913300Ssam * i.e. the option string is separated from the numeric value by 21013300Ssam * a # character. If the option is not found we return -1. 21113300Ssam * Note that we handle octal numbers beginning with 0. 21213300Ssam */ 21313300Ssam tgetnum(id) 21413300Ssam char *id; 21513300Ssam { 21613300Ssam register int i, base; 21713300Ssam register char *bp = tbuf; 21813300Ssam 21913300Ssam for (;;) { 22013300Ssam bp = tskip(bp); 22113300Ssam if (*bp == 0) 22213300Ssam return (-1); 22313300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 22413300Ssam continue; 22513300Ssam if (*bp == '@') 22613300Ssam return(-1); 22713300Ssam if (*bp != '#') 22813300Ssam continue; 22913300Ssam bp++; 23013300Ssam base = 10; 23113300Ssam if (*bp == '0') 23213300Ssam base = 8; 23313300Ssam i = 0; 23413300Ssam while (isdigit(*bp)) 23513300Ssam i *= base, i += *bp++ - '0'; 23613300Ssam return (i); 23713300Ssam } 23813300Ssam } 23913300Ssam 24013300Ssam /* 24113300Ssam * Handle a flag option. 24213300Ssam * Flag options are given "naked", i.e. followed by a : or the end 24313300Ssam * of the buffer. Return 1 if we find the option, or 0 if it is 24413300Ssam * not given. 24513300Ssam */ 24613300Ssam tgetflag(id) 24713300Ssam char *id; 24813300Ssam { 24913300Ssam register char *bp = tbuf; 25013300Ssam 25113300Ssam for (;;) { 25213300Ssam bp = tskip(bp); 25313300Ssam if (!*bp) 25413300Ssam return (0); 25513300Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 25613300Ssam if (!*bp || *bp == ':') 25713300Ssam return (1); 25813300Ssam else if (*bp == '@') 25913300Ssam return(0); 26013300Ssam } 26113300Ssam } 26213300Ssam } 26313300Ssam 26413300Ssam /* 26513300Ssam * Get a string valued option. 26613300Ssam * These are given as 26713300Ssam * cl=^Z 26813300Ssam * Much decoding is done on the strings, and the strings are 26913300Ssam * placed in area, which is a ref parameter which is updated. 27013300Ssam * No checking on area overflow. 27113300Ssam */ 27213300Ssam char * 27313300Ssam tgetstr(id, area) 27413300Ssam char *id, **area; 27513300Ssam { 27613300Ssam register char *bp = tbuf; 27713300Ssam 27813300Ssam for (;;) { 27913300Ssam bp = tskip(bp); 28013300Ssam if (!*bp) 28113300Ssam return (0); 28213300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 28313300Ssam continue; 28413300Ssam if (*bp == '@') 28513300Ssam return(0); 28613300Ssam if (*bp != '=') 28713300Ssam continue; 28813300Ssam bp++; 28913300Ssam return (tdecode(bp, area)); 29013300Ssam } 29113300Ssam } 29213300Ssam 29313300Ssam /* 29413300Ssam * Tdecode does the grung work to decode the 29513300Ssam * string capability escapes. 29613300Ssam */ 29713300Ssam static char * 29813300Ssam tdecode(str, area) 29913300Ssam register char *str; 30013300Ssam char **area; 30113300Ssam { 30213300Ssam register char *cp; 30313300Ssam register int c; 30413300Ssam register char *dp; 30513300Ssam int i; 30613300Ssam 30713300Ssam cp = *area; 30813300Ssam while ((c = *str++) && c != ':') { 30913300Ssam switch (c) { 31013300Ssam 31113300Ssam case '^': 31213300Ssam c = *str++ & 037; 31313300Ssam break; 31413300Ssam 31513300Ssam case '\\': 31613300Ssam dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 31713300Ssam c = *str++; 31813300Ssam nextc: 31913300Ssam if (*dp++ == c) { 32013300Ssam c = *dp++; 32113300Ssam break; 32213300Ssam } 32313300Ssam dp++; 32413300Ssam if (*dp) 32513300Ssam goto nextc; 32613300Ssam if (isdigit(c)) { 32713300Ssam c -= '0', i = 2; 32813300Ssam do 32913300Ssam c <<= 3, c |= *str++ - '0'; 33013300Ssam while (--i && isdigit(*str)); 33113300Ssam } 33213300Ssam break; 33313300Ssam } 33413300Ssam *cp++ = c; 33513300Ssam } 33613300Ssam *cp++ = 0; 33713300Ssam str = *area; 33813300Ssam *area = cp; 33913300Ssam return (str); 34013300Ssam } 341