122436Sdist /* 222436Sdist * Copyright (c) 1983 Regents of the University of California. 3*34203Sbostic * All rights reserved. 4*34203Sbostic * 5*34203Sbostic * Redistribution and use in source and binary forms are permitted 6*34203Sbostic * provided that this notice is preserved and that due credit is given 7*34203Sbostic * to the University of California at Berkeley. The name of the University 8*34203Sbostic * may not be used to endorse or promote products derived from this 9*34203Sbostic * software without specific prior written permission. This software 10*34203Sbostic * is provided ``as is'' without express or implied warranty. 1122436Sdist */ 1222436Sdist 1313954Ssam #ifndef lint 14*34203Sbostic static char sccsid[] = "@(#)printcap.c 5.2 (Berkeley) 05/05/88"; 15*34203Sbostic #endif /* not lint */ 1613954Ssam 173841Ssam #define BUFSIZ 1024 183841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 193678Sroot 203678Sroot #include <ctype.h> 2112429Sralph #include <stdio.h> 223678Sroot /* 233841Ssam * termcap - routines for dealing with the terminal capability data base 243678Sroot * 253678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 263678Sroot * for capabilities alphabetically would not be a n**2/2 273678Sroot * process when large numbers of capabilities are given. 283841Ssam * Note: If we add a last pointer now we will screw up the 293841Ssam * tc capability. We really should compile termcap. 303678Sroot * 313678Sroot * Essentially all the work here is scanning and decoding escapes 323678Sroot * in string capabilities. We don't use stdio because the editor 333678Sroot * doesn't, and because living w/o it is not hard. 343678Sroot */ 353678Sroot 363841Ssam #define PRINTCAP 373678Sroot 383841Ssam #ifdef PRINTCAP 393841Ssam #define tgetent pgetent 403841Ssam #define tskip pskip 413841Ssam #define tgetstr pgetstr 423841Ssam #define tdecode pdecode 433841Ssam #define tgetnum pgetnum 443841Ssam #define tgetflag pgetflag 453841Ssam #define tdecode pdecode 463841Ssam #define tnchktc pnchktc 473841Ssam #define tnamatch pnamatch 483841Ssam #undef E_TERMCAP 493841Ssam #define E_TERMCAP "/etc/printcap" 503841Ssam #define V6 513841Ssam #endif 523841Ssam 5312429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 543841Ssam static char *tbuf; 5512429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 563841Ssam char *tskip(); 573841Ssam char *tgetstr(); 583841Ssam char *tdecode(); 593841Ssam char *getenv(); 603841Ssam 613678Sroot /* 6212429Sralph * Similar to tgetent except it returns the next enrty instead of 6312429Sralph * doing a lookup. 6412429Sralph */ 6512429Sralph getprent(bp) 6612429Sralph register char *bp; 6712429Sralph { 6812429Sralph register int c, skip = 0; 6912429Sralph 7012429Sralph if (pfp == NULL && (pfp = fopen(E_TERMCAP, "r")) == NULL) 7112429Sralph return(-1); 7212429Sralph tbuf = bp; 7312429Sralph for (;;) { 7412429Sralph switch (c = getc(pfp)) { 7512429Sralph case EOF: 7612429Sralph fclose(pfp); 7712429Sralph pfp = NULL; 7812429Sralph return(0); 7912429Sralph case '\n': 8012429Sralph if (bp == tbuf) { 8112429Sralph skip = 0; 8212429Sralph continue; 8312429Sralph } 8412429Sralph if (bp[-1] == '\\') { 8512429Sralph bp--; 8612429Sralph continue; 8712429Sralph } 8812429Sralph *bp = '\0'; 8912429Sralph return(1); 9012429Sralph case '#': 9112429Sralph if (bp == tbuf) 9212429Sralph skip++; 9312429Sralph default: 9412429Sralph if (skip) 9512429Sralph continue; 9612429Sralph if (bp >= tbuf+BUFSIZ) { 9712429Sralph write(2, "Termcap entry too long\n", 23); 9812429Sralph *bp = '\0'; 9912429Sralph return(1); 10012429Sralph } 10112429Sralph *bp++ = c; 10212429Sralph } 10312429Sralph } 10412429Sralph } 10512429Sralph 10612429Sralph endprent() 10712429Sralph { 10812429Sralph if (pfp != NULL) 10912429Sralph fclose(pfp); 11012429Sralph } 11112429Sralph 11212429Sralph /* 1133841Ssam * Get an entry for terminal name in buffer bp, 1143841Ssam * from the termcap file. Parse is very rudimentary; 1153678Sroot * we just notice escaped newlines. 1163678Sroot */ 1173841Ssam tgetent(bp, name) 1183678Sroot char *bp, *name; 1193678Sroot { 1203678Sroot register char *cp; 1213678Sroot register int c; 1223678Sroot register int i = 0, cnt = 0; 1233678Sroot char ibuf[BUFSIZ]; 1243841Ssam char *cp2; 1253678Sroot int tf; 1263678Sroot 1273841Ssam tbuf = bp; 1283841Ssam tf = 0; 1293841Ssam #ifndef V6 1303841Ssam cp = getenv("TERMCAP"); 1313841Ssam /* 1323841Ssam * TERMCAP can have one of two things in it. It can be the 1333841Ssam * name of a file to use instead of /etc/termcap. In this 1343841Ssam * case it better start with a "/". Or it can be an entry to 1353841Ssam * use so we don't have to read the file. In this case it 1363841Ssam * has to already have the newlines crunched out. 1373841Ssam */ 1383841Ssam if (cp && *cp) { 1393841Ssam if (*cp!='/') { 1403841Ssam cp2 = getenv("TERM"); 1413841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1423841Ssam strcpy(bp,cp); 1433841Ssam return(tnchktc()); 1443841Ssam } else { 1453841Ssam tf = open(E_TERMCAP, 0); 1463841Ssam } 1473841Ssam } else 1483841Ssam tf = open(cp, 0); 1493841Ssam } 1503841Ssam if (tf==0) 1513841Ssam tf = open(E_TERMCAP, 0); 1523841Ssam #else 1533841Ssam tf = open(E_TERMCAP, 0); 1543841Ssam #endif 1553678Sroot if (tf < 0) 1563678Sroot return (-1); 1573678Sroot for (;;) { 1583678Sroot cp = bp; 1593678Sroot for (;;) { 1603678Sroot if (i == cnt) { 1613678Sroot cnt = read(tf, ibuf, BUFSIZ); 1623678Sroot if (cnt <= 0) { 1633678Sroot close(tf); 1643678Sroot return (0); 1653678Sroot } 1663678Sroot i = 0; 1673678Sroot } 1683678Sroot c = ibuf[i++]; 1693678Sroot if (c == '\n') { 1703678Sroot if (cp > bp && cp[-1] == '\\'){ 1713678Sroot cp--; 1723678Sroot continue; 1733678Sroot } 1743678Sroot break; 1753678Sroot } 1763841Ssam if (cp >= bp+BUFSIZ) { 1773841Ssam write(2,"Termcap entry too long\n", 23); 1783841Ssam break; 1793841Ssam } else 1803841Ssam *cp++ = c; 1813678Sroot } 1823678Sroot *cp = 0; 1833678Sroot 1843678Sroot /* 1853678Sroot * The real work for the match. 1863678Sroot */ 1873841Ssam if (tnamatch(name)) { 1883678Sroot close(tf); 1893841Ssam return(tnchktc()); 1903678Sroot } 1913678Sroot } 1923678Sroot } 1933678Sroot 1943678Sroot /* 1953841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 1963841Ssam * recursively find xxx and append that entry (minus the names) 1973841Ssam * to take the place of the tc=xxx entry. This allows termcap 1983841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 1993841Ssam * Note that this works because of the left to right scan. 2003841Ssam */ 2013841Ssam tnchktc() 2023841Ssam { 2033841Ssam register char *p, *q; 2043841Ssam char tcname[16]; /* name of similar terminal */ 2053841Ssam char tcbuf[BUFSIZ]; 2063841Ssam char *holdtbuf = tbuf; 2073841Ssam int l; 2083841Ssam 2093841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2103841Ssam while (*--p != ':') 2113841Ssam if (p<tbuf) { 2123841Ssam write(2, "Bad termcap entry\n", 18); 2133841Ssam return (0); 2143841Ssam } 2153841Ssam p++; 2163841Ssam /* p now points to beginning of last field */ 2173841Ssam if (p[0] != 't' || p[1] != 'c') 2183841Ssam return(1); 2193841Ssam strcpy(tcname,p+3); 2203841Ssam q = tcname; 2213841Ssam while (q && *q != ':') 2223841Ssam q++; 2233841Ssam *q = 0; 2243841Ssam if (++hopcount > MAXHOP) { 2253841Ssam write(2, "Infinite tc= loop\n", 18); 2263841Ssam return (0); 2273841Ssam } 2283841Ssam if (tgetent(tcbuf, tcname) != 1) 2293841Ssam return(0); 2303841Ssam for (q=tcbuf; *q != ':'; q++) 2313841Ssam ; 2323841Ssam l = p - holdtbuf + strlen(q); 2333841Ssam if (l > BUFSIZ) { 2343841Ssam write(2, "Termcap entry too long\n", 23); 2353841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2363841Ssam } 2373841Ssam strcpy(p, q+1); 2383841Ssam tbuf = holdtbuf; 2393841Ssam return(1); 2403841Ssam } 2413841Ssam 2423841Ssam /* 2433841Ssam * Tnamatch deals with name matching. The first field of the termcap 2443678Sroot * entry is a sequence of names separated by |'s, so we compare 2453678Sroot * against each such name. The normal : terminator after the last 2463678Sroot * name (before the first field) stops us. 2473678Sroot */ 2483841Ssam tnamatch(np) 2493678Sroot char *np; 2503678Sroot { 2513678Sroot register char *Np, *Bp; 2523678Sroot 2533841Ssam Bp = tbuf; 2543841Ssam if (*Bp == '#') 2553841Ssam return(0); 2563678Sroot for (;;) { 2573678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2583678Sroot continue; 2593678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2603678Sroot return (1); 2613678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2623678Sroot Bp++; 2633678Sroot if (*Bp == 0 || *Bp == ':') 2643678Sroot return (0); 2653678Sroot Bp++; 2663678Sroot } 2673678Sroot } 2683678Sroot 2693678Sroot /* 2703678Sroot * Skip to the next field. Notice that this is very dumb, not 2713678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2723841Ssam * into the termcap file in octal. 2733678Sroot */ 2743678Sroot static char * 2753841Ssam tskip(bp) 2763678Sroot register char *bp; 2773678Sroot { 2783678Sroot 2793678Sroot while (*bp && *bp != ':') 2803678Sroot bp++; 2813678Sroot if (*bp == ':') 2823678Sroot bp++; 2833678Sroot return (bp); 2843678Sroot } 2853678Sroot 2863678Sroot /* 2873678Sroot * Return the (numeric) option id. 2883678Sroot * Numeric options look like 2893678Sroot * li#80 2903678Sroot * i.e. the option string is separated from the numeric value by 2913678Sroot * a # character. If the option is not found we return -1. 2923678Sroot * Note that we handle octal numbers beginning with 0. 2933678Sroot */ 2943841Ssam tgetnum(id) 2953678Sroot char *id; 2963678Sroot { 2973678Sroot register int i, base; 2983841Ssam register char *bp = tbuf; 2993678Sroot 3003678Sroot for (;;) { 3013841Ssam bp = tskip(bp); 3023678Sroot if (*bp == 0) 3033678Sroot return (-1); 3043678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3053678Sroot continue; 3063841Ssam if (*bp == '@') 3073841Ssam return(-1); 3083678Sroot if (*bp != '#') 3093678Sroot continue; 3103678Sroot bp++; 3113678Sroot base = 10; 3123678Sroot if (*bp == '0') 3133678Sroot base = 8; 3143678Sroot i = 0; 3153678Sroot while (isdigit(*bp)) 3163678Sroot i *= base, i += *bp++ - '0'; 3173678Sroot return (i); 3183678Sroot } 3193678Sroot } 3203678Sroot 3213678Sroot /* 3223678Sroot * Handle a flag option. 3233678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3243678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3253678Sroot * not given. 3263678Sroot */ 3273841Ssam tgetflag(id) 3283678Sroot char *id; 3293678Sroot { 3303841Ssam register char *bp = tbuf; 3313678Sroot 3323678Sroot for (;;) { 3333841Ssam bp = tskip(bp); 3343678Sroot if (!*bp) 3353678Sroot return (0); 3363841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3373841Ssam if (!*bp || *bp == ':') 3383841Ssam return (1); 3393841Ssam else if (*bp == '@') 3403841Ssam return(0); 3413841Ssam } 3423678Sroot } 3433678Sroot } 3443678Sroot 3453678Sroot /* 3463678Sroot * Get a string valued option. 3473678Sroot * These are given as 3483678Sroot * cl=^Z 3493678Sroot * Much decoding is done on the strings, and the strings are 3503678Sroot * placed in area, which is a ref parameter which is updated. 3513678Sroot * No checking on area overflow. 3523678Sroot */ 3533678Sroot char * 3543841Ssam tgetstr(id, area) 3553678Sroot char *id, **area; 3563678Sroot { 3573841Ssam register char *bp = tbuf; 3583678Sroot 3593678Sroot for (;;) { 3603841Ssam bp = tskip(bp); 3613678Sroot if (!*bp) 3623678Sroot return (0); 3633678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3643678Sroot continue; 3653841Ssam if (*bp == '@') 3663841Ssam return(0); 3673678Sroot if (*bp != '=') 3683678Sroot continue; 3693678Sroot bp++; 3703841Ssam return (tdecode(bp, area)); 3713678Sroot } 3723678Sroot } 3733678Sroot 3743678Sroot /* 3753678Sroot * Tdecode does the grung work to decode the 3763678Sroot * string capability escapes. 3773678Sroot */ 3783678Sroot static char * 3793841Ssam tdecode(str, area) 3803678Sroot register char *str; 3813678Sroot char **area; 3823678Sroot { 3833678Sroot register char *cp; 3843678Sroot register int c; 3853678Sroot register char *dp; 3863678Sroot int i; 3873678Sroot 3883678Sroot cp = *area; 3893678Sroot while ((c = *str++) && c != ':') { 3903678Sroot switch (c) { 3913678Sroot 3923678Sroot case '^': 3933678Sroot c = *str++ & 037; 3943678Sroot break; 3953678Sroot 3963678Sroot case '\\': 3973678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 3983678Sroot c = *str++; 3993678Sroot nextc: 4003678Sroot if (*dp++ == c) { 4013678Sroot c = *dp++; 4023678Sroot break; 4033678Sroot } 4043678Sroot dp++; 4053678Sroot if (*dp) 4063678Sroot goto nextc; 4073678Sroot if (isdigit(c)) { 4083678Sroot c -= '0', i = 2; 4093678Sroot do 4103678Sroot c <<= 3, c |= *str++ - '0'; 4113678Sroot while (--i && isdigit(*str)); 4123678Sroot } 4133678Sroot break; 4143678Sroot } 4153678Sroot *cp++ = c; 4163678Sroot } 4173678Sroot *cp++ = 0; 4183678Sroot str = *area; 4193678Sroot *area = cp; 4203678Sroot return (str); 4213678Sroot } 422