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