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