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