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