59600f0f2c6996d1d23161e70d55e7b5055eb4f6
[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  * @(#)vgrindefs.c      8.1 (Berkeley) 6/6/93
34  *
35  * $DragonFly: src/usr.bin/vgrind/vgrindefs.c,v 1.4 2004/09/03 20:38:01 dillon Exp $
36  */
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
64 /*
65  * Get an entry for terminal name in buffer bp,
66  * from the termcap file.  Parse is very rudimentary;
67  * we just notice escaped newlines.
68  */
69 int
70 tgetent(char *bp, char *name, char *file)
71 {
72         register char *cp;
73         register int c;
74         register int i = 0, cnt = 0;
75         char ibuf[BUFSIZ];
76         char *cp2;
77         int tf;
78
79         tbuf = bp;
80         tf = 0;
81         filename = file;
82         tf = open(filename, 0);
83         if (tf < 0)
84                 return (-1);
85         for (;;) {
86                 cp = bp;
87                 for (;;) {
88                         if (i == cnt) {
89                                 cnt = read(tf, ibuf, BUFSIZ);
90                                 if (cnt <= 0) {
91                                         close(tf);
92                                         return (0);
93                                 }
94                                 i = 0;
95                         }
96                         c = ibuf[i++];
97                         if (c == '\n') {
98                                 if (cp > bp && cp[-1] == '\\'){
99                                         cp--;
100                                         continue;
101                                 }
102                                 break;
103                         }
104                         if (cp >= bp+BUFSIZ) {
105                                 write(2,"Vgrind entry too long\n", 23);
106                                 break;
107                         } else
108                                 *cp++ = c;
109                 }
110                 *cp = 0;
111
112                 /*
113                  * The real work for the match.
114                  */
115                 if (tnamatch(name)) {
116                         close(tf);
117                         return(tnchktc());
118                 }
119         }
120 }
121
122 /*
123  * tnchktc: check the last entry, see if it's tc=xxx. If so,
124  * recursively find xxx and append that entry (minus the names)
125  * to take the place of the tc=xxx entry. This allows termcap
126  * entries to say "like an HP2621 but doesn't turn on the labels".
127  * Note that this works because of the left to right scan.
128  */
129 int
130 tnchktc(void)
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 int
178 tnamatch(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(register char *bp)
205 {
206
207         while (*bp && *bp != ':')
208                 bp++;
209         if (*bp == ':')
210                 bp++;
211         return (bp);
212 }
213
214 /*
215  * Return the (numeric) option id.
216  * Numeric options look like
217  *      li#80
218  * i.e. the option string is separated from the numeric value by
219  * a # character.  If the option is not found we return -1.
220  * Note that we handle octal numbers beginning with 0.
221  */
222 int
223 tgetnum(char *id)
224 {
225         register int i, base;
226         register char *bp = tbuf;
227
228         for (;;) {
229                 bp = tskip(bp);
230                 if (*bp == 0)
231                         return (-1);
232                 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
233                         continue;
234                 if (*bp == '@')
235                         return(-1);
236                 if (*bp != '#')
237                         continue;
238                 bp++;
239                 base = 10;
240                 if (*bp == '0')
241                         base = 8;
242                 i = 0;
243                 while (isdigit(*bp))
244                         i *= base, i += *bp++ - '0';
245                 return (i);
246         }
247 }
248
249 /*
250  * Handle a flag option.
251  * Flag options are given "naked", i.e. followed by a : or the end
252  * of the buffer.  Return 1 if we find the option, or 0 if it is
253  * not given.
254  */
255 int
256 tgetflag(char *id)
257 {
258         register char *bp = tbuf;
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 *
282 tgetstr(char *id, char **area)
283 {
284         register char *bp = tbuf;
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 /*
302  * Tdecode does the grung work to decode the
303  * string capability escapes.
304  */
305 static char *
306 tdecode(register char *str, char **area)
307 {
308         register char *cp;
309         register int c;
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 }