1*12429Sralph /* printcap.c 1.5 83/05/13 */ 23678Sroot /* Copyright (c) 1979 Regents of the University of California */ 33841Ssam #define BUFSIZ 1024 43841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 53678Sroot 63678Sroot #include <ctype.h> 7*12429Sralph #include <stdio.h> 83678Sroot /* 93841Ssam * termcap - routines for dealing with the terminal capability data base 103678Sroot * 113678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 123678Sroot * for capabilities alphabetically would not be a n**2/2 133678Sroot * process when large numbers of capabilities are given. 143841Ssam * Note: If we add a last pointer now we will screw up the 153841Ssam * tc capability. We really should compile termcap. 163678Sroot * 173678Sroot * Essentially all the work here is scanning and decoding escapes 183678Sroot * in string capabilities. We don't use stdio because the editor 193678Sroot * doesn't, and because living w/o it is not hard. 203678Sroot */ 213678Sroot 223841Ssam #define PRINTCAP 233678Sroot 243841Ssam #ifdef PRINTCAP 253841Ssam #define tgetent pgetent 263841Ssam #define tskip pskip 273841Ssam #define tgetstr pgetstr 283841Ssam #define tdecode pdecode 293841Ssam #define tgetnum pgetnum 303841Ssam #define tgetflag pgetflag 313841Ssam #define tdecode pdecode 323841Ssam #define tnchktc pnchktc 333841Ssam #define tnamatch pnamatch 343841Ssam #undef E_TERMCAP 353841Ssam #define E_TERMCAP "/etc/printcap" 363841Ssam #define V6 373841Ssam #endif 383841Ssam 39*12429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 403841Ssam static char *tbuf; 41*12429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 423841Ssam char *tskip(); 433841Ssam char *tgetstr(); 443841Ssam char *tdecode(); 453841Ssam char *getenv(); 463841Ssam 473678Sroot /* 48*12429Sralph * Similar to tgetent except it returns the next enrty instead of 49*12429Sralph * doing a lookup. 50*12429Sralph */ 51*12429Sralph getprent(bp) 52*12429Sralph register char *bp; 53*12429Sralph { 54*12429Sralph register int c, skip = 0; 55*12429Sralph 56*12429Sralph if (pfp == NULL && (pfp = fopen(E_TERMCAP, "r")) == NULL) 57*12429Sralph return(-1); 58*12429Sralph tbuf = bp; 59*12429Sralph for (;;) { 60*12429Sralph switch (c = getc(pfp)) { 61*12429Sralph case EOF: 62*12429Sralph fclose(pfp); 63*12429Sralph pfp = NULL; 64*12429Sralph return(0); 65*12429Sralph case '\n': 66*12429Sralph if (bp == tbuf) { 67*12429Sralph skip = 0; 68*12429Sralph continue; 69*12429Sralph } 70*12429Sralph if (bp[-1] == '\\') { 71*12429Sralph bp--; 72*12429Sralph continue; 73*12429Sralph } 74*12429Sralph *bp = '\0'; 75*12429Sralph return(1); 76*12429Sralph case '#': 77*12429Sralph if (bp == tbuf) 78*12429Sralph skip++; 79*12429Sralph default: 80*12429Sralph if (skip) 81*12429Sralph continue; 82*12429Sralph if (bp >= tbuf+BUFSIZ) { 83*12429Sralph write(2, "Termcap entry too long\n", 23); 84*12429Sralph *bp = '\0'; 85*12429Sralph return(1); 86*12429Sralph } 87*12429Sralph *bp++ = c; 88*12429Sralph } 89*12429Sralph } 90*12429Sralph } 91*12429Sralph 92*12429Sralph endprent() 93*12429Sralph { 94*12429Sralph if (pfp != NULL) 95*12429Sralph fclose(pfp); 96*12429Sralph } 97*12429Sralph 98*12429Sralph /* 993841Ssam * Get an entry for terminal name in buffer bp, 1003841Ssam * from the termcap file. Parse is very rudimentary; 1013678Sroot * we just notice escaped newlines. 1023678Sroot */ 1033841Ssam tgetent(bp, name) 1043678Sroot char *bp, *name; 1053678Sroot { 1063678Sroot register char *cp; 1073678Sroot register int c; 1083678Sroot register int i = 0, cnt = 0; 1093678Sroot char ibuf[BUFSIZ]; 1103841Ssam char *cp2; 1113678Sroot int tf; 1123678Sroot 1133841Ssam tbuf = bp; 1143841Ssam tf = 0; 1153841Ssam #ifndef V6 1163841Ssam cp = getenv("TERMCAP"); 1173841Ssam /* 1183841Ssam * TERMCAP can have one of two things in it. It can be the 1193841Ssam * name of a file to use instead of /etc/termcap. In this 1203841Ssam * case it better start with a "/". Or it can be an entry to 1213841Ssam * use so we don't have to read the file. In this case it 1223841Ssam * has to already have the newlines crunched out. 1233841Ssam */ 1243841Ssam if (cp && *cp) { 1253841Ssam if (*cp!='/') { 1263841Ssam cp2 = getenv("TERM"); 1273841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1283841Ssam strcpy(bp,cp); 1293841Ssam return(tnchktc()); 1303841Ssam } else { 1313841Ssam tf = open(E_TERMCAP, 0); 1323841Ssam } 1333841Ssam } else 1343841Ssam tf = open(cp, 0); 1353841Ssam } 1363841Ssam if (tf==0) 1373841Ssam tf = open(E_TERMCAP, 0); 1383841Ssam #else 1393841Ssam tf = open(E_TERMCAP, 0); 1403841Ssam #endif 1413678Sroot if (tf < 0) 1423678Sroot return (-1); 1433678Sroot for (;;) { 1443678Sroot cp = bp; 1453678Sroot for (;;) { 1463678Sroot if (i == cnt) { 1473678Sroot cnt = read(tf, ibuf, BUFSIZ); 1483678Sroot if (cnt <= 0) { 1493678Sroot close(tf); 1503678Sroot return (0); 1513678Sroot } 1523678Sroot i = 0; 1533678Sroot } 1543678Sroot c = ibuf[i++]; 1553678Sroot if (c == '\n') { 1563678Sroot if (cp > bp && cp[-1] == '\\'){ 1573678Sroot cp--; 1583678Sroot continue; 1593678Sroot } 1603678Sroot break; 1613678Sroot } 1623841Ssam if (cp >= bp+BUFSIZ) { 1633841Ssam write(2,"Termcap entry too long\n", 23); 1643841Ssam break; 1653841Ssam } else 1663841Ssam *cp++ = c; 1673678Sroot } 1683678Sroot *cp = 0; 1693678Sroot 1703678Sroot /* 1713678Sroot * The real work for the match. 1723678Sroot */ 1733841Ssam if (tnamatch(name)) { 1743678Sroot close(tf); 1753841Ssam return(tnchktc()); 1763678Sroot } 1773678Sroot } 1783678Sroot } 1793678Sroot 1803678Sroot /* 1813841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1823841Ssam * recursively find xxx and append that entry (minus the names) 1833841Ssam * to take the place of the tc=xxx entry. This allows termcap 1843841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1853841Ssam * Note that this works because of the left to right scan. 1863841Ssam */ 1873841Ssam tnchktc() 1883841Ssam { 1893841Ssam register char *p, *q; 1903841Ssam char tcname[16]; /* name of similar terminal */ 1913841Ssam char tcbuf[BUFSIZ]; 1923841Ssam char *holdtbuf = tbuf; 1933841Ssam int l; 1943841Ssam 1953841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 1963841Ssam while (*--p != ':') 1973841Ssam if (p<tbuf) { 1983841Ssam write(2, "Bad termcap entry\n", 18); 1993841Ssam return (0); 2003841Ssam } 2013841Ssam p++; 2023841Ssam /* p now points to beginning of last field */ 2033841Ssam if (p[0] != 't' || p[1] != 'c') 2043841Ssam return(1); 2053841Ssam strcpy(tcname,p+3); 2063841Ssam q = tcname; 2073841Ssam while (q && *q != ':') 2083841Ssam q++; 2093841Ssam *q = 0; 2103841Ssam if (++hopcount > MAXHOP) { 2113841Ssam write(2, "Infinite tc= loop\n", 18); 2123841Ssam return (0); 2133841Ssam } 2143841Ssam if (tgetent(tcbuf, tcname) != 1) 2153841Ssam return(0); 2163841Ssam for (q=tcbuf; *q != ':'; q++) 2173841Ssam ; 2183841Ssam l = p - holdtbuf + strlen(q); 2193841Ssam if (l > BUFSIZ) { 2203841Ssam write(2, "Termcap entry too long\n", 23); 2213841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2223841Ssam } 2233841Ssam strcpy(p, q+1); 2243841Ssam tbuf = holdtbuf; 2253841Ssam return(1); 2263841Ssam } 2273841Ssam 2283841Ssam /* 2293841Ssam * Tnamatch deals with name matching. The first field of the termcap 2303678Sroot * entry is a sequence of names separated by |'s, so we compare 2313678Sroot * against each such name. The normal : terminator after the last 2323678Sroot * name (before the first field) stops us. 2333678Sroot */ 2343841Ssam tnamatch(np) 2353678Sroot char *np; 2363678Sroot { 2373678Sroot register char *Np, *Bp; 2383678Sroot 2393841Ssam Bp = tbuf; 2403841Ssam if (*Bp == '#') 2413841Ssam return(0); 2423678Sroot for (;;) { 2433678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2443678Sroot continue; 2453678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2463678Sroot return (1); 2473678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2483678Sroot Bp++; 2493678Sroot if (*Bp == 0 || *Bp == ':') 2503678Sroot return (0); 2513678Sroot Bp++; 2523678Sroot } 2533678Sroot } 2543678Sroot 2553678Sroot /* 2563678Sroot * Skip to the next field. Notice that this is very dumb, not 2573678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2583841Ssam * into the termcap file in octal. 2593678Sroot */ 2603678Sroot static char * 2613841Ssam tskip(bp) 2623678Sroot register char *bp; 2633678Sroot { 2643678Sroot 2653678Sroot while (*bp && *bp != ':') 2663678Sroot bp++; 2673678Sroot if (*bp == ':') 2683678Sroot bp++; 2693678Sroot return (bp); 2703678Sroot } 2713678Sroot 2723678Sroot /* 2733678Sroot * Return the (numeric) option id. 2743678Sroot * Numeric options look like 2753678Sroot * li#80 2763678Sroot * i.e. the option string is separated from the numeric value by 2773678Sroot * a # character. If the option is not found we return -1. 2783678Sroot * Note that we handle octal numbers beginning with 0. 2793678Sroot */ 2803841Ssam tgetnum(id) 2813678Sroot char *id; 2823678Sroot { 2833678Sroot register int i, base; 2843841Ssam register char *bp = tbuf; 2853678Sroot 2863678Sroot for (;;) { 2873841Ssam bp = tskip(bp); 2883678Sroot if (*bp == 0) 2893678Sroot return (-1); 2903678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 2913678Sroot continue; 2923841Ssam if (*bp == '@') 2933841Ssam return(-1); 2943678Sroot if (*bp != '#') 2953678Sroot continue; 2963678Sroot bp++; 2973678Sroot base = 10; 2983678Sroot if (*bp == '0') 2993678Sroot base = 8; 3003678Sroot i = 0; 3013678Sroot while (isdigit(*bp)) 3023678Sroot i *= base, i += *bp++ - '0'; 3033678Sroot return (i); 3043678Sroot } 3053678Sroot } 3063678Sroot 3073678Sroot /* 3083678Sroot * Handle a flag option. 3093678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3103678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3113678Sroot * not given. 3123678Sroot */ 3133841Ssam tgetflag(id) 3143678Sroot char *id; 3153678Sroot { 3163841Ssam register char *bp = tbuf; 3173678Sroot 3183678Sroot for (;;) { 3193841Ssam bp = tskip(bp); 3203678Sroot if (!*bp) 3213678Sroot return (0); 3223841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3233841Ssam if (!*bp || *bp == ':') 3243841Ssam return (1); 3253841Ssam else if (*bp == '@') 3263841Ssam return(0); 3273841Ssam } 3283678Sroot } 3293678Sroot } 3303678Sroot 3313678Sroot /* 3323678Sroot * Get a string valued option. 3333678Sroot * These are given as 3343678Sroot * cl=^Z 3353678Sroot * Much decoding is done on the strings, and the strings are 3363678Sroot * placed in area, which is a ref parameter which is updated. 3373678Sroot * No checking on area overflow. 3383678Sroot */ 3393678Sroot char * 3403841Ssam tgetstr(id, area) 3413678Sroot char *id, **area; 3423678Sroot { 3433841Ssam register char *bp = tbuf; 3443678Sroot 3453678Sroot for (;;) { 3463841Ssam bp = tskip(bp); 3473678Sroot if (!*bp) 3483678Sroot return (0); 3493678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3503678Sroot continue; 3513841Ssam if (*bp == '@') 3523841Ssam return(0); 3533678Sroot if (*bp != '=') 3543678Sroot continue; 3553678Sroot bp++; 3563841Ssam return (tdecode(bp, area)); 3573678Sroot } 3583678Sroot } 3593678Sroot 3603678Sroot /* 3613678Sroot * Tdecode does the grung work to decode the 3623678Sroot * string capability escapes. 3633678Sroot */ 3643678Sroot static char * 3653841Ssam tdecode(str, area) 3663678Sroot register char *str; 3673678Sroot char **area; 3683678Sroot { 3693678Sroot register char *cp; 3703678Sroot register int c; 3713678Sroot register char *dp; 3723678Sroot int i; 3733678Sroot 3743678Sroot cp = *area; 3753678Sroot while ((c = *str++) && c != ':') { 3763678Sroot switch (c) { 3773678Sroot 3783678Sroot case '^': 3793678Sroot c = *str++ & 037; 3803678Sroot break; 3813678Sroot 3823678Sroot case '\\': 3833678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3843678Sroot c = *str++; 3853678Sroot nextc: 3863678Sroot if (*dp++ == c) { 3873678Sroot c = *dp++; 3883678Sroot break; 3893678Sroot } 3903678Sroot dp++; 3913678Sroot if (*dp) 3923678Sroot goto nextc; 3933678Sroot if (isdigit(c)) { 3943678Sroot c -= '0', i = 2; 3953678Sroot do 3963678Sroot c <<= 3, c |= *str++ - '0'; 3973678Sroot while (--i && isdigit(*str)); 3983678Sroot } 3993678Sroot break; 4003678Sroot } 4013678Sroot *cp++ = c; 4023678Sroot } 4033678Sroot *cp++ = 0; 4043678Sroot str = *area; 4053678Sroot *area = cp; 4063678Sroot return (str); 4073678Sroot } 408