122072Sdist /* 2*36499Sbostic * Copyright (c) 1980 The Regents of the University of California. 3*36499Sbostic * All rights reserved. 4*36499Sbostic * 5*36499Sbostic * Redistribution and use in source and binary forms are permitted 6*36499Sbostic * provided that the above copyright notice and this paragraph are 7*36499Sbostic * duplicated in all such forms and that any documentation, 8*36499Sbostic * advertising materials, and other materials related to such 9*36499Sbostic * distribution and use acknowledge that the software was developed 10*36499Sbostic * by the University of California, Berkeley. The name of the 11*36499Sbostic * University may not be used to endorse or promote products derived 12*36499Sbostic * from this software without specific prior written permission. 13*36499Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*36499Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*36499Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622072Sdist */ 1722072Sdist 1813300Ssam #ifndef lint 19*36499Sbostic static char sccsid[] = "@(#)termcap.c 5.3 (Berkeley) 01/03/89"; 20*36499Sbostic #endif /* not lint */ 2113300Ssam 2213300Ssam #define BUFSIZ 1024 2313300Ssam #define MAXHOP 32 /* max number of tc= indirections */ 2432153Sjak #define PBUFSIZ 512 /* max length of filename path */ 2532153Sjak #define PVECSIZ 32 /* max number of names in path */ 2632153Sjak #define DEF_PATH ".termcap /etc/termcap" 2713300Ssam 2813300Ssam #include <ctype.h> 2913300Ssam /* 3013300Ssam * termcap - routines for dealing with the terminal capability data base 3113300Ssam * 3213300Ssam * BUG: Should use a "last" pointer in tbuf, so that searching 3313300Ssam * for capabilities alphabetically would not be a n**2/2 3413300Ssam * process when large numbers of capabilities are given. 3513300Ssam * Note: If we add a last pointer now we will screw up the 3613300Ssam * tc capability. We really should compile termcap. 3713300Ssam * 3813300Ssam * Essentially all the work here is scanning and decoding escapes 3913300Ssam * in string capabilities. We don't use stdio because the editor 4013300Ssam * doesn't, and because living w/o it is not hard. 4113300Ssam */ 4213300Ssam 4313300Ssam static char *tbuf; 4413300Ssam static int hopcount; /* detect infinite loops in termcap, init 0 */ 4532153Sjak static char pathbuf[PBUFSIZ]; /* holds raw path of filenames */ 4632153Sjak static char *pathvec[PVECSIZ]; /* to point to names in pathbuf */ 4732153Sjak static char **pvec; /* holds usable tail of path vector */ 4813300Ssam char *tskip(); 4913300Ssam char *tgetstr(); 5013300Ssam char *tdecode(); 5113300Ssam char *getenv(); 5213300Ssam 5313300Ssam /* 5432153Sjak * Get an entry for terminal name in buffer bp from the termcap file. 5513300Ssam */ 5613300Ssam tgetent(bp, name) 5713300Ssam char *bp, *name; 5813300Ssam { 5932153Sjak register char *p; 6013300Ssam register char *cp; 6113300Ssam register int c; 6232153Sjak char *term, *home, *termpath; 6332153Sjak char **fname = pathvec; 6413300Ssam 6532153Sjak pvec = pathvec; 6613300Ssam tbuf = bp; 6732153Sjak p = pathbuf; 6813300Ssam #ifndef V6 6913300Ssam cp = getenv("TERMCAP"); 7013300Ssam /* 7113300Ssam * TERMCAP can have one of two things in it. It can be the 7213300Ssam * name of a file to use instead of /etc/termcap. In this 7313300Ssam * case it better start with a "/". Or it can be an entry to 7413300Ssam * use so we don't have to read the file. In this case it 7532153Sjak * has to already have the newlines crunched out. If TERMCAP 7632153Sjak * does not hold a file name then a path of names is searched 7732153Sjak * instead. The path is found in the TERMPATH variable, or 7832153Sjak * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists. 7913300Ssam */ 8032153Sjak if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */ 8132153Sjak if (termpath = getenv("TERMPATH")) 8232153Sjak strncpy(pathbuf, termpath, PBUFSIZ); 8332153Sjak else { 8432153Sjak if (home = getenv("HOME")) { /* set up default */ 8532153Sjak p += strlen(home); /* path, looking in */ 8632153Sjak strcpy(pathbuf, home); /* $HOME first */ 8732153Sjak *p++ = '/'; 8832153Sjak } /* if no $HOME look in current directory */ 8932153Sjak strncpy(p, DEF_PATH, PBUFSIZ - (p - pathbuf)); 9017676Sserge } 9113300Ssam } 9232153Sjak else /* user-defined name in TERMCAP */ 9332153Sjak strncpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */ 9413300Ssam #else 9532153Sjak strncpy(pathbuf, "/etc/termcap", PBUFSIZ); 9613300Ssam #endif 9732153Sjak *fname++ = pathbuf; /* tokenize path into vector of names */ 9832153Sjak while (*++p) 9932153Sjak if (*p == ' ' || *p == ':') { 10032153Sjak *p = '\0'; 10132153Sjak while (*++p) 10232153Sjak if (*p != ' ' && *p != ':') 10332153Sjak break; 10432153Sjak if (*p == '\0') 10532153Sjak break; 10632153Sjak *fname++ = p; 10732153Sjak if (fname >= pathvec + PVECSIZ) { 10832153Sjak fname--; 10932153Sjak break; 11032153Sjak } 11132153Sjak } 11232153Sjak *fname = (char *) 0; /* mark end of vector */ 11332153Sjak if (cp && *cp && *cp != '/') { 11432153Sjak tbuf = cp; 11532153Sjak c = tnamatch(name); 11632153Sjak tbuf = bp; 11732153Sjak if (c) { 11832153Sjak strcpy(bp,cp); 11932153Sjak return (tnchktc()); 12032153Sjak } 12132153Sjak } 12232153Sjak return (tfindent(bp, name)); /* find terminal entry in path */ 12332153Sjak } 12432153Sjak 12532153Sjak /* 12632153Sjak * tfindent - reads through the list of files in pathvec as if they were one 12732153Sjak * continuous file searching for terminal entries along the way. It will 12832153Sjak * participate in indirect recursion if the call to tnchktc() finds a tc= 12932153Sjak * field, which is only searched for in the current file and files ocurring 13032153Sjak * after it in pathvec. The usable part of this vector is kept in the global 13132153Sjak * variable pvec. Terminal entries may not be broken across files. Parse is 13232153Sjak * very rudimentary; we just notice escaped newlines. 13332153Sjak */ 13432153Sjak tfindent(bp, name) 13532153Sjak char *bp, *name; 13632153Sjak { 13732153Sjak register char *cp; 13832153Sjak register int c; 13932153Sjak register int i, cnt; 14032153Sjak char ibuf[BUFSIZ]; 14132153Sjak int opencnt = 0; 14232153Sjak int tf; 14332153Sjak 14432153Sjak tbuf = bp; 14532153Sjak nextfile: 14632153Sjak i = cnt = 0; 14732153Sjak while (*pvec && (tf = open(*pvec, 0)) < 0) 14832153Sjak pvec++; 14932153Sjak if (!*pvec) 15032153Sjak return (opencnt ? 0 : -1); 15132153Sjak opencnt++; 15213300Ssam for (;;) { 15313300Ssam cp = bp; 15413300Ssam for (;;) { 15513300Ssam if (i == cnt) { 15613300Ssam cnt = read(tf, ibuf, BUFSIZ); 15713300Ssam if (cnt <= 0) { 15813300Ssam close(tf); 15932153Sjak pvec++; 16032153Sjak goto nextfile; 16113300Ssam } 16213300Ssam i = 0; 16313300Ssam } 16413300Ssam c = ibuf[i++]; 16513300Ssam if (c == '\n') { 16613300Ssam if (cp > bp && cp[-1] == '\\'){ 16713300Ssam cp--; 16813300Ssam continue; 16913300Ssam } 17013300Ssam break; 17113300Ssam } 17213300Ssam if (cp >= bp+BUFSIZ) { 17313300Ssam write(2,"Termcap entry too long\n", 23); 17413300Ssam break; 17513300Ssam } else 17613300Ssam *cp++ = c; 17713300Ssam } 17813300Ssam *cp = 0; 17913300Ssam 18013300Ssam /* 18113300Ssam * The real work for the match. 18213300Ssam */ 18313300Ssam if (tnamatch(name)) { 18413300Ssam close(tf); 18513300Ssam return(tnchktc()); 18613300Ssam } 18713300Ssam } 18813300Ssam } 18913300Ssam 19013300Ssam /* 19113300Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 19213300Ssam * recursively find xxx and append that entry (minus the names) 19313300Ssam * to take the place of the tc=xxx entry. This allows termcap 19413300Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 19513300Ssam * Note that this works because of the left to right scan. 19613300Ssam */ 19713300Ssam tnchktc() 19813300Ssam { 19913300Ssam register char *p, *q; 20013300Ssam char tcname[16]; /* name of similar terminal */ 20113300Ssam char tcbuf[BUFSIZ]; 20213300Ssam char *holdtbuf = tbuf; 20313300Ssam int l; 20413300Ssam 20513300Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 20613300Ssam while (*--p != ':') 20713300Ssam if (p<tbuf) { 20813300Ssam write(2, "Bad termcap entry\n", 18); 20913300Ssam return (0); 21013300Ssam } 21113300Ssam p++; 21213300Ssam /* p now points to beginning of last field */ 21313300Ssam if (p[0] != 't' || p[1] != 'c') 21413300Ssam return(1); 21513300Ssam strcpy(tcname,p+3); 21613300Ssam q = tcname; 21717338Sralph while (*q && *q != ':') 21813300Ssam q++; 21913300Ssam *q = 0; 22013300Ssam if (++hopcount > MAXHOP) { 22113300Ssam write(2, "Infinite tc= loop\n", 18); 22213300Ssam return (0); 22313300Ssam } 22432153Sjak if (tfindent(tcbuf, tcname) != 1) { 22517584Ssam hopcount = 0; /* unwind recursion */ 22613300Ssam return(0); 22717584Ssam } 22813300Ssam for (q=tcbuf; *q != ':'; q++) 22913300Ssam ; 23013300Ssam l = p - holdtbuf + strlen(q); 23113300Ssam if (l > BUFSIZ) { 23213300Ssam write(2, "Termcap entry too long\n", 23); 23313300Ssam q[BUFSIZ - (p-tbuf)] = 0; 23413300Ssam } 23513300Ssam strcpy(p, q+1); 23613300Ssam tbuf = holdtbuf; 23717584Ssam hopcount = 0; /* unwind recursion */ 23813300Ssam return(1); 23913300Ssam } 24013300Ssam 24113300Ssam /* 24213300Ssam * Tnamatch deals with name matching. The first field of the termcap 24313300Ssam * entry is a sequence of names separated by |'s, so we compare 24413300Ssam * against each such name. The normal : terminator after the last 24513300Ssam * name (before the first field) stops us. 24613300Ssam */ 24713300Ssam tnamatch(np) 24813300Ssam char *np; 24913300Ssam { 25013300Ssam register char *Np, *Bp; 25113300Ssam 25213300Ssam Bp = tbuf; 25313300Ssam if (*Bp == '#') 25413300Ssam return(0); 25513300Ssam for (;;) { 25613300Ssam for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 25713300Ssam continue; 25813300Ssam if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 25913300Ssam return (1); 26013300Ssam while (*Bp && *Bp != ':' && *Bp != '|') 26113300Ssam Bp++; 26213300Ssam if (*Bp == 0 || *Bp == ':') 26313300Ssam return (0); 26413300Ssam Bp++; 26513300Ssam } 26613300Ssam } 26713300Ssam 26813300Ssam /* 26913300Ssam * Skip to the next field. Notice that this is very dumb, not 27013300Ssam * knowing about \: escapes or any such. If necessary, :'s can be put 27113300Ssam * into the termcap file in octal. 27213300Ssam */ 27313300Ssam static char * 27413300Ssam tskip(bp) 27513300Ssam register char *bp; 27613300Ssam { 27713300Ssam 27813300Ssam while (*bp && *bp != ':') 27913300Ssam bp++; 28013300Ssam if (*bp == ':') 28113300Ssam bp++; 28213300Ssam return (bp); 28313300Ssam } 28413300Ssam 28513300Ssam /* 28613300Ssam * Return the (numeric) option id. 28713300Ssam * Numeric options look like 28813300Ssam * li#80 28913300Ssam * i.e. the option string is separated from the numeric value by 29013300Ssam * a # character. If the option is not found we return -1. 29113300Ssam * Note that we handle octal numbers beginning with 0. 29213300Ssam */ 29313300Ssam tgetnum(id) 29413300Ssam char *id; 29513300Ssam { 29613300Ssam register int i, base; 29713300Ssam register char *bp = tbuf; 29813300Ssam 29913300Ssam for (;;) { 30013300Ssam bp = tskip(bp); 30113300Ssam if (*bp == 0) 30213300Ssam return (-1); 30313300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 30413300Ssam continue; 30513300Ssam if (*bp == '@') 30613300Ssam return(-1); 30713300Ssam if (*bp != '#') 30813300Ssam continue; 30913300Ssam bp++; 31013300Ssam base = 10; 31113300Ssam if (*bp == '0') 31213300Ssam base = 8; 31313300Ssam i = 0; 31413300Ssam while (isdigit(*bp)) 31513300Ssam i *= base, i += *bp++ - '0'; 31613300Ssam return (i); 31713300Ssam } 31813300Ssam } 31913300Ssam 32013300Ssam /* 32113300Ssam * Handle a flag option. 32213300Ssam * Flag options are given "naked", i.e. followed by a : or the end 32313300Ssam * of the buffer. Return 1 if we find the option, or 0 if it is 32413300Ssam * not given. 32513300Ssam */ 32613300Ssam tgetflag(id) 32713300Ssam char *id; 32813300Ssam { 32913300Ssam register char *bp = tbuf; 33013300Ssam 33113300Ssam for (;;) { 33213300Ssam bp = tskip(bp); 33313300Ssam if (!*bp) 33413300Ssam return (0); 33513300Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 33613300Ssam if (!*bp || *bp == ':') 33713300Ssam return (1); 33813300Ssam else if (*bp == '@') 33913300Ssam return(0); 34013300Ssam } 34113300Ssam } 34213300Ssam } 34313300Ssam 34413300Ssam /* 34513300Ssam * Get a string valued option. 34613300Ssam * These are given as 34713300Ssam * cl=^Z 34813300Ssam * Much decoding is done on the strings, and the strings are 34913300Ssam * placed in area, which is a ref parameter which is updated. 35013300Ssam * No checking on area overflow. 35113300Ssam */ 35213300Ssam char * 35313300Ssam tgetstr(id, area) 35413300Ssam char *id, **area; 35513300Ssam { 35613300Ssam register char *bp = tbuf; 35713300Ssam 35813300Ssam for (;;) { 35913300Ssam bp = tskip(bp); 36013300Ssam if (!*bp) 36113300Ssam return (0); 36213300Ssam if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 36313300Ssam continue; 36413300Ssam if (*bp == '@') 36513300Ssam return(0); 36613300Ssam if (*bp != '=') 36713300Ssam continue; 36813300Ssam bp++; 36913300Ssam return (tdecode(bp, area)); 37013300Ssam } 37113300Ssam } 37213300Ssam 37313300Ssam /* 37413300Ssam * Tdecode does the grung work to decode the 37513300Ssam * string capability escapes. 37613300Ssam */ 37713300Ssam static char * 37813300Ssam tdecode(str, area) 37913300Ssam register char *str; 38013300Ssam char **area; 38113300Ssam { 38213300Ssam register char *cp; 38313300Ssam register int c; 38413300Ssam register char *dp; 38513300Ssam int i; 38613300Ssam 38713300Ssam cp = *area; 38813300Ssam while ((c = *str++) && c != ':') { 38913300Ssam switch (c) { 39013300Ssam 39113300Ssam case '^': 39213300Ssam c = *str++ & 037; 39313300Ssam break; 39413300Ssam 39513300Ssam case '\\': 39613300Ssam dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 39713300Ssam c = *str++; 39813300Ssam nextc: 39913300Ssam if (*dp++ == c) { 40013300Ssam c = *dp++; 40113300Ssam break; 40213300Ssam } 40313300Ssam dp++; 40413300Ssam if (*dp) 40513300Ssam goto nextc; 40613300Ssam if (isdigit(c)) { 40713300Ssam c -= '0', i = 2; 40813300Ssam do 40913300Ssam c <<= 3, c |= *str++ - '0'; 41013300Ssam while (--i && isdigit(*str)); 41113300Ssam } 41213300Ssam break; 41313300Ssam } 41413300Ssam *cp++ = c; 41513300Ssam } 41613300Ssam *cp++ = 0; 41713300Ssam str = *area; 41813300Ssam *area = cp; 41913300Ssam return (str); 42013300Ssam } 421