Initial import from FreeBSD RELENG_4:
[games.git] / usr.sbin / pcvt / keycap / keycap.c
1 /*-
2  * Copyright (c) 1992, 1993 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Holger Veit
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
38 static char *id =
39         "@(#)keycap.c, 3.20, Last Edit-Date: [Tue Dec 20 14:51:50 1994]";
40
41 /*---------------------------------------------------------------------------*
42  *
43  *      keycap.c        Keyboard capabilities database handling
44  *      -------------------------------------------------------
45  *
46  *      converted from printcap by Holger Veit (veit@du9ds3.uni-duisburg.de)
47  *
48  *      BUG:    Should use a "last" pointer in tbuf, so that searching
49  *              for capabilities alphabetically would not be a n**2/2
50  *              process when large numbers of capabilities are given.
51  *
52  *      Note:   If we add a last pointer now we will screw up the
53  *              tc capability. We really should compile termcap.
54  *
55  *      modified by Hellmuth Michaelis (hm@hcshh.hcs.de) to fit into the
56  *      vt220 driver pcvt 2.0 distribution
57  *
58  *      -hm     header conversion & cosmetic changes for pcvt 2.0 distribution
59  *      -hm     debugging remapping
60  *      -hm     cleaning up from termcap ....
61  *      -hm     split off header file keycap.h
62  *
63  *---------------------------------------------------------------------------*/
64
65 #include <stdio.h>
66 #include <ctype.h>
67
68 #include "keycap.h"
69
70 #define KEYCAP_BUFSIZ   1024
71
72 #define MAXHOP  32              /* max number of tc= indirections */
73
74 char    *getenv();
75
76 static  FILE *pfp = NULL;       /* keycap data base file pointer */
77 static  char *tbuf;
78 static  int hopcount;           /* detect infinite loops in keycap, init 0 */
79
80 static int knchktc();
81 static int knamatch();
82 static char *kdecode();
83
84 /*---------------------------------------------------------------------------*
85  *      match a name
86  *---------------------------------------------------------------------------*/
87 static char *nmatch(id,cstr)
88 char *id,*cstr;
89 {
90         register n = strlen(id);
91         register char *c = cstr+n;
92
93         if (strncmp(id,cstr,n)==0 &&
94             (*c==':' || *c=='|' || *c=='=' || *c=='#') || *c=='@')
95                 return c;
96         return 0;
97 }
98
99 /*---------------------------------------------------------------------------*
100  * Get an entry for keyboard name in buffer bp from the keycap file.
101  * Parse is very rudimentary, we just notice escaped newlines.
102  *---------------------------------------------------------------------------*/
103 kgetent(bp, name)
104 char *bp, *name;
105 {
106         register char *cp;
107         register int c;
108         register int i = 0, cnt = 0;
109         char ibuf[KEYCAP_BUFSIZ];
110         char *cp2;
111         int tf;
112
113         tbuf = bp;
114         tf = 0;
115
116         tf = open(KEYCAP_PATH, 0);
117
118         if (tf < 0)
119                 return (-1);
120         for (;;) {
121                 cp = bp;
122                 for (;;) {
123                         if (i == cnt) {
124                                 cnt = read(tf, ibuf, KEYCAP_BUFSIZ);
125                                 if (cnt <= 0) {
126                                         close(tf);
127                                         return (0);
128                                 }
129                                 i = 0;
130                         }
131                         c = ibuf[i++];
132                         if (c == '\n') {
133                                 if (cp > bp && cp[-1] == '\\'){
134                                         cp--;
135                                         continue;
136                                 }
137                                 break;
138                         }
139                         if (cp >= bp+KEYCAP_BUFSIZ) {
140                                 write(2,"Keycap entry too long\n", 23);
141                                 break;
142                         } else
143                                 *cp++ = c;
144                 }
145                 *cp = 0;
146
147                 /*
148                  * The real work for the match.
149                  */
150                 if (knamatch(name)) {
151                         close(tf);
152                         return(knchktc());
153                 }
154         }
155 }
156
157 /*---------------------------------------------------------------------------*
158  * knchktc: check the last entry, see if it's tc=xxx. If so, recursively
159  * find xxx and append that entry (minus the names) to take the place of
160  * the tc=xxx entry. Note that this works because of the left to right scan.
161  *---------------------------------------------------------------------------*/
162 static int knchktc()
163 {
164         register char *p, *q;
165         char tcname[16];        /* name of similar keyboard */
166         char tcbuf[KEYCAP_BUFSIZ];
167         char *holdtbuf = tbuf;
168         int l;
169
170         p = tbuf + strlen(tbuf) - 2;    /* before the last colon */
171         while (*--p != ':')
172                 if (p<tbuf) {
173                         write(2, "Bad keycap entry\n", 18);
174                         return (0);
175                 }
176         p++;
177         /* p now points to beginning of last field */
178         if (p[0] != 't' || p[1] != 'c')
179                 return(1);
180         strcpy(tcname,p+3);
181         q = tcname;
182         while (q && *q != ':')
183                 q++;
184         *q = 0;
185         if (++hopcount > MAXHOP) {
186                 write(2, "Infinite tc= loop\n", 18);
187                 return (0);
188         }
189         if (kgetent(tcbuf, tcname) != 1)
190                 return(0);
191         for (q=tcbuf; *q != ':'; q++)
192                 ;
193         l = p - holdtbuf + strlen(q);
194         if (l > KEYCAP_BUFSIZ) {
195                 write(2, "Keycap entry too long\n", 23);
196                 q[KEYCAP_BUFSIZ - (p-tbuf)] = 0;
197         }
198         strcpy(p, q+1);
199         tbuf = holdtbuf;
200         return(1);
201 }
202
203 /*---------------------------------------------------------------------------*
204  * knamatch deals with name matching.  The first field of the keycap entry
205  * is a sequence of names separated by |'s, so we compare against each such
206  * name. The normal : terminator after the last name (before the first field)
207  * stops us.
208  *---------------------------------------------------------------------------*/
209 static int knamatch(np)
210 char *np;
211 {
212         register char *Np, *Bp;
213
214         Bp = tbuf;
215         if (*Bp == '#' || *Bp == 0)
216                 return(0);
217         for (;;) {
218                 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
219                         continue;
220                 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
221                         return (1);
222                 while (*Bp && *Bp != ':' && *Bp != '|')
223                         Bp++;
224                 if (*Bp == 0 || *Bp == ':')
225                         return (0);
226                 Bp++;
227         }
228 }
229
230 /*---------------------------------------------------------------------------*
231  * Skip to the next field. Notice that this is very dumb, not knowing about
232  * \: escapes or any such. If necessary, :'s can be put into the keycap file
233  * in octal.
234  *---------------------------------------------------------------------------*/
235 static char *kskip(bp)
236 char *bp;
237 {
238         while (*bp && *bp != ':')
239                 bp++;
240         if (*bp == ':')
241                 bp++;
242         return (bp);
243 }
244
245 /*---------------------------------------------------------------------------*
246  * Return the (numeric) option id. Numeric options look like 'li#80' i.e.
247  * the option string is separated from the numeric value by a # character.
248  * If the option is not found we return -1. Note that we handle octal
249  * numbers beginning with 0.
250  *---------------------------------------------------------------------------*/
251 int kgetnum(id)
252 char *id;
253 {
254         register int i, base;
255         register char *bp = tbuf,*xp;
256
257         for (;;) {
258                 bp = kskip(bp);
259                 if (*bp == 0)
260                         return (-1);
261                 if ((xp=nmatch(id,bp)) == 0)
262                         continue;
263                 bp = xp;        /* we have an entry */
264                 if (*bp == '@')
265                         return(-1);
266                 if (*bp != '#')
267                         continue;
268                 bp++;
269                 base = 10;
270                 if (*bp == '0')
271                         base = 8;
272                 i = 0;
273                 while (isdigit(*bp))
274                         i *= base, i += *bp++ - '0';
275                 return (i);
276         }
277 }
278
279 /*---------------------------------------------------------------------------*
280  * Handle a flag option. Flag options are given "naked", i.e. followed by
281  * a : or the end of the buffer.  Return 1 if we find the option, or 0 if
282  * it is not given.
283  *---------------------------------------------------------------------------*/
284 int kgetflag(id)
285 char *id;
286 {
287         register char *bp = tbuf,*xp;
288
289         for (;;) {
290                 bp = kskip(bp);
291                 if (!*bp)
292                         return (0);
293                 if ((xp=nmatch(id,bp)) != 0) {
294                         bp = xp;
295                         if (!*bp || *bp == ':')
296                                 return (1);
297                         else if (*bp == '@')
298                                 return(0);
299                 }
300         }
301 }
302
303 /*---------------------------------------------------------------------------*
304  * Get a string valued option. These are given as 'cl=^Z'. Much decoding
305  * is done on the strings, and the strings are placed in area, which is a
306  * ref parameter which is updated. No checking on area overflow.
307  *---------------------------------------------------------------------------*/
308 char *kgetstr(id, area)
309 char *id;
310 char **area;
311 {
312         register char *bp = tbuf,*xp;
313
314         for (;;) {
315                 bp = kskip(bp);
316                 if (!*bp)
317                         return (0);
318                 if ((xp = nmatch(id,bp)) == 0)
319                         continue;
320                 bp = xp;
321                 if (*bp == '@')
322                         return(0);
323                 if (*bp != '=')
324                         continue;
325                 bp++;
326                 return (kdecode(bp, area));
327         }
328 }
329
330 /*---------------------------------------------------------------------------*
331  * kdecode does the grung work to decode the string capability escapes.
332  *---------------------------------------------------------------------------*/
333 static char *kdecode(str, area)
334 char *str;
335 char **area;
336 {
337         register char *cp;
338         register int c;
339         register char *dp;
340         int i;
341
342         cp = *area;
343         while ((c = *str++) && c != ':') {
344                 switch (c) {
345
346                 case '^':
347                         c = *str++ & 037;
348                         break;
349
350                 case '\\':
351                         dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
352                         c = *str++;
353 nextc:
354                         if (*dp++ == c) {
355                                 c = *dp++;
356                                 break;
357                         }
358                         dp++;
359                         if (*dp)
360                                 goto nextc;
361                         if (isdigit(c)) {
362                                 c -= '0', i = 2;
363                                 do
364                                         c <<= 3, c |= *str++ - '0';
365                                 while (--i && isdigit(*str));
366                         }
367                         break;
368                 }
369                 *cp++ = c;
370         }
371         *cp++ = 0;
372         str = *area;
373         *area = cp;
374         return (str);
375 }
376
377 /*-------------------------------- EOF --------------------------------------*/