122436Sdist /*
2*61840Sbostic  * Copyright (c) 1983, 1993
3*61840Sbostic  *	The Regents of the University of California.  All rights reserved.
434203Sbostic  *
542801Sbostic  * %sccs.include.redist.c%
622436Sdist  */
722436Sdist 
813954Ssam #ifndef lint
9*61840Sbostic static char sccsid[] = "@(#)printcap.c	8.1 (Berkeley) 06/06/93";
1034203Sbostic #endif /* not lint */
1113954Ssam 
1255470Sbostic #include <sys/param.h>
1355470Sbostic 
1455470Sbostic #include <fcntl.h>
1555470Sbostic #include <dirent.h>
1655470Sbostic #include <unistd.h>
1755470Sbostic #include <stdio.h>
1846978Sbostic #include <ctype.h>
1955470Sbostic #include <string.h>
2055470Sbostic #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 
6155470Sbostic static char *tskip __P((char *));
6255470Sbostic static char *tskip __P((char *bp));
6355470Sbostic static char *tdecode __P((char *, char **));
6455470Sbostic 
653678Sroot /*
6612429Sralph  * Similar to tgetent except it returns the next enrty instead of
6712429Sralph  * doing a lookup.
6812429Sralph  */
6955470Sbostic 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 
11155470Sbostic 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  */
12355470Sbostic 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  */
20755470Sbostic 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  */
25555470Sbostic 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  */
30255470Sbostic 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  */
33655470Sbostic 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 }
43255470Sbostic 
433