2 * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/usr.sbin/kbdmap/kbdmap.c,v 1.2 2002/10/27 17:44:33 wollman Exp $
27 * $DragonFly: src/usr.sbin/kbdmap/kbdmap.c,v 1.1 2004/06/21 01:37:42 cpressey Exp $
30 #include <sys/cdefs.h>
31 #include <sys/types.h>
32 #include <sys/queue.h>
41 #include <stringlist.h>
47 static const char *lang_default
= DEFAULT_LANG
;
48 static const char *font
;
49 static const char *lang
;
50 static const char *program
;
51 static const char *keymapdir
= DEFAULT_KEYMAP_DIR
;
52 static const char *fontdir
= DEFAULT_FONT_DIR
;
53 static const char *sysconfig
= DEFAULT_SYSCONFIG
;
54 static const char *font_default
= DEFAULT_FONT
;
55 static const char *font_current
;
56 static const char *dir
;
57 static const char *menu
= "";
69 SLIST_ENTRY(keymap
) entries
;
71 static SLIST_HEAD(slisthead
, keymap
) head
= SLIST_HEAD_INITIALIZER(head
);
75 * Get keymap entry for 'key', or NULL of not found
77 static struct keymap
*
78 get_keymap(const char *key
)
82 SLIST_FOREACH(km
, &head
, entries
)
83 if (!strcmp(km
->keym
, key
))
90 * Count the number of keymaps we found
98 SLIST_FOREACH(km
, &head
, entries
)
105 * Remove any keymap with given keym
108 remove_keymap(const char *keym
)
112 SLIST_FOREACH(km
, &head
, entries
) {
113 if (!strcmp(keym
, km
->keym
)) {
114 SLIST_REMOVE(&head
, km
, keymap
, entries
);
122 * Add to hash with 'key'
125 add_keymap(const char *desc
, int mark
, const char *keym
)
127 struct keymap
*km
, *km_new
;
129 /* Is there already an entry with this key? */
130 SLIST_FOREACH(km
, &head
, entries
) {
131 if (!strcmp(km
->keym
, keym
)) {
132 /* Reuse this entry */
134 km
->desc
= strdup(desc
);
140 km_new
= (struct keymap
*) malloc (sizeof(struct keymap
));
141 km_new
->desc
= strdup(desc
);
142 km_new
->keym
= strdup(keym
);
145 /* Add to keymap list */
146 SLIST_INSERT_HEAD(&head
, km_new
, entries
);
150 * Figure out the default language to use.
157 if ((locale
= getenv("LC_ALL")) == NULL
&&
158 (locale
= getenv("LC_CTYPE")) == NULL
&&
159 (locale
= getenv("LANG")) == NULL
)
160 locale
= lang_default
;
162 /* Check for alias */
163 if (!strcmp(locale
, "C"))
164 locale
= DEFAULT_LANG
;
170 * Extract filename part
173 extract_name(const char *name
)
177 p
= strrchr(name
, '/');
178 if (p
!= NULL
&& p
[1] != '\0')
185 * Return file extension or NULL
188 get_extension(const char *name
)
192 p
= strrchr(name
, '.');
194 if (p
!= NULL
&& p
[1] != '\0')
201 * Read font from /etc/rc.conf else return default.
202 * Freeing the memory is the caller's responsibility.
207 char line
[256], buf
[20];
210 FILE *fp
= fopen(sysconfig
, "r");
212 while (fgets(line
, sizeof(line
), fp
)) {
218 matches
= sscanf(line
,
219 " font%dx%d = \"%20[-.0-9a-zA-Z_]",
222 if (strcmp(buf
, "NO")) {
225 fnt
= (char *) malloc(strlen(buf
) + 1);
231 fprintf(stderr
, "Could not open %s for reading\n", sysconfig
);
237 * Set a font using 'vidcontrol'
240 vidcontrol(const char *fnt
)
246 /* syscons test failed */
252 /* Extract font size */
253 p
= strrchr(tmp
, '-');
254 if (p
&& p
[1] != '\0') {
256 /* Remove any '.fnt' extension */
257 if ((q
= strstr(p
, ".fnt")))
261 * Check font size is valid, with no trailing characters
262 * ('&ch' should not be matched)
264 if (sscanf(p
, "%dx%d%c", &i
, &i
, &ch
) != 2)
265 fprintf(stderr
, "Which font size? %s\n", fnt
);
268 asprintf(&cmd
, "vidcontrol -f %s %s", p
, fnt
);
270 fprintf(stderr
, "%s\n", cmd
);
275 fprintf(stderr
, "Which font size? %s\n", fnt
);
281 * Execute 'kbdcontrol' with the appropriate arguments
284 do_kbdcontrol(struct keymap
*km
)
287 asprintf(&kbd_cmd
, "kbdcontrol -l %s/%s", dir
, km
->keym
);
292 printf("keymap=%s\n", km
->keym
);
297 * Call 'vidcontrol' with the appropriate arguments
300 do_vidfont(struct keymap
*km
)
302 char *vid_cmd
, *tmp
, *p
, *q
;
304 asprintf(&vid_cmd
, "%s/%s", dir
, km
->keym
);
308 tmp
= strdup(km
->keym
);
309 p
= strrchr(tmp
, '-');
310 if (p
&& p
[1]!='\0') {
312 q
= get_extension(p
);
315 printf("font%s=%s\n", p
, km
->keym
);
322 * Display dialog from 'keymaps[]'
325 show_dialog(struct keymap
**km_sorted
, int num_keymaps
)
329 char tmp_name
[] = "/tmp/_kbd_lang.XXXX";
333 fd
= mkstemp(tmp_name
);
335 fprintf(stderr
, "Could not open temporary file \"%s\"\n",
339 asprintf(&dialog
, "/usr/bin/dialog --clear --title \"Keyboard Menu\" "
340 "--menu \"%s\" -1 -1 10", menu
);
342 ext
= extract_name(dir
);
344 /* start right font, assume that current font is equal
345 * to default font in /etc/rc.conf
347 * $font is the font which require the language $lang; e.g.
348 * russian *need* a koi8 font
349 * $font_current is the current font from /etc/rc.conf
351 if (font
&& strcmp(font
, font_current
))
354 /* Build up the command */
356 for (i
=0; i
<num_keymaps
; i
++) {
358 * Each 'font' is passed as ' "font" ""', so allow the
361 size
+= strlen(km_sorted
[i
]->desc
) + 6;
364 /* Allow the space for '2> tmpfilename' redirection */
365 size
+= strlen(tmp_name
) + 3;
367 cmd
= (char *) malloc(strlen(dialog
) + size
+ 1);
370 for (i
=0; i
<num_keymaps
; i
++) {
372 strcat(cmd
, km_sorted
[i
]->desc
);
374 strcat(cmd
, " \"\"");
378 strcat(cmd
, tmp_name
);
380 /* Show the dialog.. */
383 fp
= fopen(tmp_name
, "r");
386 if (fgets(choice
, 64, fp
) != NULL
) {
387 /* Find key for desc */
388 for (i
=0; i
<num_keymaps
; i
++) {
389 if (!strcmp(choice
, km_sorted
[i
]->desc
)) {
390 if (!strcmp(program
, "kbdmap"))
391 do_kbdcontrol(km_sorted
[i
]);
393 do_vidfont(km_sorted
[i
]);
398 if (font
!= NULL
&& strcmp(font
, font_current
))
399 /* Cancelled, restore old font */
400 vidcontrol(font_current
);
404 fprintf(stderr
, "Failed to open temporary file");
414 * Search for 'token' in comma delimited array 'buffer'.
415 * Return true for found, false for not found.
418 find_token(const char *buffer
, const char *token
)
420 char *buffer_tmp
, *buffer_copy
, *inputstring
;
424 buffer_copy
= strdup(buffer
);
425 buffer_tmp
= buffer_copy
;
426 inputstring
= buffer_copy
;
431 while ((*ap
= strsep(&inputstring
, ",")) != NULL
) {
432 if (strcmp(buffer_tmp
, token
) == 0) {
444 * Compare function for qsort
447 compare_keymap(const void *a
, const void *b
)
450 /* We've been passed pointers to pointers, so: */
451 const struct keymap
*km1
= *((const struct keymap
* const *) a
);
452 const struct keymap
*km2
= *((const struct keymap
* const *) b
);
454 return strcmp(km1
->desc
, km2
->desc
);
458 * Compare function for qsort
461 compare_lang(const void *a
, const void *b
)
463 const char *l1
= *((const char * const *) a
);
464 const char *l2
= *((const char * const *) b
);
466 return strcmp(l1
, l2
);
470 * Change '8x8' to '8x08' so qsort will put it before eg. '8x14'
473 kludge_desc(struct keymap
**km_sorted
, int num_keymaps
)
477 for (i
=0; i
<num_keymaps
; i
++) {
479 char *km
= km_sorted
[i
]->desc
;
480 if ((p
= strstr(km
, "8x8")) != NULL
) {
487 /* Make enough space for the extra '0' */
489 km
= realloc(km
, len
+ 2);
491 for (j
=len
; j
!=offset
+1; j
--)
496 km_sorted
[i
]->desc
= km
;
502 * Reverse 'kludge_desc()' - change '8x08' back to '8x8'
505 unkludge_desc(struct keymap
**km_sorted
, int num_keymaps
)
509 for (i
=0; i
<num_keymaps
; i
++) {
511 char *km
= km_sorted
[i
]->desc
;
512 if ((p
= strstr(km
, "8x08")) != NULL
) {
517 km
= realloc(km
, p
- km
- 1);
518 km_sorted
[i
]->desc
= km
;
524 * Return 0 if file exists and is readable, else -1
527 check_file(const char *keym
)
531 if (access(keym
, R_OK
) == -1) {
533 asprintf(&fn
, "%s/%s", dir
, keym
);
534 if (access(fn
, R_OK
) == -1) {
536 fprintf(stderr
, "%s not found!\n", fn
);
542 fprintf(stderr
, "No read permission for %s!\n", keym
);
550 * Read options from the relevent configuration file, then
558 int mark
, num_keymaps
, items
, i
;
559 char buffer
[256], filename
[PATH_MAX
];
560 char keym
[64], lng
[64], desc
[64];
561 char dialect
[64], lang_abk
[64];
563 struct keymap
**km_sorted
;
565 StringList
*lang_list
;
569 lang_list
= sl_init();
571 sprintf(filename
, "%s/INDEX.%s", dir
, extract_name(dir
));
573 /* en_US.ISO8859-1 -> en_..\.ISO8859-1 */
574 strlcpy(dialect
, lang
, sizeof(dialect
));
575 if (strlen(dialect
) >= 6 && dialect
[2] == '-') {
581 /* en_US.ISO8859-1 -> en */
582 strlcpy(lang_abk
, lang
, sizeof(lang_abk
));
583 if (strlen(lang_abk
) >= 3 && lang_abk
[2] == '-')
586 fprintf(stderr
, "lang_default = %s\n", lang_default
);
587 fprintf(stderr
, "dialect = %s\n", dialect
);
588 fprintf(stderr
, "lang_abk = %s\n", lang_abk
);
590 fp
= fopen(filename
, "r");
593 while (fgets(buffer
, sizeof(buffer
), fp
)) {
604 /* Parse input, removing newline */
605 matches
= sscanf(p
, "%64[^:]:%64[^:]:%64[^:\n]",
608 if (strcmp(keym
, "FONT")
609 && strcmp(keym
, "MENU")) {
610 /* Check file exists & is readable */
611 if (check_file(keym
) == -1)
618 * Take note of supported languages, which
619 * might be in a comma-delimited list
621 char *tmp
= strdup(lng
);
624 for (delim
= tmp
; ; ) {
626 if (ch
== ',' || ch
== '\0') {
628 if (!sl_find(lang_list
, tmp
))
629 sl_add(lang_list
, tmp
);
636 /* Set empty language to default language */
643 /* 4) Your choice if it exists
644 * 3) Long match eg. en_GB.ISO8859-1 is equal to
646 * 2) short match 'de'
647 * 1) default langlist 'en'
650 * Language may be a comma separated list
651 * A higher match overwrites a lower
652 * A later entry overwrites a previous if it exists
653 * twice in the database
656 /* Check for favoured language */
657 km
= get_keymap(keym
);
658 mark
= (km
) ? km
->mark
: 0;
660 if (find_token(lg
, lang
))
661 add_keymap(desc
, 4, keym
);
662 else if (mark
<= 3 && find_token(lg
, dialect
))
663 add_keymap(desc
, 3, keym
);
664 else if (mark
<= 2 && find_token(lg
, lang_abk
))
665 add_keymap(desc
, 2, keym
);
666 else if (mark
<= 1 && find_token(lg
, lang_default
))
667 add_keymap(desc
, 1, keym
);
669 add_keymap(desc
, 0, keym
);
674 printf("Could not open file\n");
677 qsort(lang_list
->sl_str
, lang_list
->sl_cur
, sizeof(char*),
679 printf("Currently supported languages: ");
680 for (i
=0; i
< (int) lang_list
->sl_cur
; i
++)
681 printf("%s ", lang_list
->sl_str
[i
]);
686 km
= get_keymap("MENU");
688 /* Take note of menu title */
689 menu
= strdup(km
->desc
);
690 km
= get_keymap("FONT");
692 /* Take note of language font */
693 font
= strdup(km
->desc
);
695 /* Remove unwanted items from list */
696 remove_keymap("MENU");
697 remove_keymap("FONT");
699 /* Look for keymaps not in database */
702 while ((dp
= readdir(dirp
)) != NULL
) {
703 const char *ext
= get_extension(dp
->d_name
);
705 if ((!strcmp(ext
, ".fnt") ||
706 !strcmp(ext
, ".kbd")) &&
707 !get_keymap(dp
->d_name
)) {
710 /* Remove any .fnt or .kbd extension */
711 q
= strdup(dp
->d_name
);
712 *(get_extension(q
)) = '\0';
713 add_keymap(q
, 0, dp
->d_name
);
718 "'%s' not in database\n",
725 fprintf(stderr
, "Could not open directory '%s'\n", dir
);
727 /* Sort items in keymap */
728 num_keymaps
= get_num_keymaps();
730 km_sorted
= (struct keymap
**)
731 malloc(num_keymaps
*sizeof(struct keymap
*));
733 /* Make array of pointers to items in hash */
735 SLIST_FOREACH(km
, &head
, entries
)
736 km_sorted
[items
++] = km
;
738 /* Change '8x8' to '8x08' so sort works as we might expect... */
739 kludge_desc(km_sorted
, num_keymaps
);
741 qsort(km_sorted
, num_keymaps
, sizeof(struct keymap
*), compare_keymap
);
743 /* ...change back again */
744 unkludge_desc(km_sorted
, num_keymaps
);
747 for (i
=0; i
<num_keymaps
; i
++)
748 printf("%s\n", km_sorted
[i
]->desc
);
752 show_dialog(km_sorted
, num_keymaps
);
758 * Display usage information and exit
764 fprintf(stderr
, "usage: %s\t[-K] [-V] [-d|-default] [-h|-help] "
765 "[-l|-lang language]\n\t\t[-p|-print] [-r|-restore] [-s|-show] "
766 "[-v|-verbose]\n", program
);
771 parse_args(int argc
, char **argv
)
775 for (i
=1; i
<argc
; i
++) {
776 if (argv
[i
][0] != '-')
778 else if (!strcmp(argv
[i
], "-help") || !strcmp(argv
[i
], "-h"))
780 else if (!strcmp(argv
[i
], "-verbose") || !strcmp(argv
[i
], "-v"))
782 else if (!strcmp(argv
[i
], "-lang") || !strcmp(argv
[i
], "-l"))
787 else if (!strcmp(argv
[i
], "-default") || !strcmp(argv
[i
], "-d"))
789 else if (!strcmp(argv
[i
], "-show") || !strcmp(argv
[i
], "-s"))
791 else if (!strcmp(argv
[i
], "-print") || !strcmp(argv
[i
], "-p"))
793 else if (!strcmp(argv
[i
], "-restore") ||
794 !strcmp(argv
[i
], "-r")) {
795 vidcontrol(font_current
);
797 } else if (!strcmp(argv
[i
], "-K"))
799 else if (!strcmp(argv
[i
], "-V"))
807 * A front-end for the 'vidfont' and 'kbdmap' programs.
810 main(int argc
, char **argv
)
813 x11
= system("kbdcontrol -d >/dev/null");
816 fprintf(stderr
, "You are not on a virtual console - "
817 "expect certain strange side-effects\n");
825 program
= extract_name(argv
[0]);
827 /* Parse command line arguments */
828 parse_args(argc
, argv
);
830 font_current
= get_font();
831 if (font_current
== NULL
)
832 font_current
= font_default
;
834 if (strcmp(program
, "kbdmap"))
839 /* Read and display options */