adding GNU dc ("desk calculator")
[unix-history] / usr.sbin / keymap / lib / keycap.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Parts Copyright 1992 by Holger Veit
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * This code has been derived from the PRINTCAP source code that is part
36 * of 386bsd which underlies the above Copyright. Changes have been made
37 * by Holger Veit (-hv-)
38 *
39 * 29/07/92 -hv- copied and adapted from printcap common_source
40 *
41 * -hv-: Hey, after gettytab, termcap and printcap this is the fourth time,
42 * that this code has been adapted. Now, we should perhaps think about
43 * a universal capabilities library which unites all four and allows different
44 * data bases to be checked with a unified interface. This must be
45 * documented of course in great detail. Any volunteers?
46 *
47 * "Suddenly, it was so quiet, one could almost hear a byte drop to
48 * the floor..."
49 * "1000 methods to get rid of hackers"
50 * by H. Veit, to appear RSN :-)
51 */
52
53#ifndef lint
54static char sccsid[] = "@(#)keycap.c 1.0 (Contributed to 386bsd) 7/29/92";
55#endif /* not lint */
56
57#include <ctype.h>
58#include <stdio.h>
59#include "pathnames.h"
60
61#ifndef BUFSIZ
62#define BUFSIZ 1024
63#endif
64#define MAXHOP 32 /* max number of tc= indirections */
65
66/*
67 * keycap - routines for dealing with the keyboard capability data base
68 *
69 * BUG: Should use a "last" pointer in tbuf, so that searching
70 * for capabilities alphabetically would not be a n**2/2
71 * process when large numbers of capabilities are given.
72 * Note: If we add a last pointer now we will screw up the
73 * tc capability. We really should compile termcap.
74 *
75 * Essentially all the work here is scanning and decoding escapes
76 * in string capabilities. We don't use stdio because the editor
77 * doesn't, and because living w/o it is not hard.
78 */
79
80#define KEYCAP
81
82#ifdef KEYCAP
83#define tgetent kgetent
84#define tskip kskip
85#define tgetstr kgetstr
86#define tdecode kdecode
87#define tgetnum kgetnum
88#define tgetflag kgetflag
89#define tdecode kdecode
90#define tnchktc knchktc
91#define tnamatch knamatch
92#define V6
93#endif
94
95static FILE *pfp = NULL; /* keycap data base file pointer */
96static char *tbuf;
97static int hopcount; /* detect infinite loops in keycap, init 0 */
98char *tskip();
99char *tgetstr();
100char *tdecode();
101char *getenv();
102
103/*
104 *
105 * -hv- added to understand capability names longer than 2 chars
106 * which is very important if we do not want to crypt the key codes
107 */
108static char *nmatch(id,cstr)
109 char *id,*cstr;
110{
111 register n = strlen(id);
112 register char *c = cstr+n;
113
114 if (strncmp(id,cstr,n)==0 &&
115 (*c==':' || *c=='|' || *c=='=' || *c=='#' || *c=='@')) {
116 return c;
117 }
118 return 0;
119}
120
121#ifdef NOTUSED
122/*
123 * Similar to tgetent except it returns the next entry instead of
124 * doing a lookup.
125 */
126getprent(bp)
127 register char *bp;
128{
129 register int c, skip = 0;
130
131 if (pfp == NULL && (pfp = fopen(_PATH_KEYCAP, "r")) == NULL)
132 return(-1);
133 tbuf = bp;
134 for (;;) {
135 switch (c = getc(pfp)) {
136 case EOF:
137 fclose(pfp);
138 pfp = NULL;
139 return(0);
140 case '\n':
141 if (bp == tbuf) {
142 skip = 0;
143 continue;
144 }
145 if (bp[-1] == '\\') {
146 bp--;
147 continue;
148 }
149 *bp = '\0';
150 return(1);
151 case '#':
152 if (bp == tbuf)
153 skip++;
154 default:
155 if (skip)
156 continue;
157 if (bp >= tbuf+BUFSIZ) {
158 write(2, "Keycap entry too long\n", 23);
159 *bp = '\0';
160 return(1);
161 }
162 *bp++ = c;
163 }
164 }
165}
166
167endprent()
168{
169 if (pfp != NULL)
170 fclose(pfp);
171}
172#endif
173
174/*
175 * Get an entry for keyboard name in buffer bp,
176 * from the keycap file. Parse is very rudimentary;
177 * we just notice escaped newlines.
178 *
179 */
180tgetent(bp, name)
181 char *bp, *name;
182{
183
184 register char *cp;
185 register int c;
186 register int i = 0, cnt = 0;
187 char ibuf[BUFSIZ];
188 char *cp2;
189 int tf;
190
191 tbuf = bp;
192 tf = 0;
193
194 cp = getenv("KEYCAP");
195 /*
196 * KEYCAP can have one of two things in it. It can be the
197 * name of a file to use instead of /usr/share/misc/keycap. In this
198 * case it better start with a "/". Or it can be an entry to
199 * use so we don't have to read the file. In this case it
200 * has to already have the newlines crunched out.
201 */
202 if (cp && *cp) {
203 if (*cp!='/') {
204 cp2 = getenv("KEYBOARD");
205 if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
206 strcpy(bp,cp);
207 return(tnchktc());
208 } else {
209 tf = open(_PATH_KEYCAP, 0);
210 }
211 } else
212 tf = open(cp, 0);
213 }
214 if (tf==0)
215 tf = open(_PATH_KEYCAP, 0);
216
217 if (tf < 0)
218 return (-1);
219 for (;;) {
220 cp = bp;
221 for (;;) {
222 if (i == cnt) {
223 cnt = read(tf, ibuf, BUFSIZ);
224 if (cnt <= 0) {
225 close(tf);
226 return (0);
227 }
228 i = 0;
229 }
230 c = ibuf[i++];
231 if (c == '\n') {
232 if (cp > bp && cp[-1] == '\\'){
233 cp--;
234 continue;
235 }
236 break;
237 }
238 if (cp >= bp+BUFSIZ) {
239 write(2,"Keycap entry too long\n", 22);
240 break;
241 } else
242 *cp++ = c;
243 }
244 *cp = 0;
245
246 /*
247 * The real work for the match.
248 */
249 if (tnamatch(name)) {
250 close(tf);
251 return(tnchktc());
252 }
253 }
254}
255
256/*
257 * tnchktc: check the last entry, see if it's tc=xxx. If so,
258 * recursively find xxx and append that entry (minus the names)
259 * to take the place of the tc=xxx entry. This allows keycap
260 * entries to say "like an HP2621 but doesn't turn on the labels".
261 * Note that this works because of the left to right scan.
262 */
263tnchktc()
264{
265 register char *p, *q;
266 char tcname[16]; /* name of similar keyboard */
267 char tcbuf[BUFSIZ];
268 char *holdtbuf = tbuf;
269 int l;
270
271 p = tbuf + strlen(tbuf) - 2; /* before the last colon */
272 while (*--p != ':')
273 if (p<tbuf) {
274 write(2, "Bad keycap entry\n", 17);
275 return (0);
276 }
277 p++;
278 /* p now points to beginning of last field */
279 if (p[0] != 't' || p[1] != 'c')
280 return(1);
281 strcpy(tcname,p+3);
282 q = tcname;
283 while (q && *q != ':')
284 q++;
285 *q = 0;
286 if (++hopcount > MAXHOP) {
287 write(2, "Infinite tc= loop\n", 18);
288 return (0);
289 }
290 if (tgetent(tcbuf, tcname) != 1)
291 return(0);
292 for (q=tcbuf; *q != ':'; q++)
293 ;
294 l = p - holdtbuf + strlen(q);
295 if (l > BUFSIZ) {
296 write(2, "Keycap entry too long\n", 22);
297 q[BUFSIZ - (p-tbuf)] = 0;
298 }
299 strcpy(p, q+1);
300 tbuf = holdtbuf;
301 return(1);
302}
303
304/*
305 * Tnamatch deals with name matching. The first field of the keycap
306 * entry is a sequence of names separated by |'s, so we compare
307 * against each such name. The normal : terminator after the last
308 * name (before the first field) stops us.
309 */
310tnamatch(np)
311 char *np;
312{
313 register char *Np, *Bp;
314
315 Bp = tbuf;
316 if (*Bp == '#' || *Bp == 0)
317 return(0);
318 for (;;) {
319 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
320 continue;
321 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
322 return (1);
323 while (*Bp && *Bp != ':' && *Bp != '|')
324 Bp++;
325 if (*Bp == 0 || *Bp == ':')
326 return (0);
327 Bp++;
328 }
329}
330
331/*
332 * Skip to the next field. Notice that this is very dumb, not
333 * knowing about \: escapes or any such. If necessary, :'s can be put
334 * into the keycap file in octal.
335 */
336static char *
337tskip(bp)
338 register char *bp;
339{
340
341 while (*bp && *bp != ':')
342 bp++;
343 if (*bp == ':')
344 bp++;
345 return (bp);
346}
347
348/*
349 * Return the (numeric) option id.
350 * Numeric options look like
351 * li#80
352 * i.e. the option string is separated from the numeric value by
353 * a # character. If the option is not found we return -1.
354 * Note that we handle octal numbers beginning with 0.
355 */
356tgetnum(id)
357 char *id;
358{
359 register int i, base;
360 register char *bp = tbuf,*xp;
361
362 for (;;) {
363 bp = tskip(bp);
364 if (*bp == 0)
365 return (-1);
366 if ((xp=nmatch(id,bp)) == 0)
367 continue;
368 bp = xp; /* we have an entry */
369 if (*bp == '@')
370 return(-1);
371 if (*bp != '#')
372 continue;
373 bp++;
374 base = 10;
375 if (*bp == '0')
376 base = 8;
377 i = 0;
378 while (isdigit(*bp))
379 i *= base, i += *bp++ - '0';
380 return (i);
381 }
382}
383
384/*
385 * Handle a flag option.
386 * Flag options are given "naked", i.e. followed by a : or the end
387 * of the buffer. Return 1 if we find the option, or 0 if it is
388 * not given.
389 */
390tgetflag(id)
391 char *id;
392{
393 register char *bp = tbuf,*xp;
394
395 for (;;) {
396 bp = tskip(bp);
397 if (!*bp)
398 return (0);
399 if ((xp=nmatch(id,bp)) != 0) {
400 bp = xp;
401 if (!*bp || *bp == ':')
402 return (1);
403 else if (*bp == '@')
404 return(0);
405 }
406 }
407 /*NOTREACHED*/
408}
409
410/*
411 * Get a string valued option.
412 * These are given as
413 * cl=^Z
414 * Much decoding is done on the strings, and the strings are
415 * placed in area, which is a ref parameter which is updated.
416 * No checking on area overflow.
417 */
418char *
419tgetstr(id, area)
420 char *id, **area;
421{
422 register char *bp = tbuf,*xp;
423
424 for (;;) {
425 bp = tskip(bp);
426 if (!*bp)
427 return (0);
428 if ((xp = nmatch(id,bp)) == 0)
429 continue;
430 bp = xp;
431 if (*bp == '@')
432 return(0);
433 if (*bp != '=')
434 continue;
435 bp++;
436 return (tdecode(bp, area));
437 }
438}
439
440/*
441 * Tdecode does the grung work to decode the
442 * string capability escapes.
443 */
444static char *
445tdecode(str, area)
446 register char *str;
447 char **area;
448{
449 register char *cp;
450 register int c;
451 register char *dp;
452 int i;
453
454 cp = *area;
455 while ((c = *str++) && c != ':') {
456 switch (c) {
457
458 case '^':
459 c = *str++ & 037;
460 break;
461
462 case '\\':
463 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
464 c = *str++;
465nextc:
466 if (*dp++ == c) {
467 c = *dp++;
468 break;
469 }
470 dp++;
471 if (*dp)
472 goto nextc;
473 if (isdigit(c)) {
474 c -= '0', i = 2;
475 do
476 c <<= 3, c |= *str++ - '0';
477 while (--i && isdigit(*str));
478 }
479 break;
480 }
481 *cp++ = c;
482 }
483 *cp++ = 0;
484 str = *area;
485 *area = cp;
486 return (str);
487}