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