122436Sdist /* 222436Sdist * Copyright (c) 1983 Regents of the University of California. 334203Sbostic * All rights reserved. 434203Sbostic * 534203Sbostic * Redistribution and use in source and binary forms are permitted 634936Sbostic * provided that the above copyright notice and this paragraph are 734936Sbostic * duplicated in all such forms and that any documentation, 834936Sbostic * advertising materials, and other materials related to such 934936Sbostic * distribution and use acknowledge that the software was developed 1034936Sbostic * by the University of California, Berkeley. The name of the 1134936Sbostic * University may not be used to endorse or promote products derived 1234936Sbostic * from this software without specific prior written permission. 1334936Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434936Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534936Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622436Sdist */ 1722436Sdist 1813954Ssam #ifndef lint 19*37968Sbostic static char sccsid[] = "@(#)printcap.c 5.4 (Berkeley) 05/11/89"; 2034203Sbostic #endif /* not lint */ 2113954Ssam 223841Ssam #define BUFSIZ 1024 233841Ssam #define MAXHOP 32 /* max number of tc= indirections */ 243678Sroot 253678Sroot #include <ctype.h> 2612429Sralph #include <stdio.h> 27*37968Sbostic #include "pathnames.h" 28*37968Sbostic 293678Sroot /* 303841Ssam * termcap - routines for dealing with the terminal capability data base 313678Sroot * 323678Sroot * BUG: Should use a "last" pointer in tbuf, so that searching 333678Sroot * for capabilities alphabetically would not be a n**2/2 343678Sroot * process when large numbers of capabilities are given. 353841Ssam * Note: If we add a last pointer now we will screw up the 363841Ssam * tc capability. We really should compile termcap. 373678Sroot * 383678Sroot * Essentially all the work here is scanning and decoding escapes 393678Sroot * in string capabilities. We don't use stdio because the editor 403678Sroot * doesn't, and because living w/o it is not hard. 413678Sroot */ 423678Sroot 433841Ssam #define PRINTCAP 443678Sroot 453841Ssam #ifdef PRINTCAP 463841Ssam #define tgetent pgetent 473841Ssam #define tskip pskip 483841Ssam #define tgetstr pgetstr 493841Ssam #define tdecode pdecode 503841Ssam #define tgetnum pgetnum 513841Ssam #define tgetflag pgetflag 523841Ssam #define tdecode pdecode 533841Ssam #define tnchktc pnchktc 543841Ssam #define tnamatch pnamatch 553841Ssam #define V6 563841Ssam #endif 573841Ssam 5812429Sralph static FILE *pfp = NULL; /* printcap data base file pointer */ 593841Ssam static char *tbuf; 6012429Sralph static int hopcount; /* detect infinite loops in termcap, init 0 */ 613841Ssam char *tskip(); 623841Ssam char *tgetstr(); 633841Ssam char *tdecode(); 643841Ssam char *getenv(); 653841Ssam 663678Sroot /* 6712429Sralph * Similar to tgetent except it returns the next enrty instead of 6812429Sralph * doing a lookup. 6912429Sralph */ 7012429Sralph getprent(bp) 7112429Sralph register char *bp; 7212429Sralph { 7312429Sralph register int c, skip = 0; 7412429Sralph 75*37968Sbostic 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 11112429Sralph endprent() 11212429Sralph { 11312429Sralph if (pfp != NULL) 11412429Sralph fclose(pfp); 11512429Sralph } 11612429Sralph 11712429Sralph /* 1183841Ssam * Get an entry for terminal name in buffer bp, 1193841Ssam * from the termcap file. Parse is very rudimentary; 1203678Sroot * we just notice escaped newlines. 1213678Sroot */ 1223841Ssam tgetent(bp, name) 1233678Sroot char *bp, *name; 1243678Sroot { 1253678Sroot register char *cp; 1263678Sroot register int c; 1273678Sroot register int i = 0, cnt = 0; 1283678Sroot char ibuf[BUFSIZ]; 1293841Ssam char *cp2; 1303678Sroot int tf; 1313678Sroot 1323841Ssam tbuf = bp; 1333841Ssam tf = 0; 1343841Ssam #ifndef V6 1353841Ssam cp = getenv("TERMCAP"); 1363841Ssam /* 1373841Ssam * TERMCAP can have one of two things in it. It can be the 1383841Ssam * name of a file to use instead of /etc/termcap. In this 1393841Ssam * case it better start with a "/". Or it can be an entry to 1403841Ssam * use so we don't have to read the file. In this case it 1413841Ssam * has to already have the newlines crunched out. 1423841Ssam */ 1433841Ssam if (cp && *cp) { 1443841Ssam if (*cp!='/') { 1453841Ssam cp2 = getenv("TERM"); 1463841Ssam if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 1473841Ssam strcpy(bp,cp); 1483841Ssam return(tnchktc()); 1493841Ssam } else { 150*37968Sbostic tf = open(_PATH_PRINTCAP, 0); 1513841Ssam } 1523841Ssam } else 1533841Ssam tf = open(cp, 0); 1543841Ssam } 1553841Ssam if (tf==0) 156*37968Sbostic tf = open(_PATH_PRINTCAP, 0); 1573841Ssam #else 158*37968Sbostic tf = open(_PATH_PRINTCAP, 0); 1593841Ssam #endif 1603678Sroot if (tf < 0) 1613678Sroot return (-1); 1623678Sroot for (;;) { 1633678Sroot cp = bp; 1643678Sroot for (;;) { 1653678Sroot if (i == cnt) { 1663678Sroot cnt = read(tf, ibuf, BUFSIZ); 1673678Sroot if (cnt <= 0) { 1683678Sroot close(tf); 1693678Sroot return (0); 1703678Sroot } 1713678Sroot i = 0; 1723678Sroot } 1733678Sroot c = ibuf[i++]; 1743678Sroot if (c == '\n') { 1753678Sroot if (cp > bp && cp[-1] == '\\'){ 1763678Sroot cp--; 1773678Sroot continue; 1783678Sroot } 1793678Sroot break; 1803678Sroot } 1813841Ssam if (cp >= bp+BUFSIZ) { 1823841Ssam write(2,"Termcap entry too long\n", 23); 1833841Ssam break; 1843841Ssam } else 1853841Ssam *cp++ = c; 1863678Sroot } 1873678Sroot *cp = 0; 1883678Sroot 1893678Sroot /* 1903678Sroot * The real work for the match. 1913678Sroot */ 1923841Ssam if (tnamatch(name)) { 1933678Sroot close(tf); 1943841Ssam return(tnchktc()); 1953678Sroot } 1963678Sroot } 1973678Sroot } 1983678Sroot 1993678Sroot /* 2003841Ssam * tnchktc: check the last entry, see if it's tc=xxx. If so, 2013841Ssam * recursively find xxx and append that entry (minus the names) 2023841Ssam * to take the place of the tc=xxx entry. This allows termcap 2033841Ssam * entries to say "like an HP2621 but doesn't turn on the labels". 2043841Ssam * Note that this works because of the left to right scan. 2053841Ssam */ 2063841Ssam tnchktc() 2073841Ssam { 2083841Ssam register char *p, *q; 2093841Ssam char tcname[16]; /* name of similar terminal */ 2103841Ssam char tcbuf[BUFSIZ]; 2113841Ssam char *holdtbuf = tbuf; 2123841Ssam int l; 2133841Ssam 2143841Ssam p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 2153841Ssam while (*--p != ':') 2163841Ssam if (p<tbuf) { 2173841Ssam write(2, "Bad termcap entry\n", 18); 2183841Ssam return (0); 2193841Ssam } 2203841Ssam p++; 2213841Ssam /* p now points to beginning of last field */ 2223841Ssam if (p[0] != 't' || p[1] != 'c') 2233841Ssam return(1); 2243841Ssam strcpy(tcname,p+3); 2253841Ssam q = tcname; 2263841Ssam while (q && *q != ':') 2273841Ssam q++; 2283841Ssam *q = 0; 2293841Ssam if (++hopcount > MAXHOP) { 2303841Ssam write(2, "Infinite tc= loop\n", 18); 2313841Ssam return (0); 2323841Ssam } 2333841Ssam if (tgetent(tcbuf, tcname) != 1) 2343841Ssam return(0); 2353841Ssam for (q=tcbuf; *q != ':'; q++) 2363841Ssam ; 2373841Ssam l = p - holdtbuf + strlen(q); 2383841Ssam if (l > BUFSIZ) { 2393841Ssam write(2, "Termcap entry too long\n", 23); 2403841Ssam q[BUFSIZ - (p-tbuf)] = 0; 2413841Ssam } 2423841Ssam strcpy(p, q+1); 2433841Ssam tbuf = holdtbuf; 2443841Ssam return(1); 2453841Ssam } 2463841Ssam 2473841Ssam /* 2483841Ssam * Tnamatch deals with name matching. The first field of the termcap 2493678Sroot * entry is a sequence of names separated by |'s, so we compare 2503678Sroot * against each such name. The normal : terminator after the last 2513678Sroot * name (before the first field) stops us. 2523678Sroot */ 2533841Ssam tnamatch(np) 2543678Sroot char *np; 2553678Sroot { 2563678Sroot register char *Np, *Bp; 2573678Sroot 2583841Ssam Bp = tbuf; 2593841Ssam if (*Bp == '#') 2603841Ssam return(0); 2613678Sroot for (;;) { 2623678Sroot for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 2633678Sroot continue; 2643678Sroot if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 2653678Sroot return (1); 2663678Sroot while (*Bp && *Bp != ':' && *Bp != '|') 2673678Sroot Bp++; 2683678Sroot if (*Bp == 0 || *Bp == ':') 2693678Sroot return (0); 2703678Sroot Bp++; 2713678Sroot } 2723678Sroot } 2733678Sroot 2743678Sroot /* 2753678Sroot * Skip to the next field. Notice that this is very dumb, not 2763678Sroot * knowing about \: escapes or any such. If necessary, :'s can be put 2773841Ssam * into the termcap file in octal. 2783678Sroot */ 2793678Sroot static char * 2803841Ssam tskip(bp) 2813678Sroot register char *bp; 2823678Sroot { 2833678Sroot 2843678Sroot while (*bp && *bp != ':') 2853678Sroot bp++; 2863678Sroot if (*bp == ':') 2873678Sroot bp++; 2883678Sroot return (bp); 2893678Sroot } 2903678Sroot 2913678Sroot /* 2923678Sroot * Return the (numeric) option id. 2933678Sroot * Numeric options look like 2943678Sroot * li#80 2953678Sroot * i.e. the option string is separated from the numeric value by 2963678Sroot * a # character. If the option is not found we return -1. 2973678Sroot * Note that we handle octal numbers beginning with 0. 2983678Sroot */ 2993841Ssam tgetnum(id) 3003678Sroot char *id; 3013678Sroot { 3023678Sroot register int i, base; 3033841Ssam register char *bp = tbuf; 3043678Sroot 3053678Sroot for (;;) { 3063841Ssam bp = tskip(bp); 3073678Sroot if (*bp == 0) 3083678Sroot return (-1); 3093678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3103678Sroot continue; 3113841Ssam if (*bp == '@') 3123841Ssam return(-1); 3133678Sroot if (*bp != '#') 3143678Sroot continue; 3153678Sroot bp++; 3163678Sroot base = 10; 3173678Sroot if (*bp == '0') 3183678Sroot base = 8; 3193678Sroot i = 0; 3203678Sroot while (isdigit(*bp)) 3213678Sroot i *= base, i += *bp++ - '0'; 3223678Sroot return (i); 3233678Sroot } 3243678Sroot } 3253678Sroot 3263678Sroot /* 3273678Sroot * Handle a flag option. 3283678Sroot * Flag options are given "naked", i.e. followed by a : or the end 3293678Sroot * of the buffer. Return 1 if we find the option, or 0 if it is 3303678Sroot * not given. 3313678Sroot */ 3323841Ssam tgetflag(id) 3333678Sroot char *id; 3343678Sroot { 3353841Ssam register char *bp = tbuf; 3363678Sroot 3373678Sroot for (;;) { 3383841Ssam bp = tskip(bp); 3393678Sroot if (!*bp) 3403678Sroot return (0); 3413841Ssam if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 3423841Ssam if (!*bp || *bp == ':') 3433841Ssam return (1); 3443841Ssam else if (*bp == '@') 3453841Ssam return(0); 3463841Ssam } 3473678Sroot } 3483678Sroot } 3493678Sroot 3503678Sroot /* 3513678Sroot * Get a string valued option. 3523678Sroot * These are given as 3533678Sroot * cl=^Z 3543678Sroot * Much decoding is done on the strings, and the strings are 3553678Sroot * placed in area, which is a ref parameter which is updated. 3563678Sroot * No checking on area overflow. 3573678Sroot */ 3583678Sroot char * 3593841Ssam tgetstr(id, area) 3603678Sroot char *id, **area; 3613678Sroot { 3623841Ssam register char *bp = tbuf; 3633678Sroot 3643678Sroot for (;;) { 3653841Ssam bp = tskip(bp); 3663678Sroot if (!*bp) 3673678Sroot return (0); 3683678Sroot if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 3693678Sroot continue; 3703841Ssam if (*bp == '@') 3713841Ssam return(0); 3723678Sroot if (*bp != '=') 3733678Sroot continue; 3743678Sroot bp++; 3753841Ssam return (tdecode(bp, area)); 3763678Sroot } 3773678Sroot } 3783678Sroot 3793678Sroot /* 3803678Sroot * Tdecode does the grung work to decode the 3813678Sroot * string capability escapes. 3823678Sroot */ 3833678Sroot static char * 3843841Ssam tdecode(str, area) 3853678Sroot register char *str; 3863678Sroot char **area; 3873678Sroot { 3883678Sroot register char *cp; 3893678Sroot register int c; 3903678Sroot register char *dp; 3913678Sroot int i; 3923678Sroot 3933678Sroot cp = *area; 3943678Sroot while ((c = *str++) && c != ':') { 3953678Sroot switch (c) { 3963678Sroot 3973678Sroot case '^': 3983678Sroot c = *str++ & 037; 3993678Sroot break; 4003678Sroot 4013678Sroot case '\\': 4023678Sroot dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 4033678Sroot c = *str++; 4043678Sroot nextc: 4053678Sroot if (*dp++ == c) { 4063678Sroot c = *dp++; 4073678Sroot break; 4083678Sroot } 4093678Sroot dp++; 4103678Sroot if (*dp) 4113678Sroot goto nextc; 4123678Sroot if (isdigit(c)) { 4133678Sroot c -= '0', i = 2; 4143678Sroot do 4153678Sroot c <<= 3, c |= *str++ - '0'; 4163678Sroot while (--i && isdigit(*str)); 4173678Sroot } 4183678Sroot break; 4193678Sroot } 4203678Sroot *cp++ = c; 4213678Sroot } 4223678Sroot *cp++ = 0; 4233678Sroot str = *area; 4243678Sroot *area = cp; 4253678Sroot return (str); 4263678Sroot } 427