xref: /illumos-gate/usr/src/cmd/vgrind/vgrindefs.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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 #define	BUFSIZ	1024
8 #define MAXHOP	32	/* max number of tc= indirections */
9 
10 #include <ctype.h>
11 #include <locale.h>
12 #include <string.h>
13 /*
14  * grindcap - routines for dealing with the language definitions data base
15  *	(code stolen almost totally from termcap)
16  *
17  * BUG:		Should use a "last" pointer in tbuf, so that searching
18  *		for capabilities alphabetically would not be a n**2/2
19  *		process when large numbers of capabilities are given.
20  * Note:	If we add a last pointer now we will screw up the
21  *		tc capability. We really should compile termcap.
22  *
23  * Essentially all the work here is scanning and decoding escapes
24  * in string capabilities.  We don't use stdio because the editor
25  * doesn't, and because living w/o it is not hard.
26  */
27 
28 static	char *tbuf;
29 static	char *filename;
30 static	int hopcount;	/* detect infinite loops in termcap, init 0 */
31 char	*tgetstr();
32 char	*getenv();
33 
34 static char *tdecode(char *str, char **area);
35 static char *tskip(char *bp);
36 static int tnchktc(void);
37 static int tnamatch(char *np);
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 int
47 tgetent(char *bp, char *name, char *file)
48 {
49 	char *cp;
50 	int c;
51 	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 static int
108 tnchktc(void)
109 {
110 	char *p, *q;
111 	char tcname[16];	/* name of similar terminal */
112 	char tcbuf[BUFSIZ];
113 	char *holdtbuf = tbuf;
114 	int l;
115 
116 	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
117 	while (*--p != ':')
118 		if (p<tbuf) {
119 			vgrind_msg = gettext("Bad vgrind entry\n");
120 			write(2, vgrind_msg, strlen(vgrind_msg));
121 			return (0);
122 		}
123 	p++;
124 	/* p now points to beginning of last field */
125 	if (p[0] != 't' || p[1] != 'c')
126 		return(1);
127 	strcpy(tcname,p+3);
128 	q = tcname;
129 	while (q && *q != ':')
130 		q++;
131 	*q = 0;
132 	if (++hopcount > MAXHOP) {
133 		vgrind_msg = gettext("Infinite tc= loop\n");
134 		write(2, vgrind_msg, strlen(vgrind_msg));
135 		return (0);
136 	}
137 	if (tgetent(tcbuf, tcname, filename) != 1)
138 		return(0);
139 	for (q=tcbuf; *q != ':'; q++)
140 		;
141 	l = p - holdtbuf + strlen(q);
142 	if (l > BUFSIZ) {
143 		vgrind_msg = gettext("Vgrind entry too long\n");
144 		write(2, vgrind_msg, strlen(vgrind_msg));
145 		q[BUFSIZ - (p-tbuf)] = 0;
146 	}
147 	strcpy(p, q+1);
148 	tbuf = holdtbuf;
149 	return(1);
150 }
151 
152 /*
153  * Tnamatch deals with name matching.  The first field of the termcap
154  * entry is a sequence of names separated by |'s, so we compare
155  * against each such name.  The normal : terminator after the last
156  * name (before the first field) stops us.
157  */
158 static int
159 tnamatch(char *np)
160 {
161 	char *Np, *Bp;
162 
163 	Bp = tbuf;
164 	if (*Bp == '#')
165 		return(0);
166 	for (;;) {
167 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
168 			continue;
169 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
170 			return (1);
171 		while (*Bp && *Bp != ':' && *Bp != '|')
172 			Bp++;
173 		if (*Bp == 0 || *Bp == ':')
174 			return (0);
175 		Bp++;
176 	}
177 }
178 
179 /*
180  * Skip to the next field.  Notice that this is very dumb, not
181  * knowing about \: escapes or any such.  If necessary, :'s can be put
182  * into the termcap file in octal.
183  */
184 static char *
185 tskip(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 static int
204 tgetnum(char *id)
205 {
206 	int i, base;
207 	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 int
237 tgetflag(char *id)
238 {
239 	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(char *id, char **area)
264 {
265 	char *bp = tbuf;
266 
267 	for (;;) {
268 		bp = tskip(bp);
269 		if (!*bp)
270 			return (0);
271 		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
272 			continue;
273 		if (*bp == '@')
274 			return(0);
275 		if (*bp != '=')
276 			continue;
277 		bp++;
278 		return (tdecode(bp, area));
279 	}
280 }
281 
282 /*
283  * Tdecode does the grung work to decode the
284  * string capability escapes.
285  */
286 static char *
287 tdecode(char *str, char **area)
288 {
289 	char *cp;
290 	int c;
291 	int i;
292 
293 	cp = *area;
294 	while (c = *str++) {
295 	    if (c == ':' && *(cp-1) != '\\')
296 		break;
297 	    *cp++ = c;
298 	}
299 	*cp++ = 0;
300 	str = *area;
301 	*area = cp;
302 	return (str);
303 }
304