1*13954Ssam #ifndef lint 2*13954Ssam static char sccsid[] = "@(#)printcap.c 1.6 (Berkeley) 07/17/83"; 3*13954Ssam #endif 4*13954Ssam 53678Sroot /* Copyright (c) 1979 Regents of the University of California */ 63841Ssam #define BUFSIZ 1024 73841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 83678Sroot 93678Sroot #include <ctype.h> 1012429Sralph #include <stdio.h> 113678Sroot /* 123841Ssam * termcap - routines for dealing with the terminal capability data base 133678Sroot * 143678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 153678Sroot * for capabilities alphabetically would not be a n**2/2 163678Sroot * process when large numbers of capabilities are given. 173841Ssam * Note: If we add a last pointer now we will screw up the 183841Ssam * tc capability. We really should compile termcap. 193678Sroot * 203678Sroot * Essentially all the work here is scanning and decoding escapes 213678Sroot * in string capabilities. We don't use stdio because the editor 223678Sroot * doesn't, and because living w/o it is not hard. 233678Sroot */ 243678Sroot 253841Ssam #define PRINTCAP 263678Sroot 273841Ssam #ifdef PRINTCAP 283841Ssam #define tgetent pgetent 293841Ssam #define tskip pskip 303841Ssam #define tgetstr pgetstr 313841Ssam #define tdecode pdecode 323841Ssam #define tgetnum pgetnum 333841Ssam #define tgetflag pgetflag 343841Ssam #define tdecode pdecode 353841Ssam #define tnchktc pnchktc 363841Ssam #define tnamatch pnamatch 373841Ssam #undef E_TERMCAP 383841Ssam #define E_TERMCAP "/etc/printcap" 393841Ssam #define V6 403841Ssam #endif 413841Ssam 4212429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 433841Ssam static char *tbuf; 4412429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 453841Ssam char *tskip(); 463841Ssam char *tgetstr(); 473841Ssam char *tdecode(); 483841Ssam char *getenv(); 493841Ssam 503678Sroot /* 5112429Sralph * Similar to tgetent except it returns the next enrty instead of 5212429Sralph * doing a lookup. 5312429Sralph */ 5412429Sralph getprent(bp) 5512429Sralph register char *bp; 5612429Sralph { 5712429Sralph register int c, skip = 0; 5812429Sralph 5912429Sralph if (pfp == NULL && (pfp = fopen(E_TERMCAP, "r")) == NULL) 6012429Sralph return(-1); 6112429Sralph tbuf = bp; 6212429Sralph for (;;) { 6312429Sralph switch (c = getc(pfp)) { 6412429Sralph case EOF: 6512429Sralph fclose(pfp); 6612429Sralph pfp = NULL; 6712429Sralph return(0); 6812429Sralph case '\n': 6912429Sralph if (bp == tbuf) { 7012429Sralph skip = 0; 7112429Sralph continue; 7212429Sralph } 7312429Sralph if (bp[-1] == '\\') { 7412429Sralph bp--; 7512429Sralph continue; 7612429Sralph } 7712429Sralph *bp = '\0'; 7812429Sralph return(1); 7912429Sralph case '#': 8012429Sralph if (bp == tbuf) 8112429Sralph skip++; 8212429Sralph default: 8312429Sralph if (skip) 8412429Sralph continue; 8512429Sralph if (bp >= tbuf+BUFSIZ) { 8612429Sralph write(2, "Termcap entry too long\n", 23); 8712429Sralph *bp = '\0'; 8812429Sralph return(1); 8912429Sralph } 9012429Sralph *bp++ = c; 9112429Sralph } 9212429Sralph } 9312429Sralph } 9412429Sralph 9512429Sralph endprent() 9612429Sralph { 9712429Sralph if (pfp != NULL) 9812429Sralph fclose(pfp); 9912429Sralph } 10012429Sralph 10112429Sralph /* 1023841Ssam * Get an entry for terminal name in buffer bp, 1033841Ssam * from the termcap file. Parse is very rudimentary; 1043678Sroot * we just notice escaped newlines. 1053678Sroot */ 1063841Ssam tgetent(bp, name) 1073678Sroot char *bp, *name; 1083678Sroot { 1093678Sroot register char *cp; 1103678Sroot register int c; 1113678Sroot register int i = 0, cnt = 0; 1123678Sroot char ibuf[BUFSIZ]; 1133841Ssam char *cp2; 1143678Sroot int tf; 1153678Sroot 1163841Ssam tbuf = bp; 1173841Ssam tf = 0; 1183841Ssam #ifndef V6 1193841Ssam cp = getenv("TERMCAP"); 1203841Ssam /* 1213841Ssam * TERMCAP can have one of two things in it. It can be the 1223841Ssam * name of a file to use instead of /etc/termcap. In this 1233841Ssam * case it better start with a "/". Or it can be an entry to 1243841Ssam * use so we don't have to read the file. In this case it 1253841Ssam * has to already have the newlines crunched out. 1263841Ssam */ 1273841Ssam if (cp && *cp) { 1283841Ssam if (*cp!='/') { 1293841Ssam cp2 = getenv("TERM"); 1303841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1313841Ssam strcpy(bp,cp); 1323841Ssam return(tnchktc()); 1333841Ssam } else { 1343841Ssam tf = open(E_TERMCAP, 0); 1353841Ssam } 1363841Ssam } else 1373841Ssam tf = open(cp, 0); 1383841Ssam } 1393841Ssam if (tf==0) 1403841Ssam tf = open(E_TERMCAP, 0); 1413841Ssam #else 1423841Ssam tf = open(E_TERMCAP, 0); 1433841Ssam #endif 1443678Sroot if (tf < 0) 1453678Sroot return (-1); 1463678Sroot for (;;) { 1473678Sroot cp = bp; 1483678Sroot for (;;) { 1493678Sroot if (i == cnt) { 1503678Sroot cnt = read(tf, ibuf, BUFSIZ); 1513678Sroot if (cnt <= 0) { 1523678Sroot close(tf); 1533678Sroot return (0); 1543678Sroot } 1553678Sroot i = 0; 1563678Sroot } 1573678Sroot c = ibuf[i++]; 1583678Sroot if (c == '\n') { 1593678Sroot if (cp > bp && cp[-1] == '\\'){ 1603678Sroot cp--; 1613678Sroot continue; 1623678Sroot } 1633678Sroot break; 1643678Sroot } 1653841Ssam if (cp >= bp+BUFSIZ) { 1663841Ssam write(2,"Termcap entry too long\n", 23); 1673841Ssam break; 1683841Ssam } else 1693841Ssam *cp++ = c; 1703678Sroot } 1713678Sroot *cp = 0; 1723678Sroot 1733678Sroot /* 1743678Sroot * The real work for the match. 1753678Sroot */ 1763841Ssam if (tnamatch(name)) { 1773678Sroot close(tf); 1783841Ssam return(tnchktc()); 1793678Sroot } 1803678Sroot } 1813678Sroot } 1823678Sroot 1833678Sroot /* 1843841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1853841Ssam * recursively find xxx and append that entry (minus the names) 1863841Ssam * to take the place of the tc=xxx entry. This allows termcap 1873841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1883841Ssam * Note that this works because of the left to right scan. 1893841Ssam */ 1903841Ssam tnchktc() 1913841Ssam { 1923841Ssam register char *p, *q; 1933841Ssam char tcname[16]; /* name of similar terminal */ 1943841Ssam char tcbuf[BUFSIZ]; 1953841Ssam char *holdtbuf = tbuf; 1963841Ssam int l; 1973841Ssam 1983841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 1993841Ssam while (*--p != ':') 2003841Ssam if (p<tbuf) { 2013841Ssam write(2, "Bad termcap entry\n", 18); 2023841Ssam return (0); 2033841Ssam } 2043841Ssam p++; 2053841Ssam /* p now points to beginning of last field */ 2063841Ssam if (p[0] != 't' || p[1] != 'c') 2073841Ssam return(1); 2083841Ssam strcpy(tcname,p+3); 2093841Ssam q = tcname; 2103841Ssam while (q && *q != ':') 2113841Ssam q++; 2123841Ssam *q = 0; 2133841Ssam if (++hopcount > MAXHOP) { 2143841Ssam write(2, "Infinite tc= loop\n", 18); 2153841Ssam return (0); 2163841Ssam } 2173841Ssam if (tgetent(tcbuf, tcname) != 1) 2183841Ssam return(0); 2193841Ssam for (q=tcbuf; *q != ':'; q++) 2203841Ssam ; 2213841Ssam l = p - holdtbuf + strlen(q); 2223841Ssam if (l > BUFSIZ) { 2233841Ssam write(2, "Termcap entry too long\n", 23); 2243841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2253841Ssam } 2263841Ssam strcpy(p, q+1); 2273841Ssam tbuf = holdtbuf; 2283841Ssam return(1); 2293841Ssam } 2303841Ssam 2313841Ssam /* 2323841Ssam * Tnamatch deals with name matching. The first field of the termcap 2333678Sroot * entry is a sequence of names separated by |'s, so we compare 2343678Sroot * against each such name. The normal : terminator after the last 2353678Sroot * name (before the first field) stops us. 2363678Sroot */ 2373841Ssam tnamatch(np) 2383678Sroot char *np; 2393678Sroot { 2403678Sroot register char *Np, *Bp; 2413678Sroot 2423841Ssam Bp = tbuf; 2433841Ssam if (*Bp == '#') 2443841Ssam return(0); 2453678Sroot for (;;) { 2463678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2473678Sroot continue; 2483678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2493678Sroot return (1); 2503678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2513678Sroot Bp++; 2523678Sroot if (*Bp == 0 || *Bp == ':') 2533678Sroot return (0); 2543678Sroot Bp++; 2553678Sroot } 2563678Sroot } 2573678Sroot 2583678Sroot /* 2593678Sroot * Skip to the next field. Notice that this is very dumb, not 2603678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2613841Ssam * into the termcap file in octal. 2623678Sroot */ 2633678Sroot static char * 2643841Ssam tskip(bp) 2653678Sroot register char *bp; 2663678Sroot { 2673678Sroot 2683678Sroot while (*bp && *bp != ':') 2693678Sroot bp++; 2703678Sroot if (*bp == ':') 2713678Sroot bp++; 2723678Sroot return (bp); 2733678Sroot } 2743678Sroot 2753678Sroot /* 2763678Sroot * Return the (numeric) option id. 2773678Sroot * Numeric options look like 2783678Sroot * li#80 2793678Sroot * i.e. the option string is separated from the numeric value by 2803678Sroot * a # character. If the option is not found we return -1. 2813678Sroot * Note that we handle octal numbers beginning with 0. 2823678Sroot */ 2833841Ssam tgetnum(id) 2843678Sroot char *id; 2853678Sroot { 2863678Sroot register int i, base; 2873841Ssam register char *bp = tbuf; 2883678Sroot 2893678Sroot for (;;) { 2903841Ssam bp = tskip(bp); 2913678Sroot if (*bp == 0) 2923678Sroot return (-1); 2933678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 2943678Sroot continue; 2953841Ssam if (*bp == '@') 2963841Ssam return(-1); 2973678Sroot if (*bp != '#') 2983678Sroot continue; 2993678Sroot bp++; 3003678Sroot base = 10; 3013678Sroot if (*bp == '0') 3023678Sroot base = 8; 3033678Sroot i = 0; 3043678Sroot while (isdigit(*bp)) 3053678Sroot i *= base, i += *bp++ - '0'; 3063678Sroot return (i); 3073678Sroot } 3083678Sroot } 3093678Sroot 3103678Sroot /* 3113678Sroot * Handle a flag option. 3123678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3133678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3143678Sroot * not given. 3153678Sroot */ 3163841Ssam tgetflag(id) 3173678Sroot char *id; 3183678Sroot { 3193841Ssam register char *bp = tbuf; 3203678Sroot 3213678Sroot for (;;) { 3223841Ssam bp = tskip(bp); 3233678Sroot if (!*bp) 3243678Sroot return (0); 3253841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3263841Ssam if (!*bp || *bp == ':') 3273841Ssam return (1); 3283841Ssam else if (*bp == '@') 3293841Ssam return(0); 3303841Ssam } 3313678Sroot } 3323678Sroot } 3333678Sroot 3343678Sroot /* 3353678Sroot * Get a string valued option. 3363678Sroot * These are given as 3373678Sroot * cl=^Z 3383678Sroot * Much decoding is done on the strings, and the strings are 3393678Sroot * placed in area, which is a ref parameter which is updated. 3403678Sroot * No checking on area overflow. 3413678Sroot */ 3423678Sroot char * 3433841Ssam tgetstr(id, area) 3443678Sroot char *id, **area; 3453678Sroot { 3463841Ssam register char *bp = tbuf; 3473678Sroot 3483678Sroot for (;;) { 3493841Ssam bp = tskip(bp); 3503678Sroot if (!*bp) 3513678Sroot return (0); 3523678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3533678Sroot continue; 3543841Ssam if (*bp == '@') 3553841Ssam return(0); 3563678Sroot if (*bp != '=') 3573678Sroot continue; 3583678Sroot bp++; 3593841Ssam return (tdecode(bp, area)); 3603678Sroot } 3613678Sroot } 3623678Sroot 3633678Sroot /* 3643678Sroot * Tdecode does the grung work to decode the 3653678Sroot * string capability escapes. 3663678Sroot */ 3673678Sroot static char * 3683841Ssam tdecode(str, area) 3693678Sroot register char *str; 3703678Sroot char **area; 3713678Sroot { 3723678Sroot register char *cp; 3733678Sroot register int c; 3743678Sroot register char *dp; 3753678Sroot int i; 3763678Sroot 3773678Sroot cp = *area; 3783678Sroot while ((c = *str++) && c != ':') { 3793678Sroot switch (c) { 3803678Sroot 3813678Sroot case '^': 3823678Sroot c = *str++ & 037; 3833678Sroot break; 3843678Sroot 3853678Sroot case '\\': 3863678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3873678Sroot c = *str++; 3883678Sroot nextc: 3893678Sroot if (*dp++ == c) { 3903678Sroot c = *dp++; 3913678Sroot break; 3923678Sroot } 3933678Sroot dp++; 3943678Sroot if (*dp) 3953678Sroot goto nextc; 3963678Sroot if (isdigit(c)) { 3973678Sroot c -= '0', i = 2; 3983678Sroot do 3993678Sroot c <<= 3, c |= *str++ - '0'; 4003678Sroot while (--i && isdigit(*str)); 4013678Sroot } 4023678Sroot break; 4033678Sroot } 4043678Sroot *cp++ = c; 4053678Sroot } 4063678Sroot *cp++ = 0; 4073678Sroot str = *area; 4083678Sroot *area = cp; 4093678Sroot return (str); 4103678Sroot } 411