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
44static char	keytable_dir[] = "/usr/share/lib/keytables/type_%d/";
45static char	layout_prefix[] = "layout_";
46
47struct keyentry {
48	struct keyentry	*ke_next;
49	struct kiockeymap ke_entry;
50};
51
52typedef struct keyentry keyentry;
53
54static keyentry *firstentry;
55static keyentry *lastentry;
56
57struct dupentry {
58	struct dupentry *de_next;
59	int	de_station;
60	int	de_otherstation;
61};
62
63typedef struct dupentry dupentry;
64
65static dupentry *firstduplicate;
66static dupentry *lastduplicate;
67
68static dupentry *firstswap;
69static dupentry *lastswap;
70
71static char	*infilename;
72static FILE	*infile;
73static int	lineno;
74static int	begline;
75
76static 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
84static int	nstrings = 5;	/* start out with 5 strings */
85
86typedef 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
93typedef struct {
94	int	sm_mask;
95	smtype_t sm_type;
96} smentry_t;
97
98static	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
111static void	enter_mapentry(int station, keyentry *entrylistp);
112static keyentry *makeentry(int tablemask, int entry);
113static int	loadkey(int kbdfd, keyentry *kep);
114static int	dupkey(int kbdfd, dupentry *dep, int shiftmask);
115static int	swapkey(int kbdfd, dupentry *dep, int shiftmask);
116static int	yylex();
117extern int	yyparse(void);
118static int	readesc(FILE *stream, int delim, int single_char);
119static int	wordcmp(const void *w1, const void *w2);
120static int	yyerror(char *msg);
121static void	usage(void);
122static void	set_layout(char *arg);
123static FILE	*open_mapping_file(char *pathbuf, char *name,
124			boolean_t explicit_name, int type);
125
126int
127main(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
279static void
280usage()
281{
282	(void) fprintf(stderr, "usage: loadkeys [ file ]\n");
283	exit(1);
284}
285
286static void
287set_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 */
316FILE *
317open_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
341fopen_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 */
352static void
353enter_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 */
377static keyentry *
378makeentry(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		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 */
402static void
403duplicate_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		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 */
426static void
427swap_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		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
446static int
447loadkey(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
458static int
459dupkey(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
482static int
483swapkey(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
529table:
530	table line
531|	/* null */
532;
533
534line:
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
550entrylist:
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
573entry:
574	TABLENAME code
575		{
576		$$ = makeentry($1, $2);
577		}
578;
579
580code:
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
611expr:
612	term
613		{
614		$$ = $1;
615		}
616|	expr '+' term
617		{
618		$$ = $1 + $3;
619		}
620;
621
622term:
623	CONSTANT
624		{
625		$$ = $1;
626		}
627|	FKEY '(' number ')'
628		{
629		if ($3 < 1 || $3 > 16)
630			yyerror("invalid function key number");
631		$$ = $1 + $3 - 1;
632		}
633;
634
635number:
636	INT
637		{
638		$$ = $1;
639		}
640|	CHAR
641		{
642		if (isdigit($1))
643			$$ = $1 - '0';
644		else
645			yyerror("syntax error");
646		}
647;
648
649%%
650
651typedef 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 */
660word_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
742static int
743yylex()
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			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				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				yyerror("unterminated character constant");
793			else if (c != '\'')
794				yyerror("only one character allowed in character constant");
795		}
796		break;
797
798	case '"':
799		if ((c = getc(infile)) == EOF)
800			yyerror("unterminated string constant");
801		if (c == '\n') {
802			(void) ungetc(c, infile);
803			tokentype = CHAR;
804			yylval.number = '"';
805		} else {
806			tokentype = CHARSTRING;
807			cp = &tokbuf[0];
808			do {
809				if (cp > &tokbuf[256])
810					yyerror("line too long");
811				if (c == '\\')
812					c = readesc(infile, '"', 0);
813				*cp++ = (char)c;
814			} while ((c = getc(infile)) != EOF && c != '\n' &&
815				c != '"');
816			if (c != '"')
817				yyerror("unterminated string constant");
818			*cp = '\0';
819			if (nstrings == 16)
820				yyerror("too many strings");
821			if ((int) strlen(tokbuf) > KTAB_STRLEN)
822				yyerror("string too long");
823			strings[nstrings] = strdup(tokbuf);
824			yylval.number = STRING+nstrings;
825			nstrings++;
826		}
827		break;
828
829	case '(':
830	case ')':
831	case '+':
832		tokentype = c;
833		break;
834
835	case '^':
836		if ((c = getc(infile)) == EOF)
837			yyerror("missing newline at end of line");
838		tokentype = CHAR;
839		if (c == ' ' || c == '\t' || c == '\n') {
840			/*
841			 * '^' by itself.
842			 */
843			yylval.number = '^';
844		} else {
845			yylval.number = c & 037;
846			if ((c = getc(infile)) == EOF)
847				yyerror("missing newline at end of line");
848			if (c != ' ' && c != '\t' && c != '\n')
849				yyerror("invalid control character");
850		}
851		(void) ungetc(c, infile);
852		break;
853
854	default:
855		cp = &tokbuf[0];
856		do {
857			if (cp > &tokbuf[256])
858				yyerror("line too long");
859			*cp++ = (char)c;
860		} while ((c = getc(infile)) != EOF && (isalnum(c) || c == '_'));
861		if (c == EOF)
862			yyerror("newline missing");
863		(void) ungetc(c, infile);
864		*cp = '\0';
865		if (strlen(tokbuf) == 1) {
866			tokentype = CHAR;
867			yylval.number = (unsigned char)tokbuf[0];
868		} else if (strlen(tokbuf) == 2 && tokbuf[0] == '^') {
869			tokentype = CHAR;
870			yylval.number = (unsigned char)(tokbuf[1] & 037);
871		} else {
872			word_t word;
873			register word_t *wptr;
874			char *ptr;
875
876			for (cp = &tokbuf[0]; (c = *cp) != '\0'; cp++) {
877				if (isupper(c))
878					*cp = tolower(c);
879			}
880			word.w_string = tokbuf;
881			wptr = (word_t *)bsearch((char *)&word,
882			    (char *)wordtab, NWORDS, sizeof (word_t),
883			    wordcmp);
884			if (wptr != NULL) {
885				yylval.number = wptr->w_lval;
886				tokentype = wptr->w_type;
887			} else {
888				yylval.number = strtol(tokbuf, &ptr, 0);
889				if (ptr == tokbuf)
890					yyerror("syntax error");
891				else
892					tokentype = INT;
893			}
894			break;
895		}
896	}
897
898	return (tokentype);
899}
900
901static int
902readesc(stream, delim, single_char)
903	FILE *stream;
904	int delim;
905	int single_char;
906{
907	register int c;
908	register int val;
909	register int i;
910
911	if ((c = getc(stream)) == EOF || c == '\n')
912		yyerror("unterminated character constant");
913
914	if (c >= '0' && c <= '7') {
915		val = 0;
916		i = 1;
917		for (;;) {
918			val = val*8 + c - '0';
919			if ((c = getc(stream)) == EOF || c == '\n')
920				yyerror("unterminated character constant");
921			if (c == delim)
922				break;
923			i++;
924			if (i > 3) {
925				if (single_char)
926					yyerror("escape sequence too long");
927				else
928					break;
929			}
930			if (c < '0' || c > '7') {
931				if (single_char)
932					yyerror("illegal character in escape sequence");
933				else
934					break;
935			}
936		}
937		(void) ungetc(c, stream);
938	} else {
939		switch (c) {
940
941		case 'n':
942			val = '\n';
943			break;
944
945		case 't':
946			val = '\t';
947			break;
948
949		case 'b':
950			val = '\b';
951			break;
952
953		case 'r':
954			val = '\r';
955			break;
956
957		case 'v':
958			val = '\v';
959			break;
960
961		case '\\':
962			val = '\\';
963			break;
964
965		default:
966			if (c == delim)
967				val = delim;
968			else
969				yyerror("illegal character in escape sequence");
970		}
971	}
972	return (val);
973}
974
975static int
976wordcmp(const void *w1, const void *w2)
977{
978	return (strcmp(
979		((const word_t *)w1)->w_string,
980		((const word_t *)w2)->w_string));
981}
982
983static int
984yyerror(msg)
985	char *msg;
986{
987	(void) fprintf(stderr, "%s, line %d: %s\n", infilename, lineno, msg);
988	exit(1);
989}
990