122436Sdist /* 222436Sdist * Copyright (c) 1983 Regents of the University of California. 334203Sbostic * All rights reserved. 434203Sbostic * 542801Sbostic * %sccs.include.redist.c% 622436Sdist */ 722436Sdist 813954Ssam #ifndef lint 9*46978Sbostic static char sccsid[] = "@(#)printcap.c 5.7 (Berkeley) 03/04/91"; 1034203Sbostic #endif /* not lint */ 1113954Ssam 12*46978Sbostic #include <ctype.h> 13*46978Sbostic #include <stdio.h> 14*46978Sbostic #include "pathnames.h" 15*46978Sbostic 1646300Sbostic #ifndef BUFSIZ 173841Ssam #define BUFSIZ 1024 1846300Sbostic #endif 193841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 203678Sroot 213678Sroot /* 223841Ssam * termcap - routines for dealing with the terminal capability data base 233678Sroot * 243678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 253678Sroot * for capabilities alphabetically would not be a n**2/2 263678Sroot * process when large numbers of capabilities are given. 273841Ssam * Note: If we add a last pointer now we will screw up the 283841Ssam * tc capability. We really should compile termcap. 293678Sroot * 303678Sroot * Essentially all the work here is scanning and decoding escapes 313678Sroot * in string capabilities. We don't use stdio because the editor 323678Sroot * doesn't, and because living w/o it is not hard. 333678Sroot */ 343678Sroot 353841Ssam #define PRINTCAP 363678Sroot 373841Ssam #ifdef PRINTCAP 383841Ssam #define tgetent pgetent 393841Ssam #define tskip pskip 403841Ssam #define tgetstr pgetstr 413841Ssam #define tdecode pdecode 423841Ssam #define tgetnum pgetnum 433841Ssam #define tgetflag pgetflag 443841Ssam #define tdecode pdecode 453841Ssam #define tnchktc pnchktc 463841Ssam #define tnamatch pnamatch 473841Ssam #define V6 483841Ssam #endif 493841Ssam 5012429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 513841Ssam static char *tbuf; 5212429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 533841Ssam char *tskip(); 543841Ssam char *tgetstr(); 553841Ssam char *tdecode(); 563841Ssam char *getenv(); 573841Ssam 583678Sroot /* 5912429Sralph * Similar to tgetent except it returns the next enrty instead of 6012429Sralph * doing a lookup. 6112429Sralph */ 6212429Sralph getprent(bp) 6312429Sralph register char *bp; 6412429Sralph { 6512429Sralph register int c, skip = 0; 6612429Sralph 6737968Sbostic if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL) 6812429Sralph return(-1); 6912429Sralph tbuf = bp; 7012429Sralph for (;;) { 7112429Sralph switch (c = getc(pfp)) { 7212429Sralph case EOF: 7312429Sralph fclose(pfp); 7412429Sralph pfp = NULL; 7512429Sralph return(0); 7612429Sralph case '\n': 7712429Sralph if (bp == tbuf) { 7812429Sralph skip = 0; 7912429Sralph continue; 8012429Sralph } 8112429Sralph if (bp[-1] == '\\') { 8212429Sralph bp--; 8312429Sralph continue; 8412429Sralph } 8512429Sralph *bp = '\0'; 8612429Sralph return(1); 8712429Sralph case '#': 8812429Sralph if (bp == tbuf) 8912429Sralph skip++; 9012429Sralph default: 9112429Sralph if (skip) 9212429Sralph continue; 9312429Sralph if (bp >= tbuf+BUFSIZ) { 9412429Sralph write(2, "Termcap entry too long\n", 23); 9512429Sralph *bp = '\0'; 9612429Sralph return(1); 9712429Sralph } 9812429Sralph *bp++ = c; 9912429Sralph } 10012429Sralph } 10112429Sralph } 10212429Sralph 10312429Sralph endprent() 10412429Sralph { 10512429Sralph if (pfp != NULL) 10612429Sralph fclose(pfp); 10712429Sralph } 10812429Sralph 10912429Sralph /* 1103841Ssam * Get an entry for terminal name in buffer bp, 1113841Ssam * from the termcap file. Parse is very rudimentary; 1123678Sroot * we just notice escaped newlines. 1133678Sroot */ 1143841Ssam tgetent(bp, name) 1153678Sroot char *bp, *name; 1163678Sroot { 1173678Sroot register char *cp; 1183678Sroot register int c; 1193678Sroot register int i = 0, cnt = 0; 1203678Sroot char ibuf[BUFSIZ]; 1213841Ssam char *cp2; 1223678Sroot int tf; 1233678Sroot 1243841Ssam tbuf = bp; 1253841Ssam tf = 0; 1263841Ssam #ifndef V6 1273841Ssam cp = getenv("TERMCAP"); 1283841Ssam /* 1293841Ssam * TERMCAP can have one of two things in it. It can be the 1303841Ssam * name of a file to use instead of /etc/termcap. In this 1313841Ssam * case it better start with a "/". Or it can be an entry to 1323841Ssam * use so we don't have to read the file. In this case it 1333841Ssam * has to already have the newlines crunched out. 1343841Ssam */ 1353841Ssam if (cp && *cp) { 1363841Ssam if (*cp!='/') { 1373841Ssam cp2 = getenv("TERM"); 1383841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1393841Ssam strcpy(bp,cp); 1403841Ssam return(tnchktc()); 1413841Ssam } else { 14237968Sbostic tf = open(_PATH_PRINTCAP, 0); 1433841Ssam } 1443841Ssam } else 1453841Ssam tf = open(cp, 0); 1463841Ssam } 1473841Ssam if (tf==0) 14837968Sbostic tf = open(_PATH_PRINTCAP, 0); 1493841Ssam #else 15037968Sbostic tf = open(_PATH_PRINTCAP, 0); 1513841Ssam #endif 1523678Sroot if (tf < 0) 1533678Sroot return (-1); 1543678Sroot for (;;) { 1553678Sroot cp = bp; 1563678Sroot for (;;) { 1573678Sroot if (i == cnt) { 1583678Sroot cnt = read(tf, ibuf, BUFSIZ); 1593678Sroot if (cnt <= 0) { 1603678Sroot close(tf); 1613678Sroot return (0); 1623678Sroot } 1633678Sroot i = 0; 1643678Sroot } 1653678Sroot c = ibuf[i++]; 1663678Sroot if (c == '\n') { 1673678Sroot if (cp > bp && cp[-1] == '\\'){ 1683678Sroot cp--; 1693678Sroot continue; 1703678Sroot } 1713678Sroot break; 1723678Sroot } 1733841Ssam if (cp >= bp+BUFSIZ) { 1743841Ssam write(2,"Termcap entry too long\n", 23); 1753841Ssam break; 1763841Ssam } else 1773841Ssam *cp++ = c; 1783678Sroot } 1793678Sroot *cp = 0; 1803678Sroot 1813678Sroot /* 1823678Sroot * The real work for the match. 1833678Sroot */ 1843841Ssam if (tnamatch(name)) { 1853678Sroot close(tf); 1863841Ssam return(tnchktc()); 1873678Sroot } 1883678Sroot } 1893678Sroot } 1903678Sroot 1913678Sroot /* 1923841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1933841Ssam * recursively find xxx and append that entry (minus the names) 1943841Ssam * to take the place of the tc=xxx entry. This allows termcap 1953841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1963841Ssam * Note that this works because of the left to right scan. 1973841Ssam */ 1983841Ssam tnchktc() 1993841Ssam { 2003841Ssam register char *p, *q; 2013841Ssam char tcname[16]; /* name of similar terminal */ 2023841Ssam char tcbuf[BUFSIZ]; 2033841Ssam char *holdtbuf = tbuf; 2043841Ssam int l; 2053841Ssam 2063841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2073841Ssam while (*--p != ':') 2083841Ssam if (p<tbuf) { 2093841Ssam write(2, "Bad termcap entry\n", 18); 2103841Ssam return (0); 2113841Ssam } 2123841Ssam p++; 2133841Ssam /* p now points to beginning of last field */ 2143841Ssam if (p[0] != 't' || p[1] != 'c') 2153841Ssam return(1); 2163841Ssam strcpy(tcname,p+3); 2173841Ssam q = tcname; 2183841Ssam while (q && *q != ':') 2193841Ssam q++; 2203841Ssam *q = 0; 2213841Ssam if (++hopcount > MAXHOP) { 2223841Ssam write(2, "Infinite tc= loop\n", 18); 2233841Ssam return (0); 2243841Ssam } 2253841Ssam if (tgetent(tcbuf, tcname) != 1) 2263841Ssam return(0); 2273841Ssam for (q=tcbuf; *q != ':'; q++) 2283841Ssam ; 2293841Ssam l = p - holdtbuf + strlen(q); 2303841Ssam if (l > BUFSIZ) { 2313841Ssam write(2, "Termcap entry too long\n", 23); 2323841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2333841Ssam } 2343841Ssam strcpy(p, q+1); 2353841Ssam tbuf = holdtbuf; 2363841Ssam return(1); 2373841Ssam } 2383841Ssam 2393841Ssam /* 2403841Ssam * Tnamatch deals with name matching. The first field of the termcap 2413678Sroot * entry is a sequence of names separated by |'s, so we compare 2423678Sroot * against each such name. The normal : terminator after the last 2433678Sroot * name (before the first field) stops us. 2443678Sroot */ 2453841Ssam tnamatch(np) 2463678Sroot char *np; 2473678Sroot { 2483678Sroot register char *Np, *Bp; 2493678Sroot 2503841Ssam Bp = tbuf; 2513841Ssam if (*Bp == '#') 2523841Ssam return(0); 2533678Sroot for (;;) { 2543678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2553678Sroot continue; 2563678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2573678Sroot return (1); 2583678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2593678Sroot Bp++; 2603678Sroot if (*Bp == 0 || *Bp == ':') 2613678Sroot return (0); 2623678Sroot Bp++; 2633678Sroot } 2643678Sroot } 2653678Sroot 2663678Sroot /* 2673678Sroot * Skip to the next field. Notice that this is very dumb, not 2683678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2693841Ssam * into the termcap file in octal. 2703678Sroot */ 2713678Sroot static char * 2723841Ssam tskip(bp) 2733678Sroot register char *bp; 2743678Sroot { 2753678Sroot 2763678Sroot while (*bp && *bp != ':') 2773678Sroot bp++; 2783678Sroot if (*bp == ':') 2793678Sroot bp++; 2803678Sroot return (bp); 2813678Sroot } 2823678Sroot 2833678Sroot /* 2843678Sroot * Return the (numeric) option id. 2853678Sroot * Numeric options look like 2863678Sroot * li#80 2873678Sroot * i.e. the option string is separated from the numeric value by 2883678Sroot * a # character. If the option is not found we return -1. 2893678Sroot * Note that we handle octal numbers beginning with 0. 2903678Sroot */ 2913841Ssam tgetnum(id) 2923678Sroot char *id; 2933678Sroot { 2943678Sroot register int i, base; 2953841Ssam register char *bp = tbuf; 2963678Sroot 2973678Sroot for (;;) { 2983841Ssam bp = tskip(bp); 2993678Sroot if (*bp == 0) 3003678Sroot return (-1); 3013678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3023678Sroot continue; 3033841Ssam if (*bp == '@') 3043841Ssam return(-1); 3053678Sroot if (*bp != '#') 3063678Sroot continue; 3073678Sroot bp++; 3083678Sroot base = 10; 3093678Sroot if (*bp == '0') 3103678Sroot base = 8; 3113678Sroot i = 0; 3123678Sroot while (isdigit(*bp)) 3133678Sroot i *= base, i += *bp++ - '0'; 3143678Sroot return (i); 3153678Sroot } 3163678Sroot } 3173678Sroot 3183678Sroot /* 3193678Sroot * Handle a flag option. 3203678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3213678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3223678Sroot * not given. 3233678Sroot */ 3243841Ssam tgetflag(id) 3253678Sroot char *id; 3263678Sroot { 3273841Ssam register char *bp = tbuf; 3283678Sroot 3293678Sroot for (;;) { 3303841Ssam bp = tskip(bp); 3313678Sroot if (!*bp) 3323678Sroot return (0); 3333841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3343841Ssam if (!*bp || *bp == ':') 3353841Ssam return (1); 3363841Ssam else if (*bp == '@') 3373841Ssam return(0); 3383841Ssam } 3393678Sroot } 3403678Sroot } 3413678Sroot 3423678Sroot /* 3433678Sroot * Get a string valued option. 3443678Sroot * These are given as 3453678Sroot * cl=^Z 3463678Sroot * Much decoding is done on the strings, and the strings are 3473678Sroot * placed in area, which is a ref parameter which is updated. 3483678Sroot * No checking on area overflow. 3493678Sroot */ 3503678Sroot char * 3513841Ssam tgetstr(id, area) 3523678Sroot char *id, **area; 3533678Sroot { 3543841Ssam register char *bp = tbuf; 3553678Sroot 3563678Sroot for (;;) { 3573841Ssam bp = tskip(bp); 3583678Sroot if (!*bp) 3593678Sroot return (0); 3603678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3613678Sroot continue; 3623841Ssam if (*bp == '@') 3633841Ssam return(0); 3643678Sroot if (*bp != '=') 3653678Sroot continue; 3663678Sroot bp++; 3673841Ssam return (tdecode(bp, area)); 3683678Sroot } 3693678Sroot } 3703678Sroot 3713678Sroot /* 3723678Sroot * Tdecode does the grung work to decode the 3733678Sroot * string capability escapes. 3743678Sroot */ 3753678Sroot static char * 3763841Ssam tdecode(str, area) 3773678Sroot register char *str; 3783678Sroot char **area; 3793678Sroot { 3803678Sroot register char *cp; 3813678Sroot register int c; 3823678Sroot register char *dp; 3833678Sroot int i; 3843678Sroot 3853678Sroot cp = *area; 3863678Sroot while ((c = *str++) && c != ':') { 3873678Sroot switch (c) { 3883678Sroot 3893678Sroot case '^': 3903678Sroot c = *str++ & 037; 3913678Sroot break; 3923678Sroot 3933678Sroot case '\\': 3943678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3953678Sroot c = *str++; 3963678Sroot nextc: 3973678Sroot if (*dp++ == c) { 3983678Sroot c = *dp++; 3993678Sroot break; 4003678Sroot } 4013678Sroot dp++; 4023678Sroot if (*dp) 4033678Sroot goto nextc; 4043678Sroot if (isdigit(c)) { 4053678Sroot c -= '0', i = 2; 4063678Sroot do 4073678Sroot c <<= 3, c |= *str++ - '0'; 4083678Sroot while (--i && isdigit(*str)); 4093678Sroot } 4103678Sroot break; 4113678Sroot } 4123678Sroot *cp++ = c; 4133678Sroot } 4143678Sroot *cp++ = 0; 4153678Sroot str = *area; 4163678Sroot *area = cp; 4173678Sroot return (str); 4183678Sroot } 419