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