xref: /csrg-svn/usr.bin/vgrind/vgrindefs.c (revision 8662)
1*8662Smckusick /* Copyright (c) 1979 Regents of the University of California */
2*8662Smckusick 
3*8662Smckusick static char sccsid[] = "@(#)vgrindefs.c	4.1	(Berkeley)	10/19/82";
4*8662Smckusick 
5*8662Smckusick #define	BUFSIZ	1024
6*8662Smckusick #define MAXHOP	32	/* max number of tc= indirections */
7*8662Smckusick 
8*8662Smckusick #include <ctype.h>
9*8662Smckusick #include "local/uparm.h"
10*8662Smckusick /*
11*8662Smckusick  * grindcap - routines for dealing with the language definitions data base
12*8662Smckusick  *	(code stolen almost totally from termcap)
13*8662Smckusick  *
14*8662Smckusick  * BUG:		Should use a "last" pointer in tbuf, so that searching
15*8662Smckusick  *		for capabilities alphabetically would not be a n**2/2
16*8662Smckusick  *		process when large numbers of capabilities are given.
17*8662Smckusick  * Note:	If we add a last pointer now we will screw up the
18*8662Smckusick  *		tc capability. We really should compile termcap.
19*8662Smckusick  *
20*8662Smckusick  * Essentially all the work here is scanning and decoding escapes
21*8662Smckusick  * in string capabilities.  We don't use stdio because the editor
22*8662Smckusick  * doesn't, and because living w/o it is not hard.
23*8662Smckusick  */
24*8662Smckusick 
25*8662Smckusick static	char *tbuf;
26*8662Smckusick static	int hopcount;	/* detect infinite loops in termcap, init 0 */
27*8662Smckusick char	*tskip();
28*8662Smckusick char	*tgetstr();
29*8662Smckusick char	*tdecode();
30*8662Smckusick char	*getenv();
31*8662Smckusick 
32*8662Smckusick /*
33*8662Smckusick  * Get an entry for terminal name in buffer bp,
34*8662Smckusick  * from the termcap file.  Parse is very rudimentary;
35*8662Smckusick  * we just notice escaped newlines.
36*8662Smckusick  */
37*8662Smckusick tgetent(bp, name, filename)
38*8662Smckusick 	char *bp, *name, *filename;
39*8662Smckusick {
40*8662Smckusick 	register char *cp;
41*8662Smckusick 	register int c;
42*8662Smckusick 	register int i = 0, cnt = 0;
43*8662Smckusick 	char ibuf[BUFSIZ];
44*8662Smckusick 	char *cp2;
45*8662Smckusick 	int tf;
46*8662Smckusick 
47*8662Smckusick 	tbuf = bp;
48*8662Smckusick 	tf = 0;
49*8662Smckusick 	tf = open(filename, 0);
50*8662Smckusick 	if (tf < 0)
51*8662Smckusick 		return (-1);
52*8662Smckusick 	for (;;) {
53*8662Smckusick 		cp = bp;
54*8662Smckusick 		for (;;) {
55*8662Smckusick 			if (i == cnt) {
56*8662Smckusick 				cnt = read(tf, ibuf, BUFSIZ);
57*8662Smckusick 				if (cnt <= 0) {
58*8662Smckusick 					close(tf);
59*8662Smckusick 					return (0);
60*8662Smckusick 				}
61*8662Smckusick 				i = 0;
62*8662Smckusick 			}
63*8662Smckusick 			c = ibuf[i++];
64*8662Smckusick 			if (c == '\n') {
65*8662Smckusick 				if (cp > bp && cp[-1] == '\\'){
66*8662Smckusick 					cp--;
67*8662Smckusick 					continue;
68*8662Smckusick 				}
69*8662Smckusick 				break;
70*8662Smckusick 			}
71*8662Smckusick 			if (cp >= bp+BUFSIZ) {
72*8662Smckusick 				write(2,"Vgrind entry too long\n", 23);
73*8662Smckusick 				break;
74*8662Smckusick 			} else
75*8662Smckusick 				*cp++ = c;
76*8662Smckusick 		}
77*8662Smckusick 		*cp = 0;
78*8662Smckusick 
79*8662Smckusick 		/*
80*8662Smckusick 		 * The real work for the match.
81*8662Smckusick 		 */
82*8662Smckusick 		if (tnamatch(name)) {
83*8662Smckusick 			close(tf);
84*8662Smckusick 			return(tnchktc());
85*8662Smckusick 		}
86*8662Smckusick 	}
87*8662Smckusick }
88*8662Smckusick 
89*8662Smckusick /*
90*8662Smckusick  * tnchktc: check the last entry, see if it's tc=xxx. If so,
91*8662Smckusick  * recursively find xxx and append that entry (minus the names)
92*8662Smckusick  * to take the place of the tc=xxx entry. This allows termcap
93*8662Smckusick  * entries to say "like an HP2621 but doesn't turn on the labels".
94*8662Smckusick  * Note that this works because of the left to right scan.
95*8662Smckusick  */
96*8662Smckusick tnchktc()
97*8662Smckusick {
98*8662Smckusick 	register char *p, *q;
99*8662Smckusick 	char tcname[16];	/* name of similar terminal */
100*8662Smckusick 	char tcbuf[BUFSIZ];
101*8662Smckusick 	char *holdtbuf = tbuf;
102*8662Smckusick 	int l;
103*8662Smckusick 
104*8662Smckusick 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
105*8662Smckusick 	while (*--p != ':')
106*8662Smckusick 		if (p<tbuf) {
107*8662Smckusick 			write(2, "Bad vgrind entry\n", 18);
108*8662Smckusick 			return (0);
109*8662Smckusick 		}
110*8662Smckusick 	p++;
111*8662Smckusick 	/* p now points to beginning of last field */
112*8662Smckusick 	if (p[0] != 't' || p[1] != 'c')
113*8662Smckusick 		return(1);
114*8662Smckusick 	strcpy(tcname,p+3);
115*8662Smckusick 	q = tcname;
116*8662Smckusick 	while (q && *q != ':')
117*8662Smckusick 		q++;
118*8662Smckusick 	*q = 0;
119*8662Smckusick 	if (++hopcount > MAXHOP) {
120*8662Smckusick 		write(2, "Infinite tc= loop\n", 18);
121*8662Smckusick 		return (0);
122*8662Smckusick 	}
123*8662Smckusick 	if (tgetent(tcbuf, tcname) != 1)
124*8662Smckusick 		return(0);
125*8662Smckusick 	for (q=tcbuf; *q != ':'; q++)
126*8662Smckusick 		;
127*8662Smckusick 	l = p - holdtbuf + strlen(q);
128*8662Smckusick 	if (l > BUFSIZ) {
129*8662Smckusick 		write(2, "Vgrind entry too long\n", 23);
130*8662Smckusick 		q[BUFSIZ - (p-tbuf)] = 0;
131*8662Smckusick 	}
132*8662Smckusick 	strcpy(p, q+1);
133*8662Smckusick 	tbuf = holdtbuf;
134*8662Smckusick 	return(1);
135*8662Smckusick }
136*8662Smckusick 
137*8662Smckusick /*
138*8662Smckusick  * Tnamatch deals with name matching.  The first field of the termcap
139*8662Smckusick  * entry is a sequence of names separated by |'s, so we compare
140*8662Smckusick  * against each such name.  The normal : terminator after the last
141*8662Smckusick  * name (before the first field) stops us.
142*8662Smckusick  */
143*8662Smckusick tnamatch(np)
144*8662Smckusick 	char *np;
145*8662Smckusick {
146*8662Smckusick 	register char *Np, *Bp;
147*8662Smckusick 
148*8662Smckusick 	Bp = tbuf;
149*8662Smckusick 	if (*Bp == '#')
150*8662Smckusick 		return(0);
151*8662Smckusick 	for (;;) {
152*8662Smckusick 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
153*8662Smckusick 			continue;
154*8662Smckusick 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
155*8662Smckusick 			return (1);
156*8662Smckusick 		while (*Bp && *Bp != ':' && *Bp != '|')
157*8662Smckusick 			Bp++;
158*8662Smckusick 		if (*Bp == 0 || *Bp == ':')
159*8662Smckusick 			return (0);
160*8662Smckusick 		Bp++;
161*8662Smckusick 	}
162*8662Smckusick }
163*8662Smckusick 
164*8662Smckusick /*
165*8662Smckusick  * Skip to the next field.  Notice that this is very dumb, not
166*8662Smckusick  * knowing about \: escapes or any such.  If necessary, :'s can be put
167*8662Smckusick  * into the termcap file in octal.
168*8662Smckusick  */
169*8662Smckusick static char *
170*8662Smckusick tskip(bp)
171*8662Smckusick 	register char *bp;
172*8662Smckusick {
173*8662Smckusick 
174*8662Smckusick 	while (*bp && *bp != ':')
175*8662Smckusick 		bp++;
176*8662Smckusick 	if (*bp == ':')
177*8662Smckusick 		bp++;
178*8662Smckusick 	return (bp);
179*8662Smckusick }
180*8662Smckusick 
181*8662Smckusick /*
182*8662Smckusick  * Return the (numeric) option id.
183*8662Smckusick  * Numeric options look like
184*8662Smckusick  *	li#80
185*8662Smckusick  * i.e. the option string is separated from the numeric value by
186*8662Smckusick  * a # character.  If the option is not found we return -1.
187*8662Smckusick  * Note that we handle octal numbers beginning with 0.
188*8662Smckusick  */
189*8662Smckusick tgetnum(id)
190*8662Smckusick 	char *id;
191*8662Smckusick {
192*8662Smckusick 	register int i, base;
193*8662Smckusick 	register char *bp = tbuf;
194*8662Smckusick 
195*8662Smckusick 	for (;;) {
196*8662Smckusick 		bp = tskip(bp);
197*8662Smckusick 		if (*bp == 0)
198*8662Smckusick 			return (-1);
199*8662Smckusick 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
200*8662Smckusick 			continue;
201*8662Smckusick 		if (*bp == '@')
202*8662Smckusick 			return(-1);
203*8662Smckusick 		if (*bp != '#')
204*8662Smckusick 			continue;
205*8662Smckusick 		bp++;
206*8662Smckusick 		base = 10;
207*8662Smckusick 		if (*bp == '0')
208*8662Smckusick 			base = 8;
209*8662Smckusick 		i = 0;
210*8662Smckusick 		while (isdigit(*bp))
211*8662Smckusick 			i *= base, i += *bp++ - '0';
212*8662Smckusick 		return (i);
213*8662Smckusick 	}
214*8662Smckusick }
215*8662Smckusick 
216*8662Smckusick /*
217*8662Smckusick  * Handle a flag option.
218*8662Smckusick  * Flag options are given "naked", i.e. followed by a : or the end
219*8662Smckusick  * of the buffer.  Return 1 if we find the option, or 0 if it is
220*8662Smckusick  * not given.
221*8662Smckusick  */
222*8662Smckusick tgetflag(id)
223*8662Smckusick 	char *id;
224*8662Smckusick {
225*8662Smckusick 	register char *bp = tbuf;
226*8662Smckusick 
227*8662Smckusick 	for (;;) {
228*8662Smckusick 		bp = tskip(bp);
229*8662Smckusick 		if (!*bp)
230*8662Smckusick 			return (0);
231*8662Smckusick 		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
232*8662Smckusick 			if (!*bp || *bp == ':')
233*8662Smckusick 				return (1);
234*8662Smckusick 			else if (*bp == '@')
235*8662Smckusick 				return(0);
236*8662Smckusick 		}
237*8662Smckusick 	}
238*8662Smckusick }
239*8662Smckusick 
240*8662Smckusick /*
241*8662Smckusick  * Get a string valued option.
242*8662Smckusick  * These are given as
243*8662Smckusick  *	cl=^Z
244*8662Smckusick  * Much decoding is done on the strings, and the strings are
245*8662Smckusick  * placed in area, which is a ref parameter which is updated.
246*8662Smckusick  * No checking on area overflow.
247*8662Smckusick  */
248*8662Smckusick char *
249*8662Smckusick tgetstr(id, area)
250*8662Smckusick 	char *id, **area;
251*8662Smckusick {
252*8662Smckusick 	register char *bp = tbuf;
253*8662Smckusick 
254*8662Smckusick 	for (;;) {
255*8662Smckusick 		bp = tskip(bp);
256*8662Smckusick 		if (!*bp)
257*8662Smckusick 			return (0);
258*8662Smckusick 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
259*8662Smckusick 			continue;
260*8662Smckusick 		if (*bp == '@')
261*8662Smckusick 			return(0);
262*8662Smckusick 		if (*bp != '=')
263*8662Smckusick 			continue;
264*8662Smckusick 		bp++;
265*8662Smckusick 		return (tdecode(bp, area));
266*8662Smckusick 	}
267*8662Smckusick }
268*8662Smckusick 
269*8662Smckusick /*
270*8662Smckusick  * Tdecode does the grung work to decode the
271*8662Smckusick  * string capability escapes.
272*8662Smckusick  */
273*8662Smckusick static char *
274*8662Smckusick tdecode(str, area)
275*8662Smckusick 	register char *str;
276*8662Smckusick 	char **area;
277*8662Smckusick {
278*8662Smckusick 	register char *cp;
279*8662Smckusick 	register int c;
280*8662Smckusick 	int i;
281*8662Smckusick 
282*8662Smckusick 	cp = *area;
283*8662Smckusick 	while (c = *str++) {
284*8662Smckusick 	    if (c == ':' && *(cp-1) != '\\')
285*8662Smckusick 		break;
286*8662Smckusick 	    *cp++ = c;
287*8662Smckusick 	}
288*8662Smckusick 	*cp++ = 0;
289*8662Smckusick 	str = *area;
290*8662Smckusick 	*area = cp;
291*8662Smckusick 	return (str);
292*8662Smckusick }
293