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
main(int argc,char ** argv)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
usage()280 usage()
281 {
282 (void) fprintf(stderr, "usage: loadkeys [ file ]\n");
283 exit(1);
284 }
285
286 static void
set_layout(char * arg)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 *
open_mapping_file(char * pathbuf,char * name,boolean_t explicit_name,int type)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
enter_mapentry(station,entrylistp)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 *
makeentry(tablemask,entry)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
duplicate_mapentry(station,otherstation)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
swap_mapentry(station,otherstation)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
loadkey(kbdfd,kep)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
dupkey(kbdfd,dep,shiftmask)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
swapkey(kbdfd,dep,shiftmask)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
yylex()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
readesc(stream,delim,single_char)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
wordcmp(const void * w1,const void * w2)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
yyerror(const char * msg)990 yyerror(const char *msg)
991 {
992 (void) fprintf(stderr, "%s, line %d: %s\n", infilename, lineno, msg);
993 exit(1);
994 }
995