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
tgetent(char * bp,char * name,char * file)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
tnchktc(void)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
tnamatch(char * np)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 *
tskip(char * bp)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
tgetnum(char * id)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
tgetflag(char * id)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 *
tgetstr(char * id,char ** area)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 *
tdecode(char * str,char ** area)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