1*22436Sdist /* 2*22436Sdist * Copyright (c) 1983 Regents of the University of California. 3*22436Sdist * All rights reserved. The Berkeley software License Agreement 4*22436Sdist * specifies the terms and conditions for redistribution. 5*22436Sdist */ 6*22436Sdist 713954Ssam #ifndef lint 8*22436Sdist static char sccsid[] = "@(#)printcap.c 5.1 (Berkeley) 06/06/85"; 9*22436Sdist #endif not lint 1013954Ssam 113841Ssam #define BUFSIZ 1024 123841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 133678Sroot 143678Sroot #include <ctype.h> 1512429Sralph #include <stdio.h> 163678Sroot /* 173841Ssam * termcap - routines for dealing with the terminal capability data base 183678Sroot * 193678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 203678Sroot * for capabilities alphabetically would not be a n**2/2 213678Sroot * process when large numbers of capabilities are given. 223841Ssam * Note: If we add a last pointer now we will screw up the 233841Ssam * tc capability. We really should compile termcap. 243678Sroot * 253678Sroot * Essentially all the work here is scanning and decoding escapes 263678Sroot * in string capabilities. We don't use stdio because the editor 273678Sroot * doesn't, and because living w/o it is not hard. 283678Sroot */ 293678Sroot 303841Ssam #define PRINTCAP 313678Sroot 323841Ssam #ifdef PRINTCAP 333841Ssam #define tgetent pgetent 343841Ssam #define tskip pskip 353841Ssam #define tgetstr pgetstr 363841Ssam #define tdecode pdecode 373841Ssam #define tgetnum pgetnum 383841Ssam #define tgetflag pgetflag 393841Ssam #define tdecode pdecode 403841Ssam #define tnchktc pnchktc 413841Ssam #define tnamatch pnamatch 423841Ssam #undef E_TERMCAP 433841Ssam #define E_TERMCAP "/etc/printcap" 443841Ssam #define V6 453841Ssam #endif 463841Ssam 4712429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 483841Ssam static char *tbuf; 4912429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 503841Ssam char *tskip(); 513841Ssam char *tgetstr(); 523841Ssam char *tdecode(); 533841Ssam char *getenv(); 543841Ssam 553678Sroot /* 5612429Sralph * Similar to tgetent except it returns the next enrty instead of 5712429Sralph * doing a lookup. 5812429Sralph */ 5912429Sralph getprent(bp) 6012429Sralph register char *bp; 6112429Sralph { 6212429Sralph register int c, skip = 0; 6312429Sralph 6412429Sralph if (pfp == NULL && (pfp = fopen(E_TERMCAP, "r")) == NULL) 6512429Sralph return(-1); 6612429Sralph tbuf = bp; 6712429Sralph for (;;) { 6812429Sralph switch (c = getc(pfp)) { 6912429Sralph case EOF: 7012429Sralph fclose(pfp); 7112429Sralph pfp = NULL; 7212429Sralph return(0); 7312429Sralph case '\n': 7412429Sralph if (bp == tbuf) { 7512429Sralph skip = 0; 7612429Sralph continue; 7712429Sralph } 7812429Sralph if (bp[-1] == '\\') { 7912429Sralph bp--; 8012429Sralph continue; 8112429Sralph } 8212429Sralph *bp = '\0'; 8312429Sralph return(1); 8412429Sralph case '#': 8512429Sralph if (bp == tbuf) 8612429Sralph skip++; 8712429Sralph default: 8812429Sralph if (skip) 8912429Sralph continue; 9012429Sralph if (bp >= tbuf+BUFSIZ) { 9112429Sralph write(2, "Termcap entry too long\n", 23); 9212429Sralph *bp = '\0'; 9312429Sralph return(1); 9412429Sralph } 9512429Sralph *bp++ = c; 9612429Sralph } 9712429Sralph } 9812429Sralph } 9912429Sralph 10012429Sralph endprent() 10112429Sralph { 10212429Sralph if (pfp != NULL) 10312429Sralph fclose(pfp); 10412429Sralph } 10512429Sralph 10612429Sralph /* 1073841Ssam * Get an entry for terminal name in buffer bp, 1083841Ssam * from the termcap file. Parse is very rudimentary; 1093678Sroot * we just notice escaped newlines. 1103678Sroot */ 1113841Ssam tgetent(bp, name) 1123678Sroot char *bp, *name; 1133678Sroot { 1143678Sroot register char *cp; 1153678Sroot register int c; 1163678Sroot register int i = 0, cnt = 0; 1173678Sroot char ibuf[BUFSIZ]; 1183841Ssam char *cp2; 1193678Sroot int tf; 1203678Sroot 1213841Ssam tbuf = bp; 1223841Ssam tf = 0; 1233841Ssam #ifndef V6 1243841Ssam cp = getenv("TERMCAP"); 1253841Ssam /* 1263841Ssam * TERMCAP can have one of two things in it. It can be the 1273841Ssam * name of a file to use instead of /etc/termcap. In this 1283841Ssam * case it better start with a "/". Or it can be an entry to 1293841Ssam * use so we don't have to read the file. In this case it 1303841Ssam * has to already have the newlines crunched out. 1313841Ssam */ 1323841Ssam if (cp && *cp) { 1333841Ssam if (*cp!='/') { 1343841Ssam cp2 = getenv("TERM"); 1353841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1363841Ssam strcpy(bp,cp); 1373841Ssam return(tnchktc()); 1383841Ssam } else { 1393841Ssam tf = open(E_TERMCAP, 0); 1403841Ssam } 1413841Ssam } else 1423841Ssam tf = open(cp, 0); 1433841Ssam } 1443841Ssam if (tf==0) 1453841Ssam tf = open(E_TERMCAP, 0); 1463841Ssam #else 1473841Ssam tf = open(E_TERMCAP, 0); 1483841Ssam #endif 1493678Sroot if (tf < 0) 1503678Sroot return (-1); 1513678Sroot for (;;) { 1523678Sroot cp = bp; 1533678Sroot for (;;) { 1543678Sroot if (i == cnt) { 1553678Sroot cnt = read(tf, ibuf, BUFSIZ); 1563678Sroot if (cnt <= 0) { 1573678Sroot close(tf); 1583678Sroot return (0); 1593678Sroot } 1603678Sroot i = 0; 1613678Sroot } 1623678Sroot c = ibuf[i++]; 1633678Sroot if (c == '\n') { 1643678Sroot if (cp > bp && cp[-1] == '\\'){ 1653678Sroot cp--; 1663678Sroot continue; 1673678Sroot } 1683678Sroot break; 1693678Sroot } 1703841Ssam if (cp >= bp+BUFSIZ) { 1713841Ssam write(2,"Termcap entry too long\n", 23); 1723841Ssam break; 1733841Ssam } else 1743841Ssam *cp++ = c; 1753678Sroot } 1763678Sroot *cp = 0; 1773678Sroot 1783678Sroot /* 1793678Sroot * The real work for the match. 1803678Sroot */ 1813841Ssam if (tnamatch(name)) { 1823678Sroot close(tf); 1833841Ssam return(tnchktc()); 1843678Sroot } 1853678Sroot } 1863678Sroot } 1873678Sroot 1883678Sroot /* 1893841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1903841Ssam * recursively find xxx and append that entry (minus the names) 1913841Ssam * to take the place of the tc=xxx entry. This allows termcap 1923841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1933841Ssam * Note that this works because of the left to right scan. 1943841Ssam */ 1953841Ssam tnchktc() 1963841Ssam { 1973841Ssam register char *p, *q; 1983841Ssam char tcname[16]; /* name of similar terminal */ 1993841Ssam char tcbuf[BUFSIZ]; 2003841Ssam char *holdtbuf = tbuf; 2013841Ssam int l; 2023841Ssam 2033841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2043841Ssam while (*--p != ':') 2053841Ssam if (p<tbuf) { 2063841Ssam write(2, "Bad termcap entry\n", 18); 2073841Ssam return (0); 2083841Ssam } 2093841Ssam p++; 2103841Ssam /* p now points to beginning of last field */ 2113841Ssam if (p[0] != 't' || p[1] != 'c') 2123841Ssam return(1); 2133841Ssam strcpy(tcname,p+3); 2143841Ssam q = tcname; 2153841Ssam while (q && *q != ':') 2163841Ssam q++; 2173841Ssam *q = 0; 2183841Ssam if (++hopcount > MAXHOP) { 2193841Ssam write(2, "Infinite tc= loop\n", 18); 2203841Ssam return (0); 2213841Ssam } 2223841Ssam if (tgetent(tcbuf, tcname) != 1) 2233841Ssam return(0); 2243841Ssam for (q=tcbuf; *q != ':'; q++) 2253841Ssam ; 2263841Ssam l = p - holdtbuf + strlen(q); 2273841Ssam if (l > BUFSIZ) { 2283841Ssam write(2, "Termcap entry too long\n", 23); 2293841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2303841Ssam } 2313841Ssam strcpy(p, q+1); 2323841Ssam tbuf = holdtbuf; 2333841Ssam return(1); 2343841Ssam } 2353841Ssam 2363841Ssam /* 2373841Ssam * Tnamatch deals with name matching. The first field of the termcap 2383678Sroot * entry is a sequence of names separated by |'s, so we compare 2393678Sroot * against each such name. The normal : terminator after the last 2403678Sroot * name (before the first field) stops us. 2413678Sroot */ 2423841Ssam tnamatch(np) 2433678Sroot char *np; 2443678Sroot { 2453678Sroot register char *Np, *Bp; 2463678Sroot 2473841Ssam Bp = tbuf; 2483841Ssam if (*Bp == '#') 2493841Ssam return(0); 2503678Sroot for (;;) { 2513678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2523678Sroot continue; 2533678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2543678Sroot return (1); 2553678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2563678Sroot Bp++; 2573678Sroot if (*Bp == 0 || *Bp == ':') 2583678Sroot return (0); 2593678Sroot Bp++; 2603678Sroot } 2613678Sroot } 2623678Sroot 2633678Sroot /* 2643678Sroot * Skip to the next field. Notice that this is very dumb, not 2653678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2663841Ssam * into the termcap file in octal. 2673678Sroot */ 2683678Sroot static char * 2693841Ssam tskip(bp) 2703678Sroot register char *bp; 2713678Sroot { 2723678Sroot 2733678Sroot while (*bp && *bp != ':') 2743678Sroot bp++; 2753678Sroot if (*bp == ':') 2763678Sroot bp++; 2773678Sroot return (bp); 2783678Sroot } 2793678Sroot 2803678Sroot /* 2813678Sroot * Return the (numeric) option id. 2823678Sroot * Numeric options look like 2833678Sroot * li#80 2843678Sroot * i.e. the option string is separated from the numeric value by 2853678Sroot * a # character. If the option is not found we return -1. 2863678Sroot * Note that we handle octal numbers beginning with 0. 2873678Sroot */ 2883841Ssam tgetnum(id) 2893678Sroot char *id; 2903678Sroot { 2913678Sroot register int i, base; 2923841Ssam register char *bp = tbuf; 2933678Sroot 2943678Sroot for (;;) { 2953841Ssam bp = tskip(bp); 2963678Sroot if (*bp == 0) 2973678Sroot return (-1); 2983678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 2993678Sroot continue; 3003841Ssam if (*bp == '@') 3013841Ssam return(-1); 3023678Sroot if (*bp != '#') 3033678Sroot continue; 3043678Sroot bp++; 3053678Sroot base = 10; 3063678Sroot if (*bp == '0') 3073678Sroot base = 8; 3083678Sroot i = 0; 3093678Sroot while (isdigit(*bp)) 3103678Sroot i *= base, i += *bp++ - '0'; 3113678Sroot return (i); 3123678Sroot } 3133678Sroot } 3143678Sroot 3153678Sroot /* 3163678Sroot * Handle a flag option. 3173678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3183678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3193678Sroot * not given. 3203678Sroot */ 3213841Ssam tgetflag(id) 3223678Sroot char *id; 3233678Sroot { 3243841Ssam register char *bp = tbuf; 3253678Sroot 3263678Sroot for (;;) { 3273841Ssam bp = tskip(bp); 3283678Sroot if (!*bp) 3293678Sroot return (0); 3303841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3313841Ssam if (!*bp || *bp == ':') 3323841Ssam return (1); 3333841Ssam else if (*bp == '@') 3343841Ssam return(0); 3353841Ssam } 3363678Sroot } 3373678Sroot } 3383678Sroot 3393678Sroot /* 3403678Sroot * Get a string valued option. 3413678Sroot * These are given as 3423678Sroot * cl=^Z 3433678Sroot * Much decoding is done on the strings, and the strings are 3443678Sroot * placed in area, which is a ref parameter which is updated. 3453678Sroot * No checking on area overflow. 3463678Sroot */ 3473678Sroot char * 3483841Ssam tgetstr(id, area) 3493678Sroot char *id, **area; 3503678Sroot { 3513841Ssam register char *bp = tbuf; 3523678Sroot 3533678Sroot for (;;) { 3543841Ssam bp = tskip(bp); 3553678Sroot if (!*bp) 3563678Sroot return (0); 3573678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3583678Sroot continue; 3593841Ssam if (*bp == '@') 3603841Ssam return(0); 3613678Sroot if (*bp != '=') 3623678Sroot continue; 3633678Sroot bp++; 3643841Ssam return (tdecode(bp, area)); 3653678Sroot } 3663678Sroot } 3673678Sroot 3683678Sroot /* 3693678Sroot * Tdecode does the grung work to decode the 3703678Sroot * string capability escapes. 3713678Sroot */ 3723678Sroot static char * 3733841Ssam tdecode(str, area) 3743678Sroot register char *str; 3753678Sroot char **area; 3763678Sroot { 3773678Sroot register char *cp; 3783678Sroot register int c; 3793678Sroot register char *dp; 3803678Sroot int i; 3813678Sroot 3823678Sroot cp = *area; 3833678Sroot while ((c = *str++) && c != ':') { 3843678Sroot switch (c) { 3853678Sroot 3863678Sroot case '^': 3873678Sroot c = *str++ & 037; 3883678Sroot break; 3893678Sroot 3903678Sroot case '\\': 3913678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3923678Sroot c = *str++; 3933678Sroot nextc: 3943678Sroot if (*dp++ == c) { 3953678Sroot c = *dp++; 3963678Sroot break; 3973678Sroot } 3983678Sroot dp++; 3993678Sroot if (*dp) 4003678Sroot goto nextc; 4013678Sroot if (isdigit(c)) { 4023678Sroot c -= '0', i = 2; 4033678Sroot do 4043678Sroot c <<= 3, c |= *str++ - '0'; 4053678Sroot while (--i && isdigit(*str)); 4063678Sroot } 4073678Sroot break; 4083678Sroot } 4093678Sroot *cp++ = c; 4103678Sroot } 4113678Sroot *cp++ = 0; 4123678Sroot str = *area; 4133678Sroot *area = cp; 4143678Sroot return (str); 4153678Sroot } 416