Annotation of src/usr.bin/vgrind/vgrindefs.c, Revision 1.6
1.6 ! deraadt 1: /* $OpenBSD: vgrindefs.c,v 1.5 2003/02/19 07:33:34 deraadt Exp $ */
1.1 deraadt 2: /* $NetBSD: vgrindefs.c,v 1.5 1994/12/20 12:05:29 cgd Exp $ */
3:
4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
38: #if 0
39: static char sccsid[] = "@(#)vgrindefs.c 8.1 (Berkeley) 6/6/93";
40: #endif
1.6 ! deraadt 41: static char rcsid[] = "$OpenBSD: vgrindefs.c,v 1.5 2003/02/19 07:33:34 deraadt Exp $";
1.1 deraadt 42: #endif /* not lint */
43:
44: #define BUFSIZ 1024
45: #define MAXHOP 32 /* max number of tc= indirections */
46:
47: #include <stdlib.h>
48: #include <string.h>
49: #include <ctype.h>
50: /*
51: * grindcap - routines for dealing with the language definitions data base
52: * (code stolen almost totally from termcap)
53: *
54: * BUG: Should use a "last" pointer in tbuf, so that searching
55: * for capabilities alphabetically would not be a n**2/2
56: * process when large numbers of capabilities are given.
57: * Note: If we add a last pointer now we will screw up the
58: * tc capability. We really should compile termcap.
59: *
60: * Essentially all the work here is scanning and decoding escapes
61: * in string capabilities. We don't use stdio because the editor
62: * doesn't, and because living w/o it is not hard.
63: */
64:
65: static char *tbuf;
66: static char *filename;
67: static int hopcount; /* detect infinite loops in termcap, init 0 */
68: static char *tskip();
69: static char *tdecode();
70: char *tgetstr();
71:
72: /*
73: * Get an entry for terminal name in buffer bp,
74: * from the termcap file. Parse is very rudimentary;
75: * we just notice escaped newlines.
76: */
1.6 ! deraadt 77: tgetent(char *bp, char *name, char *file)
1.1 deraadt 78: {
1.6 ! deraadt 79: char ibuf[BUFSIZ], *cp, *cp2;
! 80: int i = 0, cnt = 0, c;
1.1 deraadt 81: int tf;
82:
83: tbuf = bp;
84: tf = 0;
85: filename = file;
86: tf = open(filename, 0);
87: if (tf < 0)
88: return (-1);
89: for (;;) {
90: cp = bp;
91: for (;;) {
92: if (i == cnt) {
93: cnt = read(tf, ibuf, BUFSIZ);
94: if (cnt <= 0) {
95: close(tf);
96: return (0);
97: }
98: i = 0;
99: }
100: c = ibuf[i++];
101: if (c == '\n') {
102: if (cp > bp && cp[-1] == '\\'){
103: cp--;
104: continue;
105: }
106: break;
107: }
108: if (cp >= bp+BUFSIZ) {
109: write(2,"Vgrind entry too long\n", 23);
110: break;
111: } else
112: *cp++ = c;
113: }
114: *cp = 0;
115:
116: /*
117: * The real work for the match.
118: */
119: if (tnamatch(name)) {
120: close(tf);
121: return(tnchktc());
122: }
123: }
124: }
125:
126: /*
127: * tnchktc: check the last entry, see if it's tc=xxx. If so,
128: * recursively find xxx and append that entry (minus the names)
129: * to take the place of the tc=xxx entry. This allows termcap
130: * entries to say "like an HP2621 but doesn't turn on the labels".
131: * Note that this works because of the left to right scan.
132: */
1.6 ! deraadt 133: tnchktc(void)
1.1 deraadt 134: {
1.3 mpech 135: char *p, *q;
1.1 deraadt 136: char tcname[16]; /* name of similar terminal */
137: char tcbuf[BUFSIZ];
138: char *holdtbuf = tbuf;
139: int l;
140:
141: p = tbuf + strlen(tbuf) - 2; /* before the last colon */
142: while (*--p != ':')
143: if (p<tbuf) {
144: write(2, "Bad vgrind entry\n", 18);
145: return (0);
146: }
147: p++;
148: /* p now points to beginning of last field */
149: if (p[0] != 't' || p[1] != 'c')
150: return(1);
1.5 deraadt 151: strlcpy(tcname, p+3, sizeof tcname);
1.1 deraadt 152: q = tcname;
153: while (q && *q != ':')
154: q++;
155: *q = 0;
156: if (++hopcount > MAXHOP) {
157: write(2, "Infinite tc= loop\n", 18);
158: return (0);
159: }
160: if (tgetent(tcbuf, tcname, filename) != 1)
161: return(0);
162: for (q=tcbuf; *q != ':'; q++)
163: ;
164: l = p - holdtbuf + strlen(q);
165: if (l > BUFSIZ) {
166: write(2, "Vgrind entry too long\n", 23);
167: q[BUFSIZ - (p-tbuf)] = 0;
168: }
169: strcpy(p, q+1);
170: tbuf = holdtbuf;
171: return(1);
172: }
173:
174: /*
175: * Tnamatch deals with name matching. The first field of the termcap
176: * entry is a sequence of names separated by |'s, so we compare
177: * against each such name. The normal : terminator after the last
178: * name (before the first field) stops us.
179: */
1.6 ! deraadt 180: tnamatch(char *np)
1.1 deraadt 181: {
1.3 mpech 182: char *Np, *Bp;
1.1 deraadt 183:
184: Bp = tbuf;
185: if (*Bp == '#')
186: return(0);
187: for (;;) {
188: for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
189: continue;
190: if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
191: return (1);
192: while (*Bp && *Bp != ':' && *Bp != '|')
193: Bp++;
194: if (*Bp == 0 || *Bp == ':')
195: return (0);
196: Bp++;
197: }
198: }
199:
200: /*
201: * Skip to the next field. Notice that this is very dumb, not
202: * knowing about \: escapes or any such. If necessary, :'s can be put
203: * into the termcap file in octal.
204: */
205: static char *
1.6 ! deraadt 206: tskip(char *bp)
1.1 deraadt 207: {
208:
209: while (*bp && *bp != ':')
210: bp++;
211: if (*bp == ':')
212: bp++;
213: return (bp);
214: }
215:
216: /*
217: * Return the (numeric) option id.
218: * Numeric options look like
219: * li#80
220: * i.e. the option string is separated from the numeric value by
221: * a # character. If the option is not found we return -1.
222: * Note that we handle octal numbers beginning with 0.
223: */
1.6 ! deraadt 224: tgetnum(char *id)
1.1 deraadt 225: {
1.3 mpech 226: int i, base;
227: char *bp = tbuf;
1.1 deraadt 228:
229: for (;;) {
230: bp = tskip(bp);
231: if (*bp == 0)
232: return (-1);
233: if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
234: continue;
235: if (*bp == '@')
236: return(-1);
237: if (*bp != '#')
238: continue;
239: bp++;
240: base = 10;
241: if (*bp == '0')
242: base = 8;
243: i = 0;
244: while (isdigit(*bp))
245: i *= base, i += *bp++ - '0';
246: return (i);
247: }
248: }
249:
250: /*
251: * Handle a flag option.
252: * Flag options are given "naked", i.e. followed by a : or the end
253: * of the buffer. Return 1 if we find the option, or 0 if it is
254: * not given.
255: */
1.6 ! deraadt 256: tgetflag(char *id)
1.1 deraadt 257: {
1.3 mpech 258: char *bp = tbuf;
1.1 deraadt 259:
260: for (;;) {
261: bp = tskip(bp);
262: if (!*bp)
263: return (0);
264: if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
265: if (!*bp || *bp == ':')
266: return (1);
267: else if (*bp == '@')
268: return(0);
269: }
270: }
271: }
272:
273: /*
274: * Get a string valued option.
275: * These are given as
276: * cl=^Z
277: * Much decoding is done on the strings, and the strings are
278: * placed in area, which is a ref parameter which is updated.
279: * No checking on area overflow.
280: */
281: char *
1.6 ! deraadt 282: tgetstr(char *id, char **area)
1.1 deraadt 283: {
1.3 mpech 284: char *bp = tbuf;
1.1 deraadt 285:
286: for (;;) {
287: bp = tskip(bp);
288: if (!*bp)
289: return (0);
290: if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
291: continue;
292: if (*bp == '@')
293: return(0);
294: if (*bp != '=')
295: continue;
296: bp++;
297: return (tdecode(bp, area));
298: }
299: }
300:
301: /*
1.4 deraadt 302: * Tdecode does the grunt work to decode the
1.1 deraadt 303: * string capability escapes.
304: */
305: static char *
1.6 ! deraadt 306: tdecode(char *str, char **area)
1.1 deraadt 307: {
1.3 mpech 308: char *cp;
309: int c;
1.1 deraadt 310: int i;
311:
312: cp = *area;
313: while (c = *str++) {
314: if (c == ':' && *(cp-1) != '\\')
315: break;
316: *cp++ = c;
317: }
318: *cp++ = 0;
319: str = *area;
320: *area = cp;
321: return (str);
322: }