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