122072Sdist /* 236499Sbostic * Copyright (c) 1980 The Regents of the University of California. 336499Sbostic * All rights reserved. 436499Sbostic * 542660Sbostic * %sccs.include.redist.c% 622072Sdist */ 722072Sdist 813300Ssam #ifndef lint 9*54673Storek static char sccsid[] = "@(#)termcap.c 5.6 (Berkeley) 07/03/92"; 1036499Sbostic #endif /* not lint */ 1113300Ssam 1213300Ssam #define BUFSIZ 1024 1313300Ssam #define MAXHOP 32 /* max number of tc= indirections */ 1432153Sjak #define PBUFSIZ 512 /* max length of filename path */ 1532153Sjak #define PVECSIZ 32 /* max number of names in path */ 1613300Ssam 1713300Ssam #include <ctype.h> 18*54673Storek #include <stdlib.h> 1937833Sbostic #include "pathnames.h" 2037833Sbostic 2113300Ssam /* 2213300Ssam * termcap - routines for dealing with the terminal capability data base 2313300Ssam * 2413300Ssam * BUG: Should use a "last" pointer in tbuf, so that searching 2513300Ssam * for capabilities alphabetically would not be a n**2/2 2613300Ssam * process when large numbers of capabilities are given. 2713300Ssam * Note: If we add a last pointer now we will screw up the 2813300Ssam * tc capability. We really should compile termcap. 2913300Ssam * 3013300Ssam * Essentially all the work here is scanning and decoding escapes 3113300Ssam * in string capabilities. We don't use stdio because the editor 3213300Ssam * doesn't, and because living w/o it is not hard. 3313300Ssam */ 3413300Ssam 3513300Ssam static char *tbuf; 3613300Ssam static int hopcount; /* detect infinite loops in termcap, init 0 */ 3732153Sjak static char pathbuf[PBUFSIZ]; /* holds raw path of filenames */ 3832153Sjak static char *pathvec[PVECSIZ]; /* to point to names in pathbuf */ 3932153Sjak static char **pvec; /* holds usable tail of path vector */ 4013300Ssam 41*54673Storek static char *tdecode __P((char *, char **)); 42*54673Storek static char *tskip __P ((char *)); 43*54673Storek 4413300Ssam /* 4532153Sjak * Get an entry for terminal name in buffer bp from the termcap file. 4613300Ssam */ 4713300Ssam tgetent(bp, name) 4813300Ssam char *bp, *name; 4913300Ssam { 5032153Sjak register char *p; 5113300Ssam register char *cp; 5213300Ssam register int c; 5332153Sjak char *term, *home, *termpath; 5432153Sjak char **fname = pathvec; 5513300Ssam 5632153Sjak pvec = pathvec; 5713300Ssam tbuf = bp; 5832153Sjak p = pathbuf; 5913300Ssam cp = getenv("TERMCAP"); 6013300Ssam /* 6113300Ssam * TERMCAP can have one of two things in it. It can be the 6213300Ssam * name of a file to use instead of /etc/termcap. In this 6313300Ssam * case it better start with a "/". Or it can be an entry to 6413300Ssam * use so we don't have to read the file. In this case it 6532153Sjak * has to already have the newlines crunched out. If TERMCAP 6632153Sjak * does not hold a file name then a path of names is searched 6732153Sjak * instead. The path is found in the TERMPATH variable, or 6832153Sjak * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists. 6913300Ssam */ 7032153Sjak if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */ 7132153Sjak if (termpath = getenv("TERMPATH")) 7232153Sjak strncpy(pathbuf, termpath, PBUFSIZ); 7332153Sjak else { 7432153Sjak if (home = getenv("HOME")) { /* set up default */ 7532153Sjak p += strlen(home); /* path, looking in */ 7632153Sjak strcpy(pathbuf, home); /* $HOME first */ 7732153Sjak *p++ = '/'; 7832153Sjak } /* if no $HOME look in current directory */ 7937833Sbostic strncpy(p, _PATH_DEF, PBUFSIZ - (p - pathbuf)); 8017676Sserge } 8113300Ssam } 8232153Sjak else /* user-defined name in TERMCAP */ 8332153Sjak strncpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */ 8437833Sbostic 8532153Sjak *fname++ = pathbuf; /* tokenize path into vector of names */ 8632153Sjak while (*++p) 8732153Sjak if (*p == ' ' || *p == ':') { 8832153Sjak *p = '\0'; 8932153Sjak while (*++p) 9032153Sjak if (*p != ' ' && *p != ':') 9132153Sjak break; 9232153Sjak if (*p == '\0') 9332153Sjak break; 9432153Sjak *fname++ = p; 9532153Sjak if (fname >= pathvec + PVECSIZ) { 9632153Sjak fname--; 9732153Sjak break; 9832153Sjak } 9932153Sjak } 10032153Sjak *fname = (char *) 0; /* mark end of vector */ 10132153Sjak if (cp && *cp && *cp != '/') { 10232153Sjak tbuf = cp; 10332153Sjak c = tnamatch(name); 10432153Sjak tbuf = bp; 10532153Sjak if (c) { 10632153Sjak strcpy(bp,cp); 10732153Sjak return (tnchktc()); 10832153Sjak } 10932153Sjak } 11032153Sjak return (tfindent(bp, name)); /* find terminal entry in path */ 11132153Sjak } 11232153Sjak 11332153Sjak /* 11432153Sjak * tfindent - reads through the list of files in pathvec as if they were one 11532153Sjak * continuous file searching for terminal entries along the way. It will 11632153Sjak * participate in indirect recursion if the call to tnchktc() finds a tc= 11732153Sjak * field, which is only searched for in the current file and files ocurring 11832153Sjak * after it in pathvec. The usable part of this vector is kept in the global 11932153Sjak * variable pvec. Terminal entries may not be broken across files. Parse is 12032153Sjak * very rudimentary; we just notice escaped newlines. 12132153Sjak */ 12232153Sjak tfindent(bp, name) 12332153Sjak char *bp, *name; 12432153Sjak { 12532153Sjak register char *cp; 12632153Sjak register int c; 12732153Sjak register int i, cnt; 12832153Sjak char ibuf[BUFSIZ]; 12932153Sjak int opencnt = 0; 13032153Sjak int tf; 13132153Sjak 13232153Sjak tbuf = bp; 13332153Sjak nextfile: 13432153Sjak i = cnt = 0; 13532153Sjak while (*pvec && (tf = open(*pvec, 0)) < 0) 13632153Sjak pvec++; 13732153Sjak if (!*pvec) 13832153Sjak return (opencnt ? 0 : -1); 13932153Sjak opencnt++; 14013300Ssam for (;;) { 14113300Ssam cp = bp; 14213300Ssam for (;;) { 14313300Ssam if (i == cnt) { 14413300Ssam cnt = read(tf, ibuf, BUFSIZ); 14513300Ssam if (cnt <= 0) { 14613300Ssam close(tf); 14732153Sjak pvec++; 14832153Sjak goto nextfile; 14913300Ssam } 15013300Ssam i = 0; 15113300Ssam } 15213300Ssam c = ibuf[i++]; 15313300Ssam if (c == '\n') { 15413300Ssam if (cp > bp && cp[-1] == '\\'){ 15513300Ssam cp--; 15613300Ssam continue; 15713300Ssam } 15813300Ssam break; 15913300Ssam } 16013300Ssam if (cp >= bp+BUFSIZ) { 16113300Ssam write(2,"Termcap entry too long\n", 23); 16213300Ssam break; 16313300Ssam } else 16413300Ssam *cp++ = c; 16513300Ssam } 16613300Ssam *cp = 0; 16713300Ssam 16813300Ssam /* 16913300Ssam * The real work for the match. 17013300Ssam */ 17113300Ssam if (tnamatch(name)) { 17213300Ssam close(tf); 17313300Ssam return(tnchktc()); 17413300Ssam } 17513300Ssam } 17613300Ssam } 17713300Ssam 17813300Ssam /* 17913300Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 18013300Ssam * recursively find xxx and append that entry (minus the names) 18113300Ssam * to take the place of the tc=xxx entry. This allows termcap 18213300Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 18313300Ssam * Note that this works because of the left to right scan. 18413300Ssam */ 18513300Ssam tnchktc() 18613300Ssam { 18713300Ssam register char *p, *q; 18813300Ssam char tcname[16]; /* name of similar terminal */ 18913300Ssam char tcbuf[BUFSIZ]; 19013300Ssam char *holdtbuf = tbuf; 19113300Ssam int l; 19213300Ssam 19313300Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 19413300Ssam while (*--p != ':') 19513300Ssam if (p<tbuf) { 19613300Ssam write(2, "Bad termcap entry\n", 18); 19713300Ssam return (0); 19813300Ssam } 19913300Ssam p++; 20013300Ssam /* p now points to beginning of last field */ 20113300Ssam if (p[0] != 't' || p[1] != 'c') 20213300Ssam return(1); 20313300Ssam strcpy(tcname,p+3); 20413300Ssam q = tcname; 20517338Sralph while (*q && *q != ':') 20613300Ssam q++; 20713300Ssam *q = 0; 20813300Ssam if (++hopcount > MAXHOP) { 20913300Ssam write(2, "Infinite tc= loop\n", 18); 21013300Ssam return (0); 21113300Ssam } 21232153Sjak if (tfindent(tcbuf, tcname) != 1) { 21317584Ssam hopcount = 0; /* unwind recursion */ 21413300Ssam return(0); 21517584Ssam } 21613300Ssam for (q=tcbuf; *q != ':'; q++) 21713300Ssam ; 21813300Ssam l = p - holdtbuf + strlen(q); 21913300Ssam if (l > BUFSIZ) { 22013300Ssam write(2, "Termcap entry too long\n", 23); 22113300Ssam q[BUFSIZ - (p-tbuf)] = 0; 22213300Ssam } 22313300Ssam strcpy(p, q+1); 22413300Ssam tbuf = holdtbuf; 22517584Ssam hopcount = 0; /* unwind recursion */ 22613300Ssam return(1); 22713300Ssam } 22813300Ssam 22913300Ssam /* 23013300Ssam * Tnamatch deals with name matching. The first field of the termcap 23113300Ssam * entry is a sequence of names separated by |'s, so we compare 23213300Ssam * against each such name. The normal : terminator after the last 23313300Ssam * name (before the first field) stops us. 23413300Ssam */ 23513300Ssam tnamatch(np) 23613300Ssam char *np; 23713300Ssam { 23813300Ssam register char *Np, *Bp; 23913300Ssam 24013300Ssam Bp = tbuf; 24113300Ssam if (*Bp == '#') 24213300Ssam return(0); 24313300Ssam for (;;) { 24413300Ssam for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 24513300Ssam continue; 24613300Ssam if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 24713300Ssam return (1); 24813300Ssam while (*Bp && *Bp != ':' && *Bp != '|') 24913300Ssam Bp++; 25013300Ssam if (*Bp == 0 || *Bp == ':') 25113300Ssam return (0); 25213300Ssam Bp++; 25313300Ssam } 25413300Ssam } 25513300Ssam 25613300Ssam /* 25713300Ssam * Skip to the next field. Notice that this is very dumb, not 25813300Ssam * knowing about \: escapes or any such. If necessary, :'s can be put 25913300Ssam * into the termcap file in octal. 26013300Ssam */ 26113300Ssam static char * 26213300Ssam tskip(bp) 26313300Ssam register char *bp; 26413300Ssam { 26513300Ssam 26613300Ssam while (*bp && *bp != ':') 26713300Ssam bp++; 26813300Ssam if (*bp == ':') 26913300Ssam bp++; 27013300Ssam return (bp); 27113300Ssam } 27213300Ssam 27313300Ssam /* 27413300Ssam * Return the (numeric) option id. 27513300Ssam * Numeric options look like 27613300Ssam * li#80 27713300Ssam * i.e. the option string is separated from the numeric value by 27813300Ssam * a # character. If the option is not found we return -1. 27913300Ssam * Note that we handle octal numbers beginning with 0. 28013300Ssam */ 28113300Ssam tgetnum(id) 28213300Ssam char *id; 28313300Ssam { 28413300Ssam register int i, base; 28513300Ssam register char *bp = tbuf; 28613300Ssam 28713300Ssam for (;;) { 28813300Ssam bp = tskip(bp); 28913300Ssam if (*bp == 0) 29013300Ssam return (-1); 29113300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 29213300Ssam continue; 29313300Ssam if (*bp == '@') 29413300Ssam return(-1); 29513300Ssam if (*bp != '#') 29613300Ssam continue; 29713300Ssam bp++; 29813300Ssam base = 10; 29913300Ssam if (*bp == '0') 30013300Ssam base = 8; 30113300Ssam i = 0; 30213300Ssam while (isdigit(*bp)) 30313300Ssam i *= base, i += *bp++ - '0'; 30413300Ssam return (i); 30513300Ssam } 30613300Ssam } 30713300Ssam 30813300Ssam /* 30913300Ssam * Handle a flag option. 31013300Ssam * Flag options are given "naked", i.e. followed by a : or the end 31113300Ssam * of the buffer. Return 1 if we find the option, or 0 if it is 31213300Ssam * not given. 31313300Ssam */ 31413300Ssam tgetflag(id) 31513300Ssam char *id; 31613300Ssam { 31713300Ssam register char *bp = tbuf; 31813300Ssam 31913300Ssam for (;;) { 32013300Ssam bp = tskip(bp); 32113300Ssam if (!*bp) 32213300Ssam return (0); 32313300Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 32413300Ssam if (!*bp || *bp == ':') 32513300Ssam return (1); 32613300Ssam else if (*bp == '@') 32713300Ssam return(0); 32813300Ssam } 32913300Ssam } 33013300Ssam } 33113300Ssam 33213300Ssam /* 33313300Ssam * Get a string valued option. 33413300Ssam * These are given as 33513300Ssam * cl=^Z 33613300Ssam * Much decoding is done on the strings, and the strings are 33713300Ssam * placed in area, which is a ref parameter which is updated. 33813300Ssam * No checking on area overflow. 33913300Ssam */ 34013300Ssam char * 34113300Ssam tgetstr(id, area) 34213300Ssam char *id, **area; 34313300Ssam { 34413300Ssam register char *bp = tbuf; 34513300Ssam 34613300Ssam for (;;) { 34713300Ssam bp = tskip(bp); 34813300Ssam if (!*bp) 34913300Ssam return (0); 35013300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 35113300Ssam continue; 35213300Ssam if (*bp == '@') 35313300Ssam return(0); 35413300Ssam if (*bp != '=') 35513300Ssam continue; 35613300Ssam bp++; 35713300Ssam return (tdecode(bp, area)); 35813300Ssam } 35913300Ssam } 36013300Ssam 36113300Ssam /* 36213300Ssam * Tdecode does the grung work to decode the 36313300Ssam * string capability escapes. 36413300Ssam */ 36513300Ssam static char * 36613300Ssam tdecode(str, area) 36713300Ssam register char *str; 36813300Ssam char **area; 36913300Ssam { 37013300Ssam register char *cp; 37113300Ssam register int c; 37213300Ssam register char *dp; 37313300Ssam int i; 37413300Ssam 37513300Ssam cp = *area; 37613300Ssam while ((c = *str++) && c != ':') { 37713300Ssam switch (c) { 37813300Ssam 37913300Ssam case '^': 38013300Ssam c = *str++ & 037; 38113300Ssam break; 38213300Ssam 38313300Ssam case '\\': 38413300Ssam dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 38513300Ssam c = *str++; 38613300Ssam nextc: 38713300Ssam if (*dp++ == c) { 38813300Ssam c = *dp++; 38913300Ssam break; 39013300Ssam } 39113300Ssam dp++; 39213300Ssam if (*dp) 39313300Ssam goto nextc; 39413300Ssam if (isdigit(c)) { 39513300Ssam c -= '0', i = 2; 39613300Ssam do 39713300Ssam c <<= 3, c |= *str++ - '0'; 39813300Ssam while (--i && isdigit(*str)); 39913300Ssam } 40013300Ssam break; 40113300Ssam } 40213300Ssam *cp++ = c; 40313300Ssam } 40413300Ssam *cp++ = 0; 40513300Ssam str = *area; 40613300Ssam *area = cp; 40713300Ssam return (str); 40813300Ssam } 409