122436Sdist /* 222436Sdist * Copyright (c) 1983 Regents of the University of California. 334203Sbostic * All rights reserved. 434203Sbostic * 5*42801Sbostic * %sccs.include.redist.c% 622436Sdist */ 722436Sdist 813954Ssam #ifndef lint 9*42801Sbostic static char sccsid[] = "@(#)printcap.c 5.5 (Berkeley) 06/01/90"; 1034203Sbostic #endif /* not lint */ 1113954Ssam 123841Ssam #define BUFSIZ 1024 133841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 143678Sroot 153678Sroot #include <ctype.h> 1612429Sralph #include <stdio.h> 1737968Sbostic #include "pathnames.h" 1837968Sbostic 193678Sroot /* 203841Ssam * termcap - routines for dealing with the terminal capability data base 213678Sroot * 223678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 233678Sroot * for capabilities alphabetically would not be a n**2/2 243678Sroot * process when large numbers of capabilities are given. 253841Ssam * Note: If we add a last pointer now we will screw up the 263841Ssam * tc capability. We really should compile termcap. 273678Sroot * 283678Sroot * Essentially all the work here is scanning and decoding escapes 293678Sroot * in string capabilities. We don't use stdio because the editor 303678Sroot * doesn't, and because living w/o it is not hard. 313678Sroot */ 323678Sroot 333841Ssam #define PRINTCAP 343678Sroot 353841Ssam #ifdef PRINTCAP 363841Ssam #define tgetent pgetent 373841Ssam #define tskip pskip 383841Ssam #define tgetstr pgetstr 393841Ssam #define tdecode pdecode 403841Ssam #define tgetnum pgetnum 413841Ssam #define tgetflag pgetflag 423841Ssam #define tdecode pdecode 433841Ssam #define tnchktc pnchktc 443841Ssam #define tnamatch pnamatch 453841Ssam #define V6 463841Ssam #endif 473841Ssam 4812429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 493841Ssam static char *tbuf; 5012429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 513841Ssam char *tskip(); 523841Ssam char *tgetstr(); 533841Ssam char *tdecode(); 543841Ssam char *getenv(); 553841Ssam 563678Sroot /* 5712429Sralph * Similar to tgetent except it returns the next enrty instead of 5812429Sralph * doing a lookup. 5912429Sralph */ 6012429Sralph getprent(bp) 6112429Sralph register char *bp; 6212429Sralph { 6312429Sralph register int c, skip = 0; 6412429Sralph 6537968Sbostic if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL) 6612429Sralph return(-1); 6712429Sralph tbuf = bp; 6812429Sralph for (;;) { 6912429Sralph switch (c = getc(pfp)) { 7012429Sralph case EOF: 7112429Sralph fclose(pfp); 7212429Sralph pfp = NULL; 7312429Sralph return(0); 7412429Sralph case '\n': 7512429Sralph if (bp == tbuf) { 7612429Sralph skip = 0; 7712429Sralph continue; 7812429Sralph } 7912429Sralph if (bp[-1] == '\\') { 8012429Sralph bp--; 8112429Sralph continue; 8212429Sralph } 8312429Sralph *bp = '\0'; 8412429Sralph return(1); 8512429Sralph case '#': 8612429Sralph if (bp == tbuf) 8712429Sralph skip++; 8812429Sralph default: 8912429Sralph if (skip) 9012429Sralph continue; 9112429Sralph if (bp >= tbuf+BUFSIZ) { 9212429Sralph write(2, "Termcap entry too long\n", 23); 9312429Sralph *bp = '\0'; 9412429Sralph return(1); 9512429Sralph } 9612429Sralph *bp++ = c; 9712429Sralph } 9812429Sralph } 9912429Sralph } 10012429Sralph 10112429Sralph endprent() 10212429Sralph { 10312429Sralph if (pfp != NULL) 10412429Sralph fclose(pfp); 10512429Sralph } 10612429Sralph 10712429Sralph /* 1083841Ssam * Get an entry for terminal name in buffer bp, 1093841Ssam * from the termcap file. Parse is very rudimentary; 1103678Sroot * we just notice escaped newlines. 1113678Sroot */ 1123841Ssam tgetent(bp, name) 1133678Sroot char *bp, *name; 1143678Sroot { 1153678Sroot register char *cp; 1163678Sroot register int c; 1173678Sroot register int i = 0, cnt = 0; 1183678Sroot char ibuf[BUFSIZ]; 1193841Ssam char *cp2; 1203678Sroot int tf; 1213678Sroot 1223841Ssam tbuf = bp; 1233841Ssam tf = 0; 1243841Ssam #ifndef V6 1253841Ssam cp = getenv("TERMCAP"); 1263841Ssam /* 1273841Ssam * TERMCAP can have one of two things in it. It can be the 1283841Ssam * name of a file to use instead of /etc/termcap. In this 1293841Ssam * case it better start with a "/". Or it can be an entry to 1303841Ssam * use so we don't have to read the file. In this case it 1313841Ssam * has to already have the newlines crunched out. 1323841Ssam */ 1333841Ssam if (cp && *cp) { 1343841Ssam if (*cp!='/') { 1353841Ssam cp2 = getenv("TERM"); 1363841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1373841Ssam strcpy(bp,cp); 1383841Ssam return(tnchktc()); 1393841Ssam } else { 14037968Sbostic tf = open(_PATH_PRINTCAP, 0); 1413841Ssam } 1423841Ssam } else 1433841Ssam tf = open(cp, 0); 1443841Ssam } 1453841Ssam if (tf==0) 14637968Sbostic tf = open(_PATH_PRINTCAP, 0); 1473841Ssam #else 14837968Sbostic tf = open(_PATH_PRINTCAP, 0); 1493841Ssam #endif 1503678Sroot if (tf < 0) 1513678Sroot return (-1); 1523678Sroot for (;;) { 1533678Sroot cp = bp; 1543678Sroot for (;;) { 1553678Sroot if (i == cnt) { 1563678Sroot cnt = read(tf, ibuf, BUFSIZ); 1573678Sroot if (cnt <= 0) { 1583678Sroot close(tf); 1593678Sroot return (0); 1603678Sroot } 1613678Sroot i = 0; 1623678Sroot } 1633678Sroot c = ibuf[i++]; 1643678Sroot if (c == '\n') { 1653678Sroot if (cp > bp && cp[-1] == '\\'){ 1663678Sroot cp--; 1673678Sroot continue; 1683678Sroot } 1693678Sroot break; 1703678Sroot } 1713841Ssam if (cp >= bp+BUFSIZ) { 1723841Ssam write(2,"Termcap entry too long\n", 23); 1733841Ssam break; 1743841Ssam } else 1753841Ssam *cp++ = c; 1763678Sroot } 1773678Sroot *cp = 0; 1783678Sroot 1793678Sroot /* 1803678Sroot * The real work for the match. 1813678Sroot */ 1823841Ssam if (tnamatch(name)) { 1833678Sroot close(tf); 1843841Ssam return(tnchktc()); 1853678Sroot } 1863678Sroot } 1873678Sroot } 1883678Sroot 1893678Sroot /* 1903841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1913841Ssam * recursively find xxx and append that entry (minus the names) 1923841Ssam * to take the place of the tc=xxx entry. This allows termcap 1933841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1943841Ssam * Note that this works because of the left to right scan. 1953841Ssam */ 1963841Ssam tnchktc() 1973841Ssam { 1983841Ssam register char *p, *q; 1993841Ssam char tcname[16]; /* name of similar terminal */ 2003841Ssam char tcbuf[BUFSIZ]; 2013841Ssam char *holdtbuf = tbuf; 2023841Ssam int l; 2033841Ssam 2043841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2053841Ssam while (*--p != ':') 2063841Ssam if (p<tbuf) { 2073841Ssam write(2, "Bad termcap entry\n", 18); 2083841Ssam return (0); 2093841Ssam } 2103841Ssam p++; 2113841Ssam /* p now points to beginning of last field */ 2123841Ssam if (p[0] != 't' || p[1] != 'c') 2133841Ssam return(1); 2143841Ssam strcpy(tcname,p+3); 2153841Ssam q = tcname; 2163841Ssam while (q && *q != ':') 2173841Ssam q++; 2183841Ssam *q = 0; 2193841Ssam if (++hopcount > MAXHOP) { 2203841Ssam write(2, "Infinite tc= loop\n", 18); 2213841Ssam return (0); 2223841Ssam } 2233841Ssam if (tgetent(tcbuf, tcname) != 1) 2243841Ssam return(0); 2253841Ssam for (q=tcbuf; *q != ':'; q++) 2263841Ssam ; 2273841Ssam l = p - holdtbuf + strlen(q); 2283841Ssam if (l > BUFSIZ) { 2293841Ssam write(2, "Termcap entry too long\n", 23); 2303841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2313841Ssam } 2323841Ssam strcpy(p, q+1); 2333841Ssam tbuf = holdtbuf; 2343841Ssam return(1); 2353841Ssam } 2363841Ssam 2373841Ssam /* 2383841Ssam * Tnamatch deals with name matching. The first field of the termcap 2393678Sroot * entry is a sequence of names separated by |'s, so we compare 2403678Sroot * against each such name. The normal : terminator after the last 2413678Sroot * name (before the first field) stops us. 2423678Sroot */ 2433841Ssam tnamatch(np) 2443678Sroot char *np; 2453678Sroot { 2463678Sroot register char *Np, *Bp; 2473678Sroot 2483841Ssam Bp = tbuf; 2493841Ssam if (*Bp == '#') 2503841Ssam return(0); 2513678Sroot for (;;) { 2523678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2533678Sroot continue; 2543678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2553678Sroot return (1); 2563678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2573678Sroot Bp++; 2583678Sroot if (*Bp == 0 || *Bp == ':') 2593678Sroot return (0); 2603678Sroot Bp++; 2613678Sroot } 2623678Sroot } 2633678Sroot 2643678Sroot /* 2653678Sroot * Skip to the next field. Notice that this is very dumb, not 2663678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2673841Ssam * into the termcap file in octal. 2683678Sroot */ 2693678Sroot static char * 2703841Ssam tskip(bp) 2713678Sroot register char *bp; 2723678Sroot { 2733678Sroot 2743678Sroot while (*bp && *bp != ':') 2753678Sroot bp++; 2763678Sroot if (*bp == ':') 2773678Sroot bp++; 2783678Sroot return (bp); 2793678Sroot } 2803678Sroot 2813678Sroot /* 2823678Sroot * Return the (numeric) option id. 2833678Sroot * Numeric options look like 2843678Sroot * li#80 2853678Sroot * i.e. the option string is separated from the numeric value by 2863678Sroot * a # character. If the option is not found we return -1. 2873678Sroot * Note that we handle octal numbers beginning with 0. 2883678Sroot */ 2893841Ssam tgetnum(id) 2903678Sroot char *id; 2913678Sroot { 2923678Sroot register int i, base; 2933841Ssam register char *bp = tbuf; 2943678Sroot 2953678Sroot for (;;) { 2963841Ssam bp = tskip(bp); 2973678Sroot if (*bp == 0) 2983678Sroot return (-1); 2993678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3003678Sroot continue; 3013841Ssam if (*bp == '@') 3023841Ssam return(-1); 3033678Sroot if (*bp != '#') 3043678Sroot continue; 3053678Sroot bp++; 3063678Sroot base = 10; 3073678Sroot if (*bp == '0') 3083678Sroot base = 8; 3093678Sroot i = 0; 3103678Sroot while (isdigit(*bp)) 3113678Sroot i *= base, i += *bp++ - '0'; 3123678Sroot return (i); 3133678Sroot } 3143678Sroot } 3153678Sroot 3163678Sroot /* 3173678Sroot * Handle a flag option. 3183678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3193678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3203678Sroot * not given. 3213678Sroot */ 3223841Ssam tgetflag(id) 3233678Sroot char *id; 3243678Sroot { 3253841Ssam register char *bp = tbuf; 3263678Sroot 3273678Sroot for (;;) { 3283841Ssam bp = tskip(bp); 3293678Sroot if (!*bp) 3303678Sroot return (0); 3313841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3323841Ssam if (!*bp || *bp == ':') 3333841Ssam return (1); 3343841Ssam else if (*bp == '@') 3353841Ssam return(0); 3363841Ssam } 3373678Sroot } 3383678Sroot } 3393678Sroot 3403678Sroot /* 3413678Sroot * Get a string valued option. 3423678Sroot * These are given as 3433678Sroot * cl=^Z 3443678Sroot * Much decoding is done on the strings, and the strings are 3453678Sroot * placed in area, which is a ref parameter which is updated. 3463678Sroot * No checking on area overflow. 3473678Sroot */ 3483678Sroot char * 3493841Ssam tgetstr(id, area) 3503678Sroot char *id, **area; 3513678Sroot { 3523841Ssam register char *bp = tbuf; 3533678Sroot 3543678Sroot for (;;) { 3553841Ssam bp = tskip(bp); 3563678Sroot if (!*bp) 3573678Sroot return (0); 3583678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3593678Sroot continue; 3603841Ssam if (*bp == '@') 3613841Ssam return(0); 3623678Sroot if (*bp != '=') 3633678Sroot continue; 3643678Sroot bp++; 3653841Ssam return (tdecode(bp, area)); 3663678Sroot } 3673678Sroot } 3683678Sroot 3693678Sroot /* 3703678Sroot * Tdecode does the grung work to decode the 3713678Sroot * string capability escapes. 3723678Sroot */ 3733678Sroot static char * 3743841Ssam tdecode(str, area) 3753678Sroot register char *str; 3763678Sroot char **area; 3773678Sroot { 3783678Sroot register char *cp; 3793678Sroot register int c; 3803678Sroot register char *dp; 3813678Sroot int i; 3823678Sroot 3833678Sroot cp = *area; 3843678Sroot while ((c = *str++) && c != ':') { 3853678Sroot switch (c) { 3863678Sroot 3873678Sroot case '^': 3883678Sroot c = *str++ & 037; 3893678Sroot break; 3903678Sroot 3913678Sroot case '\\': 3923678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3933678Sroot c = *str++; 3943678Sroot nextc: 3953678Sroot if (*dp++ == c) { 3963678Sroot c = *dp++; 3973678Sroot break; 3983678Sroot } 3993678Sroot dp++; 4003678Sroot if (*dp) 4013678Sroot goto nextc; 4023678Sroot if (isdigit(c)) { 4033678Sroot c -= '0', i = 2; 4043678Sroot do 4053678Sroot c <<= 3, c |= *str++ - '0'; 4063678Sroot while (--i && isdigit(*str)); 4073678Sroot } 4083678Sroot break; 4093678Sroot } 4103678Sroot *cp++ = c; 4113678Sroot } 4123678Sroot *cp++ = 0; 4133678Sroot str = *area; 4143678Sroot *area = cp; 4153678Sroot return (str); 4163678Sroot } 417