xref: /illumos-gate/usr/src/cmd/loadkeys/loadkeys.y (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 %{
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License, Version 1.0 only
7  * (the "License").  You may not use this file except in compliance
8  * with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or http://www.opensolaris.org/os/licensing.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 #ifndef lint
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 #endif
27 
28 /*
29  * Copyright (c) 1999 by Sun Microsystems, Inc.
30  * All rights reserved.
31  */
32 
33 #include <sys/param.h>
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <search.h>
37 #include <string.h>
38 #include <malloc.h>
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <sys/kbd.h>
44 #include <sys/kbio.h>
45 
46 #define	ALL	-1	/* special symbol for all tables */
47 
48 /*
49  * SunOS 4.x and Solaris 2.[1234] put Type 4 key tables into
50  * the keytables directory with no type qualification.
51  * If we're a SPARC, we might be using an NFS server that
52  * doesn't have the new type-qualified directories.
53  * (loadkeys wasn't used on non-SPARCs in 2.[1234].)
54  */
55 #ifdef	sparc
56 #define	COMPATIBILITY_DIR
57 #endif
58 
59 static char	keytable_dir[] = "/usr/share/lib/keytables/type_%d/";
60 #ifdef	COMPATIBILITY_DIR
61 static char	keytable_dir2[] = "/usr/share/lib/keytables/";
62 #endif
63 static char	layout_prefix[] = "layout_";
64 
65 struct keyentry {
66 	struct keyentry	*ke_next;
67 	struct kiockeymap ke_entry;
68 };
69 
70 typedef struct keyentry keyentry;
71 
72 static keyentry *firstentry;
73 static keyentry *lastentry;
74 
75 struct dupentry {
76 	struct dupentry *de_next;
77 	int	de_station;
78 	int	de_otherstation;
79 };
80 
81 typedef struct dupentry dupentry;
82 
83 static dupentry *firstduplicate;
84 static dupentry *lastduplicate;
85 
86 static dupentry *firstswap;
87 static dupentry *lastswap;
88 
89 static char	*infilename;
90 static FILE	*infile;
91 static int	lineno;
92 static int	begline;
93 
94 static char	*strings[16] = {
95 	"\033[H",		/* HOMEARROW */
96 	"\033[A",		/* UPARROW */
97 	"\033[B",		/* DOWNARROW */
98 	"\033[D",		/* LEFTARROW */
99 	"\033[C",		/* RIGHTARROW */
100 };
101 
102 static int	nstrings = 5;	/* start out with 5 strings */
103 
104 typedef enum {
105 	SM_INVALID,	/* this shift mask is invalid for this keyboard */
106 	SM_NORMAL,	/* "normal", valid shift mask */
107 	SM_NUMLOCK,	/* "Num Lock" shift mask */
108 	SM_UP		/* "Up" shift mask */
109 } smtype_t;
110 
111 typedef struct {
112 	int	sm_mask;
113 	smtype_t sm_type;
114 } smentry_t;
115 
116 static	smentry_t shiftmasks[] = {
117 	{ 0,		SM_NORMAL },
118 	{ SHIFTMASK,	SM_NORMAL },
119 	{ CAPSMASK,	SM_NORMAL },
120 	{ CTRLMASK,	SM_NORMAL },
121 	{ ALTGRAPHMASK,	SM_NORMAL },
122 	{ NUMLOCKMASK,	SM_NUMLOCK },
123 	{ UPMASK,	SM_UP },
124 };
125 
126 
127 #define	NSHIFTS	(sizeof (shiftmasks) / sizeof (shiftmasks[0]))
128 
129 static void	enter_mapentry(int station, keyentry *entrylistp);
130 static keyentry *makeentry(int tablemask, int entry);
131 static int	loadkey(int kbdfd, keyentry *kep);
132 static int	dupkey(int kbdfd, dupentry *dep, int shiftmask);
133 static int	swapkey(int kbdfd, dupentry *dep, int shiftmask);
134 static int	yylex();
135 static int	readesc(FILE *stream, int delim, int single_char);
136 static int	wordcmp(const void *w1, const void *w2);
137 static int	yyerror(char *msg);
138 static void	usage(void);
139 static void	set_layout(char *arg);
140 static FILE	*open_mapping_file(char *pathbuf, char *name,
141 			boolean_t explicit_name, int type);
142 
143 int
144 main(argc, argv)
145 	int argc;
146 	char **argv;
147 {
148 	register int kbdfd;
149 	int type;
150 	int layout;
151 	/* maxint is 8 hex digits. */
152 	char layout_filename[sizeof(layout_prefix)+8];
153 	char pathbuf[MAXPATHLEN];
154 	register int shift;
155 	struct kiockeymap mapentry;
156 	register keyentry *kep;
157 	register dupentry *dep;
158 	boolean_t explicit_name;
159 
160 	while(++argv, --argc) {
161 		if(argv[0][0] != '-') break;
162 		switch(argv[0][1]) {
163 		case 'e':
164 			/* -e obsolete, silently ignore */
165 			break;
166 		case 's':
167 			if (argc != 2) {
168 				usage();
169 				/* NOTREACHED */
170 			}
171 			set_layout(argv[1]);
172 			exit(0);
173 		default:
174 			usage();
175 			/* NOTREACHED */
176 		}
177 	}
178 
179 	if (argc > 1) usage();
180 
181 	if ((kbdfd = open("/dev/kbd", O_WRONLY)) < 0) {
182 		/* perror("loadkeys: /dev/kbd"); */
183 		return (1);
184 	}
185 
186 	if (ioctl(kbdfd, KIOCTYPE, &type) < 0) {
187 		/*
188 		 * There may not be a keyboard connected,
189 		 * return silently
190 		 */
191 		return (1);
192 	}
193 
194 	if (argc == 0) {
195 		/* If no keyboard detected, exit silently. */
196 		if (type == -1)
197 			return (0);
198 
199 		if (ioctl(kbdfd, KIOCLAYOUT, &layout) < 0) {
200 			perror("loadkeys: ioctl(KIOCLAYOUT)");
201 			return (1);
202 		}
203 
204 		(void) sprintf(layout_filename,
205 				"%s%.2x", layout_prefix, layout);
206 		infilename = layout_filename;
207 		explicit_name = B_FALSE;
208 	} else {
209 		infilename = argv[0];
210 		explicit_name = B_TRUE;
211 	}
212 
213 	infile = open_mapping_file(pathbuf, infilename, explicit_name, type);
214 	if (infile == NULL) return (1);
215 
216 	infilename = pathbuf;
217 
218 	lineno = 0;
219 	begline = 1;
220 	yyparse();
221 	fclose(infile);
222 
223 	/*
224 	 * See which shift masks are valid for this keyboard.
225 	 * We do that by trying to get the entry for keystation 0 and that
226 	 * shift mask; if the "ioctl" fails, we assume it's because the shift
227 	 * mask is invalid.
228 	 */
229 	for (shift = 0; shift < NSHIFTS; shift++) {
230 		mapentry.kio_tablemask =
231 		    shiftmasks[shift].sm_mask;
232 		mapentry.kio_station = 0;
233 		if (ioctl(kbdfd, KIOCGKEY, &mapentry) < 0)
234 			shiftmasks[shift].sm_type = SM_INVALID;
235 	}
236 
237 	for (kep = firstentry; kep != NULL; kep = kep->ke_next) {
238 		if (kep->ke_entry.kio_tablemask == ALL) {
239 			for (shift = 0; shift < NSHIFTS; shift++) {
240 				switch (shiftmasks[shift].sm_type) {
241 
242 				case SM_INVALID:
243 					continue;
244 
245 				case SM_NUMLOCK:
246 					/*
247 					 * Defaults to NONL, not to a copy of
248 					 * the base entry.
249 					 */
250 					if (kep->ke_entry.kio_entry != HOLE)
251 						kep->ke_entry.kio_entry = NONL;
252 					break;
253 
254 				case SM_UP:
255 					/*
256 					 * Defaults to NOP, not to a copy of
257 					 * the base entry.
258 					 */
259 					if (kep->ke_entry.kio_entry != HOLE)
260 						kep->ke_entry.kio_entry = NOP;
261 					break;
262 				}
263 				kep->ke_entry.kio_tablemask =
264 				    shiftmasks[shift].sm_mask;
265 				if (!loadkey(kbdfd, kep))
266 					return (1);
267 			}
268 		} else {
269 			if (!loadkey(kbdfd, kep))
270 				return (1);
271 		}
272 	}
273 
274 	for (dep = firstswap; dep != NULL; dep = dep->de_next) {
275 		for (shift = 0; shift < NSHIFTS; shift++) {
276 			if (shiftmasks[shift].sm_type != SM_INVALID) {
277 				if (!swapkey(kbdfd, dep,
278 				    shiftmasks[shift].sm_mask))
279 					return (0);
280 			}
281 		}
282 	}
283 
284 	for (dep = firstduplicate; dep != NULL; dep = dep->de_next) {
285 		for (shift = 0; shift < NSHIFTS; shift++) {
286 			if (shiftmasks[shift].sm_type != SM_INVALID) {
287 				if (!dupkey(kbdfd, dep,
288 				    shiftmasks[shift].sm_mask))
289 					return (0);
290 			}
291 		}
292 	}
293 
294 	close(kbdfd);
295 	return (0);
296 }
297 
298 static void
299 usage()
300 {
301 	(void) fprintf(stderr, "usage: loadkeys [ file ]\n");
302 	exit(1);
303 }
304 
305 static void
306 set_layout(char *arg)
307 {
308 	int layout;
309 	int ret;
310 	int kbdfd;
311 
312 	layout = (int) strtol(arg, &arg, 0);
313 	if (*arg != '\0') {
314 		fprintf(stderr, "usage:  loadkeys -s layoutnumber\n");
315 		exit(1);
316 	}
317 
318 	if ((kbdfd = open("/dev/kbd", O_WRONLY)) < 0) {
319 		perror("/dev/kbd");
320 		exit(1);
321 	}
322 
323 	ret = ioctl(kbdfd, KIOCSLAYOUT, layout);
324 	if (ret == -1) {
325 		perror("KIOCSLAYOUT");
326 	}
327 
328 	close(kbdfd);
329 }
330 
331 /*
332  * Attempt to find the specified mapping file.  Return a FILE * if found,
333  * else print a message on stderr and return NULL.
334  */
335 FILE *
336 open_mapping_file(
337 	char *pathbuf,
338 	char *name,
339 	boolean_t explicit_name,
340 	int type
341 ) {
342 	/* If the user specified the name, try it "raw". */
343 	if (explicit_name) {
344 		strcpy(pathbuf, name);
345 		infile = fopen(pathbuf, "r");
346 		if (infile) return (infile);
347 		if (errno != ENOENT) goto fopen_fail;
348 	}
349 
350 	/* Everything after this point applies only to relative names. */
351 	if (*name == '/') goto fopen_fail;
352 
353 	/* Try the type-qualified directory name. */
354 	sprintf(pathbuf, keytable_dir, type);
355 	if ((int)(strlen(pathbuf) + strlen(name) + 1) >= MAXPATHLEN) {
356 		(void) fprintf(stderr, "loadkeys: Name %s is too long\n",
357 				name);
358 		return (NULL);
359 	}
360 	(void) strcat(pathbuf, name);
361 	infile = fopen(pathbuf, "r");
362 	if (infile) return (infile);
363 	if (errno != ENOENT) goto fopen_fail;
364 
365 #ifdef	COMPATIBILITY_DIR
366 	/* If not, and either the name was specified explicitly */
367 	/*     or this is a type 4... */
368 	if (explicit_name || type == KB_SUN4) {
369 		/* Try the compatibility name. */
370 		/* No need to check len here, it's shorter. */
371 		(void) strcpy(pathbuf, keytable_dir2);
372 		(void) strcat(pathbuf, infilename);
373 		infile = fopen(pathbuf, "r");
374 		if (infile) return (infile);
375 		if (errno != ENOENT) goto fopen_fail;
376 	}
377 #endif
378 
379 fopen_fail:
380 	(void) fprintf(stderr, "loadkeys: ");
381 	perror(name);
382 	return (NULL);
383 }
384 
385 /*
386  * We have a list of entries for a given keystation, and the keystation number
387  * for that keystation; put that keystation number into all the entries in that
388  * list, and chain that list to the end of the main list of entries.
389  */
390 static void
391 enter_mapentry(station, entrylistp)
392 	int station;
393 	keyentry *entrylistp;
394 {
395 	register keyentry *kep;
396 
397 	if (lastentry == NULL)
398 		firstentry = entrylistp;
399 	else
400 		lastentry->ke_next = entrylistp;
401 	kep = entrylistp;
402 	for (;;) {
403 		kep->ke_entry.kio_station = (u_char)station;
404 		if (kep->ke_next == NULL) {
405 			lastentry = kep;
406 			break;
407 		}
408 		kep = kep->ke_next;
409 	}
410 }
411 
412 /*
413  * Allocate and fill in a new entry.
414  */
415 static keyentry *
416 makeentry(tablemask, entry)
417 	int tablemask;
418 	int entry;
419 {
420 	register keyentry *kep;
421 	register int index;
422 
423 	if ((kep = (keyentry *) malloc((unsigned)sizeof (keyentry))) == NULL)
424 		yyerror("out of memory for entries");
425 	kep->ke_next = NULL;
426 	kep->ke_entry.kio_tablemask = tablemask;
427 	kep->ke_entry.kio_station = 0;
428 	kep->ke_entry.kio_entry = (u_short)entry;
429 	index = entry - STRING;
430 	if (index >= 0 && index <= 15)
431 		(void) strncpy(kep->ke_entry.kio_string, strings[index],
432 		    KTAB_STRLEN);
433 	return (kep);
434 }
435 
436 /*
437  * Make a set of entries for a keystation that indicate that that keystation's
438  * settings should be copied from another keystation's settings.
439  */
440 static void
441 duplicate_mapentry(station, otherstation)
442 	int station;
443 	int otherstation;
444 {
445 	register dupentry *dep;
446 
447 	if ((dep = (dupentry *) malloc((unsigned)sizeof (dupentry))) == NULL)
448 		yyerror("out of memory for entries");
449 
450 	if (lastduplicate == NULL)
451 		firstduplicate = dep;
452 	else
453 		lastduplicate->de_next = dep;
454 	lastduplicate = dep;
455 	dep->de_next = NULL;
456 	dep->de_station = station;
457 	dep->de_otherstation = otherstation;
458 }
459 
460 /*
461  * Make a set of entries for a keystation that indicate that that keystation's
462  * settings should be swapped with another keystation's settings.
463  */
464 static void
465 swap_mapentry(station, otherstation)
466 	int station;
467 	int otherstation;
468 {
469 	register dupentry *dep;
470 
471 	if ((dep = (dupentry *) malloc((unsigned)sizeof (dupentry))) == NULL)
472 		yyerror("out of memory for entries");
473 
474 	if (lastswap == NULL)
475 		firstswap = dep;
476 	else
477 		lastswap->de_next = dep;
478 	lastswap = dep;
479 	dep->de_next = NULL;
480 	dep->de_station = station;
481 	dep->de_otherstation = otherstation;
482 }
483 
484 static int
485 loadkey(kbdfd, kep)
486 	int kbdfd;
487 	register keyentry *kep;
488 {
489 	if (ioctl(kbdfd, KIOCSKEY, &kep->ke_entry) < 0) {
490 		perror("loadkeys: ioctl(KIOCSKEY)");
491 		return (0);
492 	}
493 	return (1);
494 }
495 
496 static int
497 dupkey(kbdfd, dep, shiftmask)
498 	int kbdfd;
499 	register dupentry *dep;
500 	int shiftmask;
501 {
502 	struct kiockeymap entry;
503 
504 	entry.kio_tablemask = shiftmask;
505 	entry.kio_station = dep->de_otherstation;
506 	if (ioctl(kbdfd, KIOCGKEY, &entry) < 0) {
507 		perror("loadkeys: ioctl(KIOCGKEY)");
508 		return (0);
509 	}
510 	entry.kio_station = dep->de_station;
511 	if (ioctl(kbdfd, KIOCSKEY, &entry) < 0) {
512 		perror("loadkeys: ioctl(KIOCSKEY)");
513 		return (0);
514 	}
515 	return (1);
516 }
517 
518 
519 
520 static int
521 swapkey(kbdfd, dep, shiftmask)
522 	int kbdfd;
523 	register dupentry *dep;
524 	int shiftmask;
525 {
526 	struct kiockeymap entry1, entry2;
527 
528 	entry1.kio_tablemask = shiftmask;
529 	entry1.kio_station = dep->de_station;
530 	if (ioctl(kbdfd, KIOCGKEY, &entry1) < 0) {
531 		perror("loadkeys: ioctl(KIOCGKEY)");
532 		return (0);
533 	}
534 	entry2.kio_tablemask = shiftmask;
535 	entry2.kio_station = dep->de_otherstation;
536 	if (ioctl(kbdfd, KIOCGKEY, &entry2) < 0) {
537 		perror("loadkeys: ioctl(KIOCGKEY)");
538 		return (0);
539 	}
540 	entry1.kio_station = dep->de_otherstation;
541 	if (ioctl(kbdfd, KIOCSKEY, &entry1) < 0) {
542 		perror("loadkeys: ioctl(KIOCSKEY)");
543 		return (0);
544 	}
545 	entry2.kio_station = dep->de_station;
546 	if (ioctl(kbdfd, KIOCSKEY, &entry2) < 0) {
547 		perror("loadkeys: ioctl(KIOCSKEY)");
548 		return (0);
549 	}
550 	return (1);
551 }
552 %}
553 
554 %term TABLENAME INT CHAR CHARSTRING CONSTANT FKEY KEY SAME AS SWAP WITH
555 
556 %union {
557 	keyentry *keyentry;
558 	int	number;
559 };
560 
561 %type <keyentry>	entrylist entry
562 %type <number>		CHARSTRING CHAR INT CONSTANT FKEY TABLENAME
563 %type <number>		code expr term number
564 
565 %%
566 
567 table:
568 	table line
569 |	/* null */
570 ;
571 
572 line:
573 	KEY number entrylist '\n'
574 		{
575 		enter_mapentry($2, $3);
576 		}
577 |	KEY number SAME AS number '\n'
578 		{
579 		duplicate_mapentry($2, $5);
580 		}
581 |	SWAP number WITH number '\n'
582 		{
583 		swap_mapentry($2, $4);
584 		}
585 |	'\n'
586 ;
587 
588 entrylist:
589 	entrylist entry
590 		{
591 		/*
592 		 * Append this entry to the end of the entry list.
593 		 */
594 		register keyentry *kep;
595 		kep = $1;
596 		for (;;) {
597 			if (kep->ke_next == NULL) {
598 				kep->ke_next = $2;
599 				break;
600 			}
601 			kep = kep->ke_next;
602 		}
603 		$$ = $1;
604 		}
605 |	entry
606 		{
607 		$$ = $1;
608 		}
609 ;
610 
611 entry:
612 	TABLENAME code
613 		{
614 		$$ = makeentry($1, $2);
615 		}
616 ;
617 
618 code:
619 	CHARSTRING
620 		{
621 		$$ = $1;
622 		}
623 |	CHAR
624 		{
625 		$$ = $1;
626 		}
627 |	'('
628 		{
629 		$$ = '(';
630 		}
631 |	')'
632 		{
633 		$$ = ')';
634 		}
635 |	'+'
636 		{
637 		$$ = '+';
638 		}
639 |	expr
640 		{
641 		$$ = $1;
642 		}
643 ;
644 
645 expr:
646 	term
647 		{
648 		$$ = $1;
649 		}
650 |	expr '+' term
651 		{
652 		$$ = $1 + $3;
653 		}
654 ;
655 
656 term:
657 	CONSTANT
658 		{
659 		$$ = $1;
660 		}
661 |	FKEY '(' number ')'
662 		{
663 		if ($3 < 1 || $3 > 16)
664 			yyerror("invalid function key number");
665 		$$ = $1 + $3 - 1;
666 		}
667 ;
668 
669 number:
670 	INT
671 		{
672 		$$ = $1;
673 		}
674 |	CHAR
675 		{
676 		if (isdigit($1))
677 			$$ = $1 - '0';
678 		else
679 			yyerror("syntax error");
680 		}
681 ;
682 
683 %%
684 
685 typedef struct {
686 	char	*w_string;
687 	int	w_type;		/* token type */
688 	int	w_lval;		/* yylval for this token */
689 } word_t;
690 
691 /*
692  * Table must be in alphabetical order.
693  */
694 word_t	wordtab[] = {
695 	{ "all",	TABLENAME,	ALL },
696 	{ "alt",	CONSTANT,	ALT },
697 	{ "altg",	TABLENAME,	ALTGRAPHMASK },
698 	{ "altgraph",	CONSTANT,	ALTGRAPH },
699 	{ "as",		AS,		0 },
700 	{ "base",	TABLENAME,	0 },
701 	{ "bf",		FKEY,		BOTTOMFUNC },
702 	{ "buckybits",	CONSTANT,	BUCKYBITS },
703 	{ "caps",	TABLENAME,	CAPSMASK },
704 	{ "capslock",	CONSTANT,	CAPSLOCK },
705 	{ "compose",	CONSTANT,	COMPOSE },
706 	{ "ctrl",	TABLENAME,	CTRLMASK },
707 	{ "downarrow",	CONSTANT,	DOWNARROW },
708 	{ "error",	CONSTANT,	ERROR },
709 	{ "fa_acute",	CONSTANT,	FA_ACUTE },
710 	{ "fa_cedilla",	CONSTANT,	FA_CEDILLA },
711 	{ "fa_cflex",	CONSTANT,	FA_CFLEX },
712 	{ "fa_grave",	CONSTANT,	FA_GRAVE },
713 	{ "fa_tilde",	CONSTANT,	FA_TILDE },
714 	{ "fa_umlaut",	CONSTANT,	FA_UMLAUT },
715 	{ "hole",	CONSTANT,	HOLE },
716 	{ "homearrow",	CONSTANT,	HOMEARROW },
717 	{ "idle",	CONSTANT,	IDLE },
718 	{ "key",	KEY,		0 },
719 	{ "leftarrow",	CONSTANT,	LEFTARROW },
720 	{ "leftctrl",	CONSTANT,	LEFTCTRL },
721 	{ "leftshift",	CONSTANT,	LEFTSHIFT },
722 	{ "lf",		FKEY,		LEFTFUNC },
723 	{ "metabit",	CONSTANT,	METABIT },
724 	{ "nonl",	CONSTANT,	NONL },
725 	{ "nop",	CONSTANT,	NOP },
726 	{ "numl",	TABLENAME,	NUMLOCKMASK },
727 	{ "numlock",	CONSTANT,	NUMLOCK },
728 	{ "oops",	CONSTANT,	OOPS },
729 	{ "pad0",	CONSTANT,	PAD0 },
730 	{ "pad1",	CONSTANT,	PAD1 },
731 	{ "pad2",	CONSTANT,	PAD2 },
732 	{ "pad3",	CONSTANT,	PAD3 },
733 	{ "pad4",	CONSTANT,	PAD4 },
734 	{ "pad5",	CONSTANT,	PAD5 },
735 	{ "pad6",	CONSTANT,	PAD6 },
736 	{ "pad7",	CONSTANT,	PAD7 },
737 	{ "pad8",	CONSTANT,	PAD8 },
738 	{ "pad9",	CONSTANT,	PAD9 },
739 	{ "paddot",	CONSTANT,	PADDOT },
740 	{ "padenter",	CONSTANT,	PADENTER },
741 	{ "padequal",	CONSTANT,	PADEQUAL },
742 	{ "padminus",	CONSTANT,	PADMINUS },
743 	{ "padplus",	CONSTANT,	PADPLUS },
744 	{ "padsep",	CONSTANT,	PADSEP },
745 	{ "padslash",	CONSTANT,	PADSLASH },
746 	{ "padstar",	CONSTANT,	PADSTAR },
747 	{ "reset",	CONSTANT,	RESET },
748 	{ "rf",		FKEY,		RIGHTFUNC },
749 	{ "rightarrow",	CONSTANT,	RIGHTARROW },
750 	{ "rightctrl",	CONSTANT,	RIGHTCTRL },
751 	{ "rightshift",	CONSTANT,	RIGHTSHIFT },
752 	{ "same",	SAME,		0 },
753 	{ "shift",	TABLENAME,	SHIFTMASK },
754 	{ "shiftkeys",	CONSTANT,	SHIFTKEYS },
755 	{ "shiftlock",	CONSTANT,	SHIFTLOCK },
756 	{ "string",	CONSTANT,	STRING },
757 	{ "swap",	SWAP,		0 },
758 	{ "systembit",	CONSTANT,	SYSTEMBIT },
759 	{ "tf",		FKEY,		TOPFUNC },
760 	{ "up",		TABLENAME,	UPMASK },
761 	{ "uparrow",	CONSTANT,	UPARROW },
762 	{ "with",	WITH,		0 },
763 };
764 
765 #define	NWORDS		(sizeof (wordtab) / sizeof (wordtab[0]))
766 
767 static int
768 yylex()
769 {
770 	register int c;
771 	char tokbuf[256+1];
772 	register char *cp;
773 	register int tokentype;
774 
775 	while ((c = getc(infile)) == ' ' || c == '\t')
776 		;
777 	if (begline) {
778 		lineno++;
779 		begline = 0;
780 		if (c == '#') {
781 			while ((c = getc(infile)) != EOF && c != '\n')
782 				;
783 		}
784 	}
785 	if (c == EOF)
786 		return (0);	/* end marker */
787 	if (c == '\n') {
788 		begline = 1;
789 		return (c);
790 	}
791 
792 	switch (c) {
793 
794 	case '\'':
795 		tokentype = CHAR;
796 		if ((c = getc(infile)) == EOF)
797 			yyerror("unterminated character constant");
798 		if (c == '\n') {
799 			(void) ungetc(c, infile);
800 			yylval.number = '\'';
801 		} else {
802 			switch (c) {
803 
804 			case '\'':
805 				yyerror("null character constant");
806 				break;
807 
808 			case '\\':
809 				yylval.number = readesc(infile, '\'', 1);
810 				break;
811 
812 			default:
813 				yylval.number = c;
814 				break;
815 			}
816 			if ((c = getc(infile)) == EOF || c == '\n')
817 				yyerror("unterminated character constant");
818 			else if (c != '\'')
819 				yyerror("only one character allowed in character constant");
820 		}
821 		break;
822 
823 	case '"':
824 		if ((c = getc(infile)) == EOF)
825 			yyerror("unterminated string constant");
826 		if (c == '\n') {
827 			(void) ungetc(c, infile);
828 			tokentype = CHAR;
829 			yylval.number = '"';
830 		} else {
831 			tokentype = CHARSTRING;
832 			cp = &tokbuf[0];
833 			do {
834 				if (cp > &tokbuf[256])
835 					yyerror("line too long");
836 				if (c == '\\')
837 					c = readesc(infile, '"', 0);
838 				*cp++ = (char)c;
839 			} while ((c = getc(infile)) != EOF && c != '\n' &&
840 				c != '"');
841 			if (c != '"')
842 				yyerror("unterminated string constant");
843 			*cp = '\0';
844 			if (nstrings == 16)
845 				yyerror("too many strings");
846 			if ((int) strlen(tokbuf) > KTAB_STRLEN)
847 				yyerror("string too long");
848 			strings[nstrings] = strdup(tokbuf);
849 			yylval.number = STRING+nstrings;
850 			nstrings++;
851 		}
852 		break;
853 
854 	case '(':
855 	case ')':
856 	case '+':
857 		tokentype = c;
858 		break;
859 
860 	case '^':
861 		if ((c = getc(infile)) == EOF)
862 			yyerror("missing newline at end of line");
863 		tokentype = CHAR;
864 		if (c == ' ' || c == '\t' || c == '\n') {
865 			/*
866 			 * '^' by itself.
867 			 */
868 			yylval.number = '^';
869 		} else {
870 			yylval.number = c & 037;
871 			if ((c = getc(infile)) == EOF)
872 				yyerror("missing newline at end of line");
873 			if (c != ' ' && c != '\t' && c != '\n')
874 				yyerror("invalid control character");
875 		}
876 		(void) ungetc(c, infile);
877 		break;
878 
879 	default:
880 		cp = &tokbuf[0];
881 		do {
882 			if (cp > &tokbuf[256])
883 				yyerror("line too long");
884 			*cp++ = (char)c;
885 		} while ((c = getc(infile)) != EOF && (isalnum(c) || c == '_'));
886 		if (c == EOF)
887 			yyerror("newline missing");
888 		(void) ungetc(c, infile);
889 		*cp = '\0';
890 		if (strlen(tokbuf) == 1) {
891 			tokentype = CHAR;
892 			yylval.number = (unsigned char)tokbuf[0];
893 		} else if (strlen(tokbuf) == 2 && tokbuf[0] == '^') {
894 			tokentype = CHAR;
895 			yylval.number = (unsigned char)(tokbuf[1] & 037);
896 		} else {
897 			word_t word;
898 			register word_t *wptr;
899 			char *ptr;
900 
901 			for (cp = &tokbuf[0]; (c = *cp) != '\0'; cp++) {
902 				if (isupper(c))
903 					*cp = tolower(c);
904 			}
905 			word.w_string = tokbuf;
906 			wptr = (word_t *)bsearch((char *)&word,
907 			    (char *)wordtab, NWORDS, sizeof (word_t),
908 			    wordcmp);
909 			if (wptr != NULL) {
910 				yylval.number = wptr->w_lval;
911 				tokentype = wptr->w_type;
912 			} else {
913 				yylval.number = strtol(tokbuf, &ptr, 10);
914 				if (ptr == tokbuf)
915 					yyerror("syntax error");
916 				else
917 					tokentype = INT;
918 			}
919 			break;
920 		}
921 	}
922 
923 	return (tokentype);
924 }
925 
926 static int
927 readesc(stream, delim, single_char)
928 	FILE *stream;
929 	int delim;
930 	int single_char;
931 {
932 	register int c;
933 	register int val;
934 	register int i;
935 
936 	if ((c = getc(stream)) == EOF || c == '\n')
937 		yyerror("unterminated character constant");
938 
939 	if (c >= '0' && c <= '7') {
940 		val = 0;
941 		i = 1;
942 		for (;;) {
943 			val = val*8 + c - '0';
944 			if ((c = getc(stream)) == EOF || c == '\n')
945 				yyerror("unterminated character constant");
946 			if (c == delim)
947 				break;
948 			i++;
949 			if (i > 3) {
950 				if (single_char)
951 					yyerror("escape sequence too long");
952 				else
953 					break;
954 			}
955 			if (c < '0' || c > '7') {
956 				if (single_char)
957 					yyerror("illegal character in escape sequence");
958 				else
959 					break;
960 			}
961 		}
962 		(void) ungetc(c, stream);
963 	} else {
964 		switch (c) {
965 
966 		case 'n':
967 			val = '\n';
968 			break;
969 
970 		case 't':
971 			val = '\t';
972 			break;
973 
974 		case 'b':
975 			val = '\b';
976 			break;
977 
978 		case 'r':
979 			val = '\r';
980 			break;
981 
982 		case 'v':
983 			val = '\v';
984 			break;
985 
986 		case '\\':
987 			val = '\\';
988 			break;
989 
990 		default:
991 			if (c == delim)
992 				val = delim;
993 			else
994 				yyerror("illegal character in escape sequence");
995 		}
996 	}
997 	return (val);
998 }
999 
1000 static int
1001 wordcmp(const void *w1, const void *w2)
1002 {
1003 	return (strcmp(
1004 		((const word_t *)w1)->w_string,
1005 		((const word_t *)w2)->w_string));
1006 }
1007 
1008 static int
1009 yyerror(msg)
1010 	char *msg;
1011 {
1012 	(void) fprintf(stderr, "%s, line %d: %s\n", infilename, lineno, msg);
1013 	exit(1);
1014 }
1015