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]
25 #pragma ident "%Z%%M% %I% %E% SMI"
29 * Copyright (c) 1999 by Sun Microsystems, Inc.
30 * All rights reserved.
33 #include <sys/param.h>
46 #define ALL -1 /* special symbol for all tables */
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].)
56 #define COMPATIBILITY_DIR
59 static char keytable_dir
[] = "/usr/share/lib/keytables/type_%d/";
60 #ifdef COMPATIBILITY_DIR
61 static char keytable_dir2
[] = "/usr/share/lib/keytables/";
63 static char layout_prefix
[] = "layout_";
66 struct keyentry
*ke_next
;
67 struct kiockeymap ke_entry
;
70 typedef
struct keyentry keyentry
;
72 static keyentry
*firstentry
;
73 static keyentry
*lastentry
;
76 struct dupentry
*de_next
;
81 typedef
struct dupentry dupentry
;
83 static dupentry
*firstduplicate
;
84 static dupentry
*lastduplicate
;
86 static dupentry
*firstswap
;
87 static dupentry
*lastswap
;
89 static char *infilename
;
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 */
102 static int nstrings
= 5; /* start out with 5 strings */
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 */
116 static smentry_t shiftmasks
[] = {
118 { SHIFTMASK
, SM_NORMAL
},
119 { CAPSMASK
, SM_NORMAL
},
120 { CTRLMASK
, SM_NORMAL
},
121 { ALTGRAPHMASK
, SM_NORMAL
},
122 { NUMLOCKMASK
, SM_NUMLOCK
},
127 #define NSHIFTS (sizeof (shiftmasks) / sizeof (shiftmasks[0]))
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
);
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
);
151 /* maxint is 8 hex digits. */
152 char layout_filename
[sizeof
(layout_prefix
)+8];
153 char pathbuf
[MAXPATHLEN
];
155 struct kiockeymap mapentry
;
156 register keyentry
*kep
;
157 register dupentry
*dep
;
158 boolean_t explicit_name
;
160 while
(++argv
, --argc
) {
161 if
(argv
[0][0] != '-') break
;
164 /* -e obsolete, silently ignore */
179 if
(argc
> 1) usage
();
181 if
((kbdfd
= open
("/dev/kbd", O_WRONLY
)) < 0) {
182 /* perror("loadkeys: /dev/kbd"); */
186 if
(ioctl
(kbdfd
, KIOCTYPE
, &type
) < 0) {
188 * There may not be a keyboard connected,
195 /* If no keyboard detected, exit silently. */
199 if
(ioctl
(kbdfd
, KIOCLAYOUT
, &layout
) < 0) {
200 perror
("loadkeys: ioctl(KIOCLAYOUT)");
204 (void) sprintf
(layout_filename
,
205 "%s%.2x", layout_prefix
, layout
);
206 infilename
= layout_filename
;
207 explicit_name
= B_FALSE
;
209 infilename
= argv
[0];
210 explicit_name
= B_TRUE
;
213 infile
= open_mapping_file
(pathbuf
, infilename
, explicit_name
, type
);
214 if
(infile
== NULL
) return
(1);
216 infilename
= pathbuf
;
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
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
;
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
) {
247 * Defaults to NONL, not to a copy of
250 if
(kep
->ke_entry.kio_entry
!= HOLE
)
251 kep
->ke_entry.kio_entry
= NONL
;
256 * Defaults to NOP, not to a copy of
259 if
(kep
->ke_entry.kio_entry
!= HOLE
)
260 kep
->ke_entry.kio_entry
= NOP
;
263 kep
->ke_entry.kio_tablemask
=
264 shiftmasks
[shift
].sm_mask
;
265 if
(!loadkey
(kbdfd
, kep
))
269 if
(!loadkey
(kbdfd
, kep
))
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
))
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
))
301 (void) fprintf
(stderr
, "usage: loadkeys [ file ]\n");
306 set_layout
(char *arg
)
312 layout
= (int) strtol
(arg
, &arg
, 0);
314 fprintf
(stderr
, "usage: loadkeys -s layoutnumber\n");
318 if
((kbdfd
= open
("/dev/kbd", O_WRONLY
)) < 0) {
323 ret
= ioctl
(kbdfd
, KIOCSLAYOUT
, layout
);
325 perror
("KIOCSLAYOUT");
332 * Attempt to find the specified mapping file. Return a FILE * if found,
333 * else print a message on stderr and return NULL.
339 boolean_t explicit_name
,
342 /* If the user specified the name, try it "raw". */
344 strcpy
(pathbuf
, name
);
345 infile
= fopen
(pathbuf
, "r");
346 if
(infile
) return
(infile
);
347 if
(errno
!= ENOENT
) goto fopen_fail
;
350 /* Everything after this point applies only to relative names. */
351 if
(*name
== '/') goto fopen_fail
;
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",
360 (void) strcat
(pathbuf
, name
);
361 infile
= fopen
(pathbuf
, "r");
362 if
(infile
) return
(infile
);
363 if
(errno
!= ENOENT
) goto fopen_fail
;
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
;
380 (void) fprintf
(stderr
, "loadkeys: ");
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.
391 enter_mapentry
(station
, entrylistp
)
393 keyentry
*entrylistp
;
395 register keyentry
*kep
;
397 if
(lastentry
== NULL
)
398 firstentry
= entrylistp
;
400 lastentry
->ke_next
= entrylistp
;
403 kep
->ke_entry.kio_station
= (u_char
)station
;
404 if
(kep
->ke_next
== NULL
) {
413 * Allocate and fill in a new entry.
416 makeentry
(tablemask
, entry
)
420 register keyentry
*kep
;
423 if
((kep
= (keyentry
*) malloc
((unsigned)sizeof
(keyentry
))) == NULL
)
424 yyerror("out of memory for entries");
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
],
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.
441 duplicate_mapentry
(station
, otherstation
)
445 register dupentry
*dep
;
447 if
((dep
= (dupentry
*) malloc
((unsigned)sizeof
(dupentry
))) == NULL
)
448 yyerror("out of memory for entries");
450 if
(lastduplicate
== NULL
)
451 firstduplicate
= dep
;
453 lastduplicate
->de_next
= dep
;
456 dep
->de_station
= station
;
457 dep
->de_otherstation
= otherstation
;
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.
465 swap_mapentry
(station
, otherstation
)
469 register dupentry
*dep
;
471 if
((dep
= (dupentry
*) malloc
((unsigned)sizeof
(dupentry
))) == NULL
)
472 yyerror("out of memory for entries");
474 if
(lastswap
== NULL
)
477 lastswap
->de_next
= dep
;
480 dep
->de_station
= station
;
481 dep
->de_otherstation
= otherstation
;
487 register keyentry
*kep
;
489 if
(ioctl
(kbdfd
, KIOCSKEY
, &kep
->ke_entry
) < 0) {
490 perror
("loadkeys: ioctl(KIOCSKEY)");
497 dupkey
(kbdfd
, dep
, shiftmask
)
499 register dupentry
*dep
;
502 struct kiockeymap entry
;
504 entry.kio_tablemask
= shiftmask
;
505 entry.kio_station
= dep
->de_otherstation
;
506 if
(ioctl
(kbdfd
, KIOCGKEY
, &entry
) < 0) {
507 perror
("loadkeys: ioctl(KIOCGKEY)");
510 entry.kio_station
= dep
->de_station
;
511 if
(ioctl
(kbdfd
, KIOCSKEY
, &entry
) < 0) {
512 perror
("loadkeys: ioctl(KIOCSKEY)");
521 swapkey
(kbdfd
, dep
, shiftmask
)
523 register dupentry
*dep
;
526 struct kiockeymap entry1
, entry2
;
528 entry1.kio_tablemask
= shiftmask
;
529 entry1.kio_station
= dep
->de_station
;
530 if
(ioctl
(kbdfd
, KIOCGKEY
, &entry1
) < 0) {
531 perror
("loadkeys: ioctl(KIOCGKEY)");
534 entry2.kio_tablemask
= shiftmask
;
535 entry2.kio_station
= dep
->de_otherstation
;
536 if
(ioctl
(kbdfd
, KIOCGKEY
, &entry2
) < 0) {
537 perror
("loadkeys: ioctl(KIOCGKEY)");
540 entry1.kio_station
= dep
->de_otherstation
;
541 if
(ioctl
(kbdfd
, KIOCSKEY
, &entry1
) < 0) {
542 perror
("loadkeys: ioctl(KIOCSKEY)");
545 entry2.kio_station
= dep
->de_station
;
546 if
(ioctl
(kbdfd
, KIOCSKEY
, &entry2
) < 0) {
547 perror
("loadkeys: ioctl(KIOCSKEY)");
554 %term TABLENAME INT CHAR CHARSTRING CONSTANT FKEY KEY SAME AS SWAP WITH
561 %type
<keyentry
> entrylist entry
562 %type
<number
> CHARSTRING CHAR INT CONSTANT FKEY TABLENAME
563 %type
<number
> code expr term number
573 KEY number entrylist
'\n'
575 enter_mapentry
($2, $3);
577 | KEY number SAME AS number
'\n'
579 duplicate_mapentry
($2, $5);
581 | SWAP number WITH number
'\n'
583 swap_mapentry
($2, $4);
592 * Append this entry to the end of the entry list.
594 register keyentry
*kep
;
597 if
(kep
->ke_next
== NULL
) {
614 $$
= makeentry
($1, $2);
661 | FKEY
'(' number
')'
663 if
($3 < 1 ||
$3 > 16)
664 yyerror("invalid function key number");
679 yyerror("syntax error");
687 int w_type
; /* token type */
688 int w_lval
; /* yylval for this token */
692 * Table must be in alphabetical order.
695 { "all", TABLENAME
, ALL
},
696 { "alt", CONSTANT
, ALT
},
697 { "altg", TABLENAME
, ALTGRAPHMASK
},
698 { "altgraph", CONSTANT
, ALTGRAPH
},
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
},
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
},
753 { "shift", TABLENAME
, SHIFTMASK
},
754 { "shiftkeys", CONSTANT
, SHIFTKEYS
},
755 { "shiftlock", CONSTANT
, SHIFTLOCK
},
756 { "string", CONSTANT
, STRING
},
758 { "systembit", CONSTANT
, SYSTEMBIT
},
759 { "tf", FKEY
, TOPFUNC
},
760 { "up", TABLENAME
, UPMASK
},
761 { "uparrow", CONSTANT
, UPARROW
},
765 #define NWORDS (sizeof (wordtab) / sizeof (wordtab[0]))
773 register
int tokentype
;
775 while
((c
= getc
(infile
)) == ' ' || c
== '\t')
781 while
((c
= getc
(infile
)) != EOF
&& c
!= '\n')
786 return
(0); /* end marker */
796 if
((c
= getc
(infile
)) == EOF
)
797 yyerror("unterminated character constant");
799 (void) ungetc
(c
, infile
);
800 yylval.number
= '\'';
805 yyerror("null character constant");
809 yylval.number
= readesc
(infile
, '\'', 1);
816 if
((c
= getc
(infile
)) == EOF || c
== '\n')
817 yyerror("unterminated character constant");
819 yyerror("only one character allowed in character constant");
824 if
((c
= getc
(infile
)) == EOF
)
825 yyerror("unterminated string constant");
827 (void) ungetc
(c
, infile
);
831 tokentype
= CHARSTRING
;
834 if
(cp
> &tokbuf
[256])
835 yyerror("line too long");
837 c
= readesc
(infile
, '"', 0);
839 } while
((c
= getc
(infile
)) != EOF
&& c
!= '\n' &&
842 yyerror("unterminated string constant");
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
;
861 if
((c
= getc
(infile
)) == EOF
)
862 yyerror("missing newline at end of line");
864 if
(c
== ' ' || c
== '\t' || c
== '\n') {
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");
876 (void) ungetc
(c
, infile
);
882 if
(cp
> &tokbuf
[256])
883 yyerror("line too long");
885 } while
((c
= getc
(infile
)) != EOF
&& (isalnum
(c
) || c
== '_'));
887 yyerror("newline missing");
888 (void) ungetc
(c
, infile
);
890 if
(strlen
(tokbuf
) == 1) {
892 yylval.number
= (unsigned char)tokbuf
[0];
893 } else if
(strlen
(tokbuf
) == 2 && tokbuf
[0] == '^') {
895 yylval.number
= (unsigned char)(tokbuf
[1] & 037);
898 register word_t
*wptr
;
901 for
(cp
= &tokbuf
[0]; (c
= *cp
) != '\0'; cp
++) {
905 word.w_string
= tokbuf
;
906 wptr
= (word_t
*)bsearch
((char *)&word
,
907 (char *)wordtab
, NWORDS
, sizeof
(word_t
),
910 yylval.number
= wptr
->w_lval
;
911 tokentype
= wptr
->w_type
;
913 yylval.number
= strtol
(tokbuf
, &ptr
, 10);
915 yyerror("syntax error");
927 readesc
(stream
, delim
, single_char
)
936 if
((c
= getc
(stream
)) == EOF || c
== '\n')
937 yyerror("unterminated character constant");
939 if
(c
>= '0' && c
<= '7') {
943 val
= val
*8 + c
- '0';
944 if
((c
= getc
(stream
)) == EOF || c
== '\n')
945 yyerror("unterminated character constant");
951 yyerror("escape sequence too long");
955 if
(c
< '0' || c
> '7') {
957 yyerror("illegal character in escape sequence");
962 (void) ungetc
(c
, stream
);
994 yyerror("illegal character in escape sequence");
1001 wordcmp
(const void *w1
, const void *w2
)
1004 ((const word_t
*)w1
)->w_string
,
1005 ((const word_t
*)w2
)->w_string
));
1012 (void) fprintf
(stderr
, "%s, line %d: %s\n", infilename
, lineno
, msg
);