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