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
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.
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]
24 #pragma ident "%Z%%M% %I% %E% SMI"
27 * Copyright (c) 1999 by Sun Microsystems, Inc.
28 * All rights reserved.
31 #include <sys/param.h>
44 #define ALL -1 /* special symbol for all tables */
47 * SunOS 4.x and Solaris 2.[1234] put Type 4 key tables into
48 * the keytables directory with no type qualification.
49 * If we're a SPARC, we might be using an NFS server that
50 * doesn't have the new type-qualified directories.
51 * (loadkeys wasn't used on non-SPARCs in 2.[1234].)
54 #define COMPATIBILITY_DIR
57 static char keytable_dir
[] = "/usr/share/lib/keytables/type_%d/";
58 #ifdef COMPATIBILITY_DIR
59 static char keytable_dir2
[] = "/usr/share/lib/keytables/";
61 static char layout_prefix
[] = "layout_";
64 struct keyentry
*ke_next
;
65 struct kiockeymap ke_entry
;
68 typedef
struct keyentry keyentry
;
70 static keyentry
*firstentry
;
71 static keyentry
*lastentry
;
74 struct dupentry
*de_next
;
79 typedef
struct dupentry dupentry
;
81 static dupentry
*firstduplicate
;
82 static dupentry
*lastduplicate
;
84 static dupentry
*firstswap
;
85 static dupentry
*lastswap
;
87 static char *infilename
;
92 static char *strings
[16] = {
93 "\033[H", /* HOMEARROW */
94 "\033[A", /* UPARROW */
95 "\033[B", /* DOWNARROW */
96 "\033[D", /* LEFTARROW */
97 "\033[C", /* RIGHTARROW */
100 static int nstrings
= 5; /* start out with 5 strings */
103 SM_INVALID
, /* this shift mask is invalid for this keyboard */
104 SM_NORMAL
, /* "normal", valid shift mask */
105 SM_NUMLOCK
, /* "Num Lock" shift mask */
106 SM_UP
/* "Up" shift mask */
114 static smentry_t shiftmasks
[] = {
116 { SHIFTMASK
, SM_NORMAL
},
117 { CAPSMASK
, SM_NORMAL
},
118 { CTRLMASK
, SM_NORMAL
},
119 { ALTGRAPHMASK
, SM_NORMAL
},
120 { NUMLOCKMASK
, SM_NUMLOCK
},
125 #define NSHIFTS (sizeof (shiftmasks) / sizeof (shiftmasks[0]))
127 static void enter_mapentry
(int station
, keyentry
*entrylistp
);
128 static keyentry
*makeentry
(int tablemask
, int entry
);
129 static int loadkey
(int kbdfd
, keyentry
*kep
);
130 static int dupkey
(int kbdfd
, dupentry
*dep
, int shiftmask
);
131 static int swapkey
(int kbdfd
, dupentry
*dep
, int shiftmask
);
133 static int readesc
(FILE *stream
, int delim
, int single_char
);
134 static int wordcmp
(const void *w1
, const void *w2
);
135 static int yyerror(char *msg
);
136 static void usage
(void);
137 static void set_layout
(char *arg
);
138 static FILE *open_mapping_file
(char *pathbuf
, char *name
,
139 boolean_t explicit_name
, int type
);
149 /* maxint is 8 hex digits. */
150 char layout_filename
[sizeof
(layout_prefix
)+8];
151 char pathbuf
[MAXPATHLEN
];
153 struct kiockeymap mapentry
;
154 register keyentry
*kep
;
155 register dupentry
*dep
;
156 boolean_t explicit_name
;
158 while
(++argv
, --argc
) {
159 if
(argv
[0][0] != '-') break
;
162 /* -e obsolete, silently ignore */
177 if
(argc
> 1) usage
();
179 if
((kbdfd
= open
("/dev/kbd", O_WRONLY
)) < 0) {
180 /* perror("loadkeys: /dev/kbd"); */
184 if
(ioctl
(kbdfd
, KIOCTYPE
, &type
) < 0) {
186 * There may not be a keyboard connected,
193 /* If no keyboard detected, exit silently. */
197 if
(ioctl
(kbdfd
, KIOCLAYOUT
, &layout
) < 0) {
198 perror
("loadkeys: ioctl(KIOCLAYOUT)");
202 (void) sprintf
(layout_filename
,
203 "%s%.2x", layout_prefix
, layout
);
204 infilename
= layout_filename
;
205 explicit_name
= B_FALSE
;
207 infilename
= argv
[0];
208 explicit_name
= B_TRUE
;
211 infile
= open_mapping_file
(pathbuf
, infilename
, explicit_name
, type
);
212 if
(infile
== NULL
) return
(1);
214 infilename
= pathbuf
;
222 * See which shift masks are valid for this keyboard.
223 * We do that by trying to get the entry for keystation 0 and that
224 * shift mask; if the "ioctl" fails, we assume it's because the shift
227 for
(shift
= 0; shift
< NSHIFTS
; shift
++) {
228 mapentry.kio_tablemask
=
229 shiftmasks
[shift
].sm_mask
;
230 mapentry.kio_station
= 0;
231 if
(ioctl
(kbdfd
, KIOCGKEY
, &mapentry
) < 0)
232 shiftmasks
[shift
].sm_type
= SM_INVALID
;
235 for
(kep
= firstentry
; kep
!= NULL
; kep
= kep
->ke_next
) {
236 if
(kep
->ke_entry.kio_tablemask
== ALL
) {
237 for
(shift
= 0; shift
< NSHIFTS
; shift
++) {
238 switch
(shiftmasks
[shift
].sm_type
) {
245 * Defaults to NONL, not to a copy of
248 if
(kep
->ke_entry.kio_entry
!= HOLE
)
249 kep
->ke_entry.kio_entry
= NONL
;
254 * Defaults to NOP, not to a copy of
257 if
(kep
->ke_entry.kio_entry
!= HOLE
)
258 kep
->ke_entry.kio_entry
= NOP
;
261 kep
->ke_entry.kio_tablemask
=
262 shiftmasks
[shift
].sm_mask
;
263 if
(!loadkey
(kbdfd
, kep
))
267 if
(!loadkey
(kbdfd
, kep
))
272 for
(dep
= firstswap
; dep
!= NULL
; dep
= dep
->de_next
) {
273 for
(shift
= 0; shift
< NSHIFTS
; shift
++) {
274 if
(shiftmasks
[shift
].sm_type
!= SM_INVALID
) {
275 if
(!swapkey
(kbdfd
, dep
,
276 shiftmasks
[shift
].sm_mask
))
282 for
(dep
= firstduplicate
; dep
!= NULL
; dep
= dep
->de_next
) {
283 for
(shift
= 0; shift
< NSHIFTS
; shift
++) {
284 if
(shiftmasks
[shift
].sm_type
!= SM_INVALID
) {
285 if
(!dupkey
(kbdfd
, dep
,
286 shiftmasks
[shift
].sm_mask
))
299 (void) fprintf
(stderr
, "usage: loadkeys [ file ]\n");
304 set_layout
(char *arg
)
310 layout
= (int) strtol
(arg
, &arg
, 0);
312 fprintf
(stderr
, "usage: loadkeys -s layoutnumber\n");
316 if
((kbdfd
= open
("/dev/kbd", O_WRONLY
)) < 0) {
321 ret
= ioctl
(kbdfd
, KIOCSLAYOUT
, layout
);
323 perror
("KIOCSLAYOUT");
330 * Attempt to find the specified mapping file. Return a FILE * if found,
331 * else print a message on stderr and return NULL.
337 boolean_t explicit_name
,
340 /* If the user specified the name, try it "raw". */
342 strcpy
(pathbuf
, name
);
343 infile
= fopen
(pathbuf
, "r");
344 if
(infile
) return
(infile
);
345 if
(errno
!= ENOENT
) goto fopen_fail
;
348 /* Everything after this point applies only to relative names. */
349 if
(*name
== '/') goto fopen_fail
;
351 /* Try the type-qualified directory name. */
352 sprintf
(pathbuf
, keytable_dir
, type
);
353 if
((int)(strlen
(pathbuf
) + strlen
(name
) + 1) >= MAXPATHLEN
) {
354 (void) fprintf
(stderr
, "loadkeys: Name %s is too long\n",
358 (void) strcat
(pathbuf
, name
);
359 infile
= fopen
(pathbuf
, "r");
360 if
(infile
) return
(infile
);
361 if
(errno
!= ENOENT
) goto fopen_fail
;
363 #ifdef COMPATIBILITY_DIR
364 /* If not, and either the name was specified explicitly */
365 /* or this is a type 4... */
366 if
(explicit_name || type
== KB_SUN4
) {
367 /* Try the compatibility name. */
368 /* No need to check len here, it's shorter. */
369 (void) strcpy
(pathbuf
, keytable_dir2
);
370 (void) strcat
(pathbuf
, infilename
);
371 infile
= fopen
(pathbuf
, "r");
372 if
(infile
) return
(infile
);
373 if
(errno
!= ENOENT
) goto fopen_fail
;
378 (void) fprintf
(stderr
, "loadkeys: ");
384 * We have a list of entries for a given keystation, and the keystation number
385 * for that keystation; put that keystation number into all the entries in that
386 * list, and chain that list to the end of the main list of entries.
389 enter_mapentry
(station
, entrylistp
)
391 keyentry
*entrylistp
;
393 register keyentry
*kep
;
395 if
(lastentry
== NULL
)
396 firstentry
= entrylistp
;
398 lastentry
->ke_next
= entrylistp
;
401 kep
->ke_entry.kio_station
= (u_char
)station
;
402 if
(kep
->ke_next
== NULL
) {
411 * Allocate and fill in a new entry.
414 makeentry
(tablemask
, entry
)
418 register keyentry
*kep
;
421 if
((kep
= (keyentry
*) malloc
((unsigned)sizeof
(keyentry
))) == NULL
)
422 yyerror("out of memory for entries");
424 kep
->ke_entry.kio_tablemask
= tablemask
;
425 kep
->ke_entry.kio_station
= 0;
426 kep
->ke_entry.kio_entry
= (u_short
)entry
;
427 index
= entry
- STRING
;
428 if
(index
>= 0 && index
<= 15)
429 (void) strncpy
(kep
->ke_entry.kio_string
, strings
[index
],
435 * Make a set of entries for a keystation that indicate that that keystation's
436 * settings should be copied from another keystation's settings.
439 duplicate_mapentry
(station
, otherstation
)
443 register dupentry
*dep
;
445 if
((dep
= (dupentry
*) malloc
((unsigned)sizeof
(dupentry
))) == NULL
)
446 yyerror("out of memory for entries");
448 if
(lastduplicate
== NULL
)
449 firstduplicate
= dep
;
451 lastduplicate
->de_next
= dep
;
454 dep
->de_station
= station
;
455 dep
->de_otherstation
= otherstation
;
459 * Make a set of entries for a keystation that indicate that that keystation's
460 * settings should be swapped with another keystation's settings.
463 swap_mapentry
(station
, otherstation
)
467 register dupentry
*dep
;
469 if
((dep
= (dupentry
*) malloc
((unsigned)sizeof
(dupentry
))) == NULL
)
470 yyerror("out of memory for entries");
472 if
(lastswap
== NULL
)
475 lastswap
->de_next
= dep
;
478 dep
->de_station
= station
;
479 dep
->de_otherstation
= otherstation
;
485 register keyentry
*kep
;
487 if
(ioctl
(kbdfd
, KIOCSKEY
, &kep
->ke_entry
) < 0) {
488 perror
("loadkeys: ioctl(KIOCSKEY)");
495 dupkey
(kbdfd
, dep
, shiftmask
)
497 register dupentry
*dep
;
500 struct kiockeymap entry
;
502 entry.kio_tablemask
= shiftmask
;
503 entry.kio_station
= dep
->de_otherstation
;
504 if
(ioctl
(kbdfd
, KIOCGKEY
, &entry
) < 0) {
505 perror
("loadkeys: ioctl(KIOCGKEY)");
508 entry.kio_station
= dep
->de_station
;
509 if
(ioctl
(kbdfd
, KIOCSKEY
, &entry
) < 0) {
510 perror
("loadkeys: ioctl(KIOCSKEY)");
519 swapkey
(kbdfd
, dep
, shiftmask
)
521 register dupentry
*dep
;
524 struct kiockeymap entry1
, entry2
;
526 entry1.kio_tablemask
= shiftmask
;
527 entry1.kio_station
= dep
->de_station
;
528 if
(ioctl
(kbdfd
, KIOCGKEY
, &entry1
) < 0) {
529 perror
("loadkeys: ioctl(KIOCGKEY)");
532 entry2.kio_tablemask
= shiftmask
;
533 entry2.kio_station
= dep
->de_otherstation
;
534 if
(ioctl
(kbdfd
, KIOCGKEY
, &entry2
) < 0) {
535 perror
("loadkeys: ioctl(KIOCGKEY)");
538 entry1.kio_station
= dep
->de_otherstation
;
539 if
(ioctl
(kbdfd
, KIOCSKEY
, &entry1
) < 0) {
540 perror
("loadkeys: ioctl(KIOCSKEY)");
543 entry2.kio_station
= dep
->de_station
;
544 if
(ioctl
(kbdfd
, KIOCSKEY
, &entry2
) < 0) {
545 perror
("loadkeys: ioctl(KIOCSKEY)");
552 %term TABLENAME INT CHAR CHARSTRING CONSTANT FKEY KEY SAME AS SWAP WITH
559 %type
<keyentry
> entrylist entry
560 %type
<number
> CHARSTRING CHAR INT CONSTANT FKEY TABLENAME
561 %type
<number
> code expr term number
571 KEY number entrylist
'\n'
573 enter_mapentry
($2, $3);
575 | KEY number SAME AS number
'\n'
577 duplicate_mapentry
($2, $5);
579 | SWAP number WITH number
'\n'
581 swap_mapentry
($2, $4);
590 * Append this entry to the end of the entry list.
592 register keyentry
*kep
;
595 if
(kep
->ke_next
== NULL
) {
612 $$
= makeentry
($1, $2);
659 | FKEY
'(' number
')'
661 if
($3 < 1 ||
$3 > 16)
662 yyerror("invalid function key number");
677 yyerror("syntax error");
685 int w_type
; /* token type */
686 int w_lval
; /* yylval for this token */
690 * Table must be in alphabetical order.
693 { "all", TABLENAME
, ALL
},
694 { "alt", CONSTANT
, ALT
},
695 { "altg", TABLENAME
, ALTGRAPHMASK
},
696 { "altgraph", CONSTANT
, ALTGRAPH
},
698 { "base", TABLENAME
, 0 },
699 { "bf", FKEY
, BOTTOMFUNC
},
700 { "buckybits", CONSTANT
, BUCKYBITS
},
701 { "caps", TABLENAME
, CAPSMASK
},
702 { "capslock", CONSTANT
, CAPSLOCK
},
703 { "compose", CONSTANT
, COMPOSE
},
704 { "ctrl", TABLENAME
, CTRLMASK
},
705 { "downarrow", CONSTANT
, DOWNARROW
},
706 { "error", CONSTANT
, ERROR
},
707 { "fa_acute", CONSTANT
, FA_ACUTE
},
708 { "fa_cedilla", CONSTANT
, FA_CEDILLA
},
709 { "fa_cflex", CONSTANT
, FA_CFLEX
},
710 { "fa_grave", CONSTANT
, FA_GRAVE
},
711 { "fa_tilde", CONSTANT
, FA_TILDE
},
712 { "fa_umlaut", CONSTANT
, FA_UMLAUT
},
713 { "hole", CONSTANT
, HOLE
},
714 { "homearrow", CONSTANT
, HOMEARROW
},
715 { "idle", CONSTANT
, IDLE
},
717 { "leftarrow", CONSTANT
, LEFTARROW
},
718 { "leftctrl", CONSTANT
, LEFTCTRL
},
719 { "leftshift", CONSTANT
, LEFTSHIFT
},
720 { "lf", FKEY
, LEFTFUNC
},
721 { "metabit", CONSTANT
, METABIT
},
722 { "nonl", CONSTANT
, NONL
},
723 { "nop", CONSTANT
, NOP
},
724 { "numl", TABLENAME
, NUMLOCKMASK
},
725 { "numlock", CONSTANT
, NUMLOCK
},
726 { "oops", CONSTANT
, OOPS
},
727 { "pad0", CONSTANT
, PAD0
},
728 { "pad1", CONSTANT
, PAD1
},
729 { "pad2", CONSTANT
, PAD2
},
730 { "pad3", CONSTANT
, PAD3
},
731 { "pad4", CONSTANT
, PAD4
},
732 { "pad5", CONSTANT
, PAD5
},
733 { "pad6", CONSTANT
, PAD6
},
734 { "pad7", CONSTANT
, PAD7
},
735 { "pad8", CONSTANT
, PAD8
},
736 { "pad9", CONSTANT
, PAD9
},
737 { "paddot", CONSTANT
, PADDOT
},
738 { "padenter", CONSTANT
, PADENTER
},
739 { "padequal", CONSTANT
, PADEQUAL
},
740 { "padminus", CONSTANT
, PADMINUS
},
741 { "padplus", CONSTANT
, PADPLUS
},
742 { "padsep", CONSTANT
, PADSEP
},
743 { "padslash", CONSTANT
, PADSLASH
},
744 { "padstar", CONSTANT
, PADSTAR
},
745 { "reset", CONSTANT
, RESET
},
746 { "rf", FKEY
, RIGHTFUNC
},
747 { "rightarrow", CONSTANT
, RIGHTARROW
},
748 { "rightctrl", CONSTANT
, RIGHTCTRL
},
749 { "rightshift", CONSTANT
, RIGHTSHIFT
},
751 { "shift", TABLENAME
, SHIFTMASK
},
752 { "shiftkeys", CONSTANT
, SHIFTKEYS
},
753 { "shiftlock", CONSTANT
, SHIFTLOCK
},
754 { "string", CONSTANT
, STRING
},
756 { "systembit", CONSTANT
, SYSTEMBIT
},
757 { "tf", FKEY
, TOPFUNC
},
758 { "up", TABLENAME
, UPMASK
},
759 { "uparrow", CONSTANT
, UPARROW
},
763 #define NWORDS (sizeof (wordtab) / sizeof (wordtab[0]))
771 register
int tokentype
;
773 while
((c
= getc
(infile
)) == ' ' || c
== '\t')
779 while
((c
= getc
(infile
)) != EOF
&& c
!= '\n')
784 return
(0); /* end marker */
794 if
((c
= getc
(infile
)) == EOF
)
795 yyerror("unterminated character constant");
797 (void) ungetc
(c
, infile
);
798 yylval.number
= '\'';
803 yyerror("null character constant");
807 yylval.number
= readesc
(infile
, '\'', 1);
814 if
((c
= getc
(infile
)) == EOF || c
== '\n')
815 yyerror("unterminated character constant");
817 yyerror("only one character allowed in character constant");
822 if
((c
= getc
(infile
)) == EOF
)
823 yyerror("unterminated string constant");
825 (void) ungetc
(c
, infile
);
829 tokentype
= CHARSTRING
;
832 if
(cp
> &tokbuf
[256])
833 yyerror("line too long");
835 c
= readesc
(infile
, '"', 0);
837 } while
((c
= getc
(infile
)) != EOF
&& c
!= '\n' &&
840 yyerror("unterminated string constant");
843 yyerror("too many strings");
844 if
((int) strlen
(tokbuf
) > KTAB_STRLEN
)
845 yyerror("string too long");
846 strings
[nstrings
] = strdup
(tokbuf
);
847 yylval.number
= STRING
+nstrings
;
859 if
((c
= getc
(infile
)) == EOF
)
860 yyerror("missing newline at end of line");
862 if
(c
== ' ' || c
== '\t' || c
== '\n') {
868 yylval.number
= c
& 037;
869 if
((c
= getc
(infile
)) == EOF
)
870 yyerror("missing newline at end of line");
871 if
(c
!= ' ' && c
!= '\t' && c
!= '\n')
872 yyerror("invalid control character");
874 (void) ungetc
(c
, infile
);
880 if
(cp
> &tokbuf
[256])
881 yyerror("line too long");
883 } while
((c
= getc
(infile
)) != EOF
&& (isalnum
(c
) || c
== '_'));
885 yyerror("newline missing");
886 (void) ungetc
(c
, infile
);
888 if
(strlen
(tokbuf
) == 1) {
890 yylval.number
= (unsigned char)tokbuf
[0];
891 } else if
(strlen
(tokbuf
) == 2 && tokbuf
[0] == '^') {
893 yylval.number
= (unsigned char)(tokbuf
[1] & 037);
896 register word_t
*wptr
;
899 for
(cp
= &tokbuf
[0]; (c
= *cp
) != '\0'; cp
++) {
903 word.w_string
= tokbuf
;
904 wptr
= (word_t
*)bsearch
((char *)&word
,
905 (char *)wordtab
, NWORDS
, sizeof
(word_t
),
908 yylval.number
= wptr
->w_lval
;
909 tokentype
= wptr
->w_type
;
911 yylval.number
= strtol
(tokbuf
, &ptr
, 10);
913 yyerror("syntax error");
925 readesc
(stream
, delim
, single_char
)
934 if
((c
= getc
(stream
)) == EOF || c
== '\n')
935 yyerror("unterminated character constant");
937 if
(c
>= '0' && c
<= '7') {
941 val
= val
*8 + c
- '0';
942 if
((c
= getc
(stream
)) == EOF || c
== '\n')
943 yyerror("unterminated character constant");
949 yyerror("escape sequence too long");
953 if
(c
< '0' || c
> '7') {
955 yyerror("illegal character in escape sequence");
960 (void) ungetc
(c
, stream
);
992 yyerror("illegal character in escape sequence");
999 wordcmp
(const void *w1
, const void *w2
)
1002 ((const word_t
*)w1
)->w_string
,
1003 ((const word_t
*)w2
)->w_string
));
1010 (void) fprintf
(stderr
, "%s, line %d: %s\n", infilename
, lineno
, msg
);