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*55470Sbostic static char sccsid[] = "@(#)printcap.c 5.8 (Berkeley) 07/21/92"; 1034203Sbostic #endif /* not lint */ 1113954Ssam 12*55470Sbostic #include <sys/param.h> 13*55470Sbostic 14*55470Sbostic #include <fcntl.h> 15*55470Sbostic #include <dirent.h> 16*55470Sbostic #include <unistd.h> 17*55470Sbostic #include <stdio.h> 1846978Sbostic #include <ctype.h> 19*55470Sbostic #include <string.h> 20*55470Sbostic #include "lp.h" 2146978Sbostic #include "pathnames.h" 2246978Sbostic 2346300Sbostic #ifndef BUFSIZ 243841Ssam #define BUFSIZ 1024 2546300Sbostic #endif 263841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 273678Sroot 283678Sroot /* 293841Ssam * termcap - routines for dealing with the terminal capability data base 303678Sroot * 313678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 323678Sroot * for capabilities alphabetically would not be a n**2/2 333678Sroot * process when large numbers of capabilities are given. 343841Ssam * Note: If we add a last pointer now we will screw up the 353841Ssam * tc capability. We really should compile termcap. 363678Sroot * 373678Sroot * Essentially all the work here is scanning and decoding escapes 383678Sroot * in string capabilities. We don't use stdio because the editor 393678Sroot * doesn't, and because living w/o it is not hard. 403678Sroot */ 413678Sroot 423841Ssam #define PRINTCAP 433678Sroot 443841Ssam #ifdef PRINTCAP 453841Ssam #define tgetent pgetent 463841Ssam #define tskip pskip 473841Ssam #define tgetstr pgetstr 483841Ssam #define tdecode pdecode 493841Ssam #define tgetnum pgetnum 503841Ssam #define tgetflag pgetflag 513841Ssam #define tdecode pdecode 523841Ssam #define tnchktc pnchktc 533841Ssam #define tnamatch pnamatch 543841Ssam #define V6 553841Ssam #endif 563841Ssam 5712429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 583841Ssam static char *tbuf; 5912429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 603841Ssam 61*55470Sbostic static char *tskip __P((char *)); 62*55470Sbostic static char *tskip __P((char *bp)); 63*55470Sbostic static char *tdecode __P((char *, char **)); 64*55470Sbostic 653678Sroot /* 6612429Sralph * Similar to tgetent except it returns the next enrty instead of 6712429Sralph * doing a lookup. 6812429Sralph */ 69*55470Sbostic int 7012429Sralph getprent(bp) 7112429Sralph register char *bp; 7212429Sralph { 7312429Sralph register int c, skip = 0; 7412429Sralph 7537968Sbostic if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL) 7612429Sralph return(-1); 7712429Sralph tbuf = bp; 7812429Sralph for (;;) { 7912429Sralph switch (c = getc(pfp)) { 8012429Sralph case EOF: 8112429Sralph fclose(pfp); 8212429Sralph pfp = NULL; 8312429Sralph return(0); 8412429Sralph case '\n': 8512429Sralph if (bp == tbuf) { 8612429Sralph skip = 0; 8712429Sralph continue; 8812429Sralph } 8912429Sralph if (bp[-1] == '\\') { 9012429Sralph bp--; 9112429Sralph continue; 9212429Sralph } 9312429Sralph *bp = '\0'; 9412429Sralph return(1); 9512429Sralph case '#': 9612429Sralph if (bp == tbuf) 9712429Sralph skip++; 9812429Sralph default: 9912429Sralph if (skip) 10012429Sralph continue; 10112429Sralph if (bp >= tbuf+BUFSIZ) { 10212429Sralph write(2, "Termcap entry too long\n", 23); 10312429Sralph *bp = '\0'; 10412429Sralph return(1); 10512429Sralph } 10612429Sralph *bp++ = c; 10712429Sralph } 10812429Sralph } 10912429Sralph } 11012429Sralph 111*55470Sbostic void 11212429Sralph endprent() 11312429Sralph { 11412429Sralph if (pfp != NULL) 11512429Sralph fclose(pfp); 11612429Sralph } 11712429Sralph 11812429Sralph /* 1193841Ssam * Get an entry for terminal name in buffer bp, 1203841Ssam * from the termcap file. Parse is very rudimentary; 1213678Sroot * we just notice escaped newlines. 1223678Sroot */ 123*55470Sbostic int 1243841Ssam tgetent(bp, name) 1253678Sroot char *bp, *name; 1263678Sroot { 1273678Sroot register char *cp; 1283678Sroot register int c; 1293678Sroot register int i = 0, cnt = 0; 1303678Sroot char ibuf[BUFSIZ]; 1313678Sroot int tf; 1323678Sroot 1333841Ssam tbuf = bp; 1343841Ssam tf = 0; 1353841Ssam #ifndef V6 1363841Ssam cp = getenv("TERMCAP"); 1373841Ssam /* 1383841Ssam * TERMCAP can have one of two things in it. It can be the 1393841Ssam * name of a file to use instead of /etc/termcap. In this 1403841Ssam * case it better start with a "/". Or it can be an entry to 1413841Ssam * use so we don't have to read the file. In this case it 1423841Ssam * has to already have the newlines crunched out. 1433841Ssam */ 1443841Ssam if (cp && *cp) { 1453841Ssam if (*cp!='/') { 1463841Ssam cp2 = getenv("TERM"); 1473841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1483841Ssam strcpy(bp,cp); 1493841Ssam return(tnchktc()); 1503841Ssam } else { 15137968Sbostic tf = open(_PATH_PRINTCAP, 0); 1523841Ssam } 1533841Ssam } else 1543841Ssam tf = open(cp, 0); 1553841Ssam } 1563841Ssam if (tf==0) 15737968Sbostic tf = open(_PATH_PRINTCAP, 0); 1583841Ssam #else 15937968Sbostic tf = open(_PATH_PRINTCAP, 0); 1603841Ssam #endif 1613678Sroot if (tf < 0) 1623678Sroot return (-1); 1633678Sroot for (;;) { 1643678Sroot cp = bp; 1653678Sroot for (;;) { 1663678Sroot if (i == cnt) { 1673678Sroot cnt = read(tf, ibuf, BUFSIZ); 1683678Sroot if (cnt <= 0) { 1693678Sroot close(tf); 1703678Sroot return (0); 1713678Sroot } 1723678Sroot i = 0; 1733678Sroot } 1743678Sroot c = ibuf[i++]; 1753678Sroot if (c == '\n') { 1763678Sroot if (cp > bp && cp[-1] == '\\'){ 1773678Sroot cp--; 1783678Sroot continue; 1793678Sroot } 1803678Sroot break; 1813678Sroot } 1823841Ssam if (cp >= bp+BUFSIZ) { 1833841Ssam write(2,"Termcap entry too long\n", 23); 1843841Ssam break; 1853841Ssam } else 1863841Ssam *cp++ = c; 1873678Sroot } 1883678Sroot *cp = 0; 1893678Sroot 1903678Sroot /* 1913678Sroot * The real work for the match. 1923678Sroot */ 1933841Ssam if (tnamatch(name)) { 1943678Sroot close(tf); 1953841Ssam return(tnchktc()); 1963678Sroot } 1973678Sroot } 1983678Sroot } 1993678Sroot 2003678Sroot /* 2013841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 2023841Ssam * recursively find xxx and append that entry (minus the names) 2033841Ssam * to take the place of the tc=xxx entry. This allows termcap 2043841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 2053841Ssam * Note that this works because of the left to right scan. 2063841Ssam */ 207*55470Sbostic int 2083841Ssam tnchktc() 2093841Ssam { 2103841Ssam register char *p, *q; 2113841Ssam char tcname[16]; /* name of similar terminal */ 2123841Ssam char tcbuf[BUFSIZ]; 2133841Ssam char *holdtbuf = tbuf; 2143841Ssam int l; 2153841Ssam 2163841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2173841Ssam while (*--p != ':') 2183841Ssam if (p<tbuf) { 2193841Ssam write(2, "Bad termcap entry\n", 18); 2203841Ssam return (0); 2213841Ssam } 2223841Ssam p++; 2233841Ssam /* p now points to beginning of last field */ 2243841Ssam if (p[0] != 't' || p[1] != 'c') 2253841Ssam return(1); 2263841Ssam strcpy(tcname,p+3); 2273841Ssam q = tcname; 2283841Ssam while (q && *q != ':') 2293841Ssam q++; 2303841Ssam *q = 0; 2313841Ssam if (++hopcount > MAXHOP) { 2323841Ssam write(2, "Infinite tc= loop\n", 18); 2333841Ssam return (0); 2343841Ssam } 2353841Ssam if (tgetent(tcbuf, tcname) != 1) 2363841Ssam return(0); 2373841Ssam for (q=tcbuf; *q != ':'; q++) 2383841Ssam ; 2393841Ssam l = p - holdtbuf + strlen(q); 2403841Ssam if (l > BUFSIZ) { 2413841Ssam write(2, "Termcap entry too long\n", 23); 2423841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2433841Ssam } 2443841Ssam strcpy(p, q+1); 2453841Ssam tbuf = holdtbuf; 2463841Ssam return(1); 2473841Ssam } 2483841Ssam 2493841Ssam /* 2503841Ssam * Tnamatch deals with name matching. The first field of the termcap 2513678Sroot * entry is a sequence of names separated by |'s, so we compare 2523678Sroot * against each such name. The normal : terminator after the last 2533678Sroot * name (before the first field) stops us. 2543678Sroot */ 255*55470Sbostic int 2563841Ssam tnamatch(np) 2573678Sroot char *np; 2583678Sroot { 2593678Sroot register char *Np, *Bp; 2603678Sroot 2613841Ssam Bp = tbuf; 2623841Ssam if (*Bp == '#') 2633841Ssam return(0); 2643678Sroot for (;;) { 2653678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2663678Sroot continue; 2673678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2683678Sroot return (1); 2693678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2703678Sroot Bp++; 2713678Sroot if (*Bp == 0 || *Bp == ':') 2723678Sroot return (0); 2733678Sroot Bp++; 2743678Sroot } 2753678Sroot } 2763678Sroot 2773678Sroot /* 2783678Sroot * Skip to the next field. Notice that this is very dumb, not 2793678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2803841Ssam * into the termcap file in octal. 2813678Sroot */ 2823678Sroot static char * 2833841Ssam tskip(bp) 2843678Sroot register char *bp; 2853678Sroot { 2863678Sroot 2873678Sroot while (*bp && *bp != ':') 2883678Sroot bp++; 2893678Sroot if (*bp == ':') 2903678Sroot bp++; 2913678Sroot return (bp); 2923678Sroot } 2933678Sroot 2943678Sroot /* 2953678Sroot * Return the (numeric) option id. 2963678Sroot * Numeric options look like 2973678Sroot * li#80 2983678Sroot * i.e. the option string is separated from the numeric value by 2993678Sroot * a # character. If the option is not found we return -1. 3003678Sroot * Note that we handle octal numbers beginning with 0. 3013678Sroot */ 302*55470Sbostic int 3033841Ssam tgetnum(id) 3043678Sroot char *id; 3053678Sroot { 3063678Sroot register int i, base; 3073841Ssam register char *bp = tbuf; 3083678Sroot 3093678Sroot for (;;) { 3103841Ssam bp = tskip(bp); 3113678Sroot if (*bp == 0) 3123678Sroot return (-1); 3133678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3143678Sroot continue; 3153841Ssam if (*bp == '@') 3163841Ssam return(-1); 3173678Sroot if (*bp != '#') 3183678Sroot continue; 3193678Sroot bp++; 3203678Sroot base = 10; 3213678Sroot if (*bp == '0') 3223678Sroot base = 8; 3233678Sroot i = 0; 3243678Sroot while (isdigit(*bp)) 3253678Sroot i *= base, i += *bp++ - '0'; 3263678Sroot return (i); 3273678Sroot } 3283678Sroot } 3293678Sroot 3303678Sroot /* 3313678Sroot * Handle a flag option. 3323678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3333678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3343678Sroot * not given. 3353678Sroot */ 336*55470Sbostic int 3373841Ssam tgetflag(id) 3383678Sroot char *id; 3393678Sroot { 3403841Ssam register char *bp = tbuf; 3413678Sroot 3423678Sroot for (;;) { 3433841Ssam bp = tskip(bp); 3443678Sroot if (!*bp) 3453678Sroot return (0); 3463841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3473841Ssam if (!*bp || *bp == ':') 3483841Ssam return (1); 3493841Ssam else if (*bp == '@') 3503841Ssam return(0); 3513841Ssam } 3523678Sroot } 3533678Sroot } 3543678Sroot 3553678Sroot /* 3563678Sroot * Get a string valued option. 3573678Sroot * These are given as 3583678Sroot * cl=^Z 3593678Sroot * Much decoding is done on the strings, and the strings are 3603678Sroot * placed in area, which is a ref parameter which is updated. 3613678Sroot * No checking on area overflow. 3623678Sroot */ 3633678Sroot char * 3643841Ssam tgetstr(id, area) 3653678Sroot char *id, **area; 3663678Sroot { 3673841Ssam register char *bp = tbuf; 3683678Sroot 3693678Sroot for (;;) { 3703841Ssam bp = tskip(bp); 3713678Sroot if (!*bp) 3723678Sroot return (0); 3733678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3743678Sroot continue; 3753841Ssam if (*bp == '@') 3763841Ssam return(0); 3773678Sroot if (*bp != '=') 3783678Sroot continue; 3793678Sroot bp++; 3803841Ssam return (tdecode(bp, area)); 3813678Sroot } 3823678Sroot } 3833678Sroot 3843678Sroot /* 3853678Sroot * Tdecode does the grung work to decode the 3863678Sroot * string capability escapes. 3873678Sroot */ 3883678Sroot static char * 3893841Ssam tdecode(str, area) 3903678Sroot register char *str; 3913678Sroot char **area; 3923678Sroot { 3933678Sroot register char *cp; 3943678Sroot register int c; 3953678Sroot register char *dp; 3963678Sroot int i; 3973678Sroot 3983678Sroot cp = *area; 3993678Sroot while ((c = *str++) && c != ':') { 4003678Sroot switch (c) { 4013678Sroot 4023678Sroot case '^': 4033678Sroot c = *str++ & 037; 4043678Sroot break; 4053678Sroot 4063678Sroot case '\\': 4073678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 4083678Sroot c = *str++; 4093678Sroot nextc: 4103678Sroot if (*dp++ == c) { 4113678Sroot c = *dp++; 4123678Sroot break; 4133678Sroot } 4143678Sroot dp++; 4153678Sroot if (*dp) 4163678Sroot goto nextc; 4173678Sroot if (isdigit(c)) { 4183678Sroot c -= '0', i = 2; 4193678Sroot do 4203678Sroot c <<= 3, c |= *str++ - '0'; 4213678Sroot while (--i && isdigit(*str)); 4223678Sroot } 4233678Sroot break; 4243678Sroot } 4253678Sroot *cp++ = c; 4263678Sroot } 4273678Sroot *cp++ = 0; 4283678Sroot str = *area; 4293678Sroot *area = cp; 4303678Sroot return (str); 4313678Sroot } 432*55470Sbostic 433