113300Ssam #ifndef lint 2*17676Sserge static char sccsid[] = "@(#)termcap.c 4.4 (Berkeley) 01/09/85"; 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 int tf; 4413300Ssam 4513300Ssam tbuf = bp; 4617584Ssam tf = -1; 4713300Ssam #ifndef V6 4813300Ssam cp = getenv("TERMCAP"); 4913300Ssam /* 5013300Ssam * TERMCAP can have one of two things in it. It can be the 5113300Ssam * name of a file to use instead of /etc/termcap. In this 5213300Ssam * case it better start with a "/". Or it can be an entry to 5313300Ssam * use so we don't have to read the file. In this case it 5413300Ssam * has to already have the newlines crunched out. 5513300Ssam */ 5613300Ssam if (cp && *cp) { 57*17676Sserge if (*cp == '/') { 58*17676Sserge tf = open(cp, 0); 59*17676Sserge } else { 60*17676Sserge tbuf = cp; 61*17676Sserge c = tnamatch(name); 62*17676Sserge tbuf = bp; 63*17676Sserge if (c) { 6413300Ssam strcpy(bp,cp); 6513300Ssam return(tnchktc()); 6613300Ssam } 67*17676Sserge } 6813300Ssam } 6917584Ssam 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; 14017338Sralph while (*q && *q != ':') 14113300Ssam q++; 14213300Ssam *q = 0; 14313300Ssam if (++hopcount > MAXHOP) { 14413300Ssam write(2, "Infinite tc= loop\n", 18); 14513300Ssam return (0); 14613300Ssam } 14717584Ssam if (tgetent(tcbuf, tcname) != 1) { 14817584Ssam hopcount = 0; /* unwind recursion */ 14913300Ssam return(0); 15017584Ssam } 15113300Ssam for (q=tcbuf; *q != ':'; q++) 15213300Ssam ; 15313300Ssam l = p - holdtbuf + strlen(q); 15413300Ssam if (l > BUFSIZ) { 15513300Ssam write(2, "Termcap entry too long\n", 23); 15613300Ssam q[BUFSIZ - (p-tbuf)] = 0; 15713300Ssam } 15813300Ssam strcpy(p, q+1); 15913300Ssam tbuf = holdtbuf; 16017584Ssam hopcount = 0; /* unwind recursion */ 16113300Ssam return(1); 16213300Ssam } 16313300Ssam 16413300Ssam /* 16513300Ssam * Tnamatch deals with name matching. The first field of the termcap 16613300Ssam * entry is a sequence of names separated by |'s, so we compare 16713300Ssam * against each such name. The normal : terminator after the last 16813300Ssam * name (before the first field) stops us. 16913300Ssam */ 17013300Ssam tnamatch(np) 17113300Ssam char *np; 17213300Ssam { 17313300Ssam register char *Np, *Bp; 17413300Ssam 17513300Ssam Bp = tbuf; 17613300Ssam if (*Bp == '#') 17713300Ssam return(0); 17813300Ssam for (;;) { 17913300Ssam for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 18013300Ssam continue; 18113300Ssam if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 18213300Ssam return (1); 18313300Ssam while (*Bp && *Bp != ':' && *Bp != '|') 18413300Ssam Bp++; 18513300Ssam if (*Bp == 0 || *Bp == ':') 18613300Ssam return (0); 18713300Ssam Bp++; 18813300Ssam } 18913300Ssam } 19013300Ssam 19113300Ssam /* 19213300Ssam * Skip to the next field. Notice that this is very dumb, not 19313300Ssam * knowing about \: escapes or any such. If necessary, :'s can be put 19413300Ssam * into the termcap file in octal. 19513300Ssam */ 19613300Ssam static char * 19713300Ssam tskip(bp) 19813300Ssam register char *bp; 19913300Ssam { 20013300Ssam 20113300Ssam while (*bp && *bp != ':') 20213300Ssam bp++; 20313300Ssam if (*bp == ':') 20413300Ssam bp++; 20513300Ssam return (bp); 20613300Ssam } 20713300Ssam 20813300Ssam /* 20913300Ssam * Return the (numeric) option id. 21013300Ssam * Numeric options look like 21113300Ssam * li#80 21213300Ssam * i.e. the option string is separated from the numeric value by 21313300Ssam * a # character. If the option is not found we return -1. 21413300Ssam * Note that we handle octal numbers beginning with 0. 21513300Ssam */ 21613300Ssam tgetnum(id) 21713300Ssam char *id; 21813300Ssam { 21913300Ssam register int i, base; 22013300Ssam register char *bp = tbuf; 22113300Ssam 22213300Ssam for (;;) { 22313300Ssam bp = tskip(bp); 22413300Ssam if (*bp == 0) 22513300Ssam return (-1); 22613300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 22713300Ssam continue; 22813300Ssam if (*bp == '@') 22913300Ssam return(-1); 23013300Ssam if (*bp != '#') 23113300Ssam continue; 23213300Ssam bp++; 23313300Ssam base = 10; 23413300Ssam if (*bp == '0') 23513300Ssam base = 8; 23613300Ssam i = 0; 23713300Ssam while (isdigit(*bp)) 23813300Ssam i *= base, i += *bp++ - '0'; 23913300Ssam return (i); 24013300Ssam } 24113300Ssam } 24213300Ssam 24313300Ssam /* 24413300Ssam * Handle a flag option. 24513300Ssam * Flag options are given "naked", i.e. followed by a : or the end 24613300Ssam * of the buffer. Return 1 if we find the option, or 0 if it is 24713300Ssam * not given. 24813300Ssam */ 24913300Ssam tgetflag(id) 25013300Ssam char *id; 25113300Ssam { 25213300Ssam register char *bp = tbuf; 25313300Ssam 25413300Ssam for (;;) { 25513300Ssam bp = tskip(bp); 25613300Ssam if (!*bp) 25713300Ssam return (0); 25813300Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 25913300Ssam if (!*bp || *bp == ':') 26013300Ssam return (1); 26113300Ssam else if (*bp == '@') 26213300Ssam return(0); 26313300Ssam } 26413300Ssam } 26513300Ssam } 26613300Ssam 26713300Ssam /* 26813300Ssam * Get a string valued option. 26913300Ssam * These are given as 27013300Ssam * cl=^Z 27113300Ssam * Much decoding is done on the strings, and the strings are 27213300Ssam * placed in area, which is a ref parameter which is updated. 27313300Ssam * No checking on area overflow. 27413300Ssam */ 27513300Ssam char * 27613300Ssam tgetstr(id, area) 27713300Ssam char *id, **area; 27813300Ssam { 27913300Ssam register char *bp = tbuf; 28013300Ssam 28113300Ssam for (;;) { 28213300Ssam bp = tskip(bp); 28313300Ssam if (!*bp) 28413300Ssam return (0); 28513300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 28613300Ssam continue; 28713300Ssam if (*bp == '@') 28813300Ssam return(0); 28913300Ssam if (*bp != '=') 29013300Ssam continue; 29113300Ssam bp++; 29213300Ssam return (tdecode(bp, area)); 29313300Ssam } 29413300Ssam } 29513300Ssam 29613300Ssam /* 29713300Ssam * Tdecode does the grung work to decode the 29813300Ssam * string capability escapes. 29913300Ssam */ 30013300Ssam static char * 30113300Ssam tdecode(str, area) 30213300Ssam register char *str; 30313300Ssam char **area; 30413300Ssam { 30513300Ssam register char *cp; 30613300Ssam register int c; 30713300Ssam register char *dp; 30813300Ssam int i; 30913300Ssam 31013300Ssam cp = *area; 31113300Ssam while ((c = *str++) && c != ':') { 31213300Ssam switch (c) { 31313300Ssam 31413300Ssam case '^': 31513300Ssam c = *str++ & 037; 31613300Ssam break; 31713300Ssam 31813300Ssam case '\\': 31913300Ssam dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 32013300Ssam c = *str++; 32113300Ssam nextc: 32213300Ssam if (*dp++ == c) { 32313300Ssam c = *dp++; 32413300Ssam break; 32513300Ssam } 32613300Ssam dp++; 32713300Ssam if (*dp) 32813300Ssam goto nextc; 32913300Ssam if (isdigit(c)) { 33013300Ssam c -= '0', i = 2; 33113300Ssam do 33213300Ssam c <<= 3, c |= *str++ - '0'; 33313300Ssam while (--i && isdigit(*str)); 33413300Ssam } 33513300Ssam break; 33613300Ssam } 33713300Ssam *cp++ = c; 33813300Ssam } 33913300Ssam *cp++ = 0; 34013300Ssam str = *area; 34113300Ssam *area = cp; 34213300Ssam return (str); 34313300Ssam } 344