]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/kbdmap/kbdmap.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / kbdmap / kbdmap.c
1 /*-
2  * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/types.h>
31 #include <sys/queue.h>
32
33 #include <assert.h>
34 #include <ctype.h>
35 #include <dirent.h>
36 #include <limits.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stringlist.h>
41 #include <unistd.h>
42
43 #include "kbdmap.h"
44
45
46 static const char *lang_default = DEFAULT_LANG;
47 static const char *font;
48 static const char *lang;
49 static const char *program;
50 static const char *keymapdir = DEFAULT_KEYMAP_DIR;
51 static const char *fontdir = DEFAULT_FONT_DIR;
52 static const char *sysconfig = DEFAULT_SYSCONFIG;
53 static const char *font_default = DEFAULT_FONT;
54 static const char *font_current;
55 static const char *dir;
56 static const char *menu = "";
57
58 static int x11;
59 static int show;
60 static int verbose;
61 static int print;
62
63
64 struct keymap {
65         char    *desc;
66         char    *keym;
67         int     mark;
68         SLIST_ENTRY(keymap) entries;
69 };
70 static SLIST_HEAD(slisthead, keymap) head = SLIST_HEAD_INITIALIZER(head);
71
72
73 /*
74  * Get keymap entry for 'key', or NULL of not found
75  */
76 static struct keymap *
77 get_keymap(const char *key)
78 {
79         struct keymap *km;
80
81         SLIST_FOREACH(km, &head, entries)
82                 if (!strcmp(km->keym, key))
83                         return km;
84
85         return NULL;
86 }
87
88 /*
89  * Count the number of keymaps we found
90  */
91 static int
92 get_num_keymaps(void)
93 {
94         struct keymap *km;
95         int count = 0;
96
97         SLIST_FOREACH(km, &head, entries)
98                 count++;
99
100         return count;
101 }
102
103 /*
104  * Remove any keymap with given keym
105  */
106 static void
107 remove_keymap(const char *keym)
108 {
109         struct keymap *km;
110
111         SLIST_FOREACH(km, &head, entries) {
112                 if (!strcmp(keym, km->keym)) {
113                         SLIST_REMOVE(&head, km, keymap, entries);
114                         free(km);
115                         break;
116                 }
117         }
118 }
119
120 /*
121  * Add to hash with 'key'
122  */
123 static void
124 add_keymap(const char *desc, int mark, const char *keym)
125 {
126         struct keymap *km, *km_new;
127
128         /* Is there already an entry with this key? */
129         SLIST_FOREACH(km, &head, entries) {
130                 if (!strcmp(km->keym, keym)) {
131                         /* Reuse this entry */
132                         free(km->desc);
133                         km->desc = strdup(desc);
134                         km->mark = mark;
135                         return;
136                 }
137         }
138
139         km_new = (struct keymap *) malloc (sizeof(struct keymap));
140         km_new->desc = strdup(desc);
141         km_new->keym = strdup(keym);
142         km_new->mark = mark;
143
144         /* Add to keymap list */
145         SLIST_INSERT_HEAD(&head, km_new, entries);
146 }
147
148 /*
149  * Figure out the default language to use.
150  */
151 static const char *
152 get_locale(void)
153 {
154         const char *locale;
155
156         if ((locale = getenv("LC_ALL")) == NULL &&
157             (locale = getenv("LC_CTYPE")) == NULL &&
158             (locale = getenv("LANG")) == NULL)
159                 locale = lang_default;
160
161         /* Check for alias */
162         if (!strcmp(locale, "C"))
163                 locale = DEFAULT_LANG;
164
165         return locale;
166 }
167
168 /*
169  * Extract filename part
170  */
171 static const char *
172 extract_name(const char *name)
173 {
174         char *p;
175
176         p = strrchr(name, '/');
177         if (p != NULL && p[1] != '\0')
178                 return p + 1;
179
180         return name;
181 }
182
183 /*
184  * Return file extension or NULL
185  */
186 static char *
187 get_extension(const char *name)
188 {
189         char *p;
190
191         p = strrchr(name, '.');
192
193         if (p != NULL && p[1] != '\0')
194                 return p;
195
196         return NULL;
197 }
198
199 /*
200  * Read font from /etc/rc.conf else return default.
201  * Freeing the memory is the caller's responsibility.
202  */
203 static char *
204 get_font(void)
205 {
206         char line[256], buf[20];
207         char *fnt = NULL;
208
209         FILE *fp = fopen(sysconfig, "r");
210         if (fp) {
211                 while (fgets(line, sizeof(line), fp)) {
212                         int a, b, matches;
213
214                         if (line[0] == '#')
215                                 continue;
216
217                         matches = sscanf(line,
218                             " font%dx%d = \"%20[-.0-9a-zA-Z_]",
219                             &a, &b, buf);
220                         if (matches==3) {
221                                 if (strcmp(buf, "NO")) {
222                                         if (fnt)
223                                                 free(fnt);
224                                         fnt = (char *) malloc(strlen(buf) + 1);
225                                         strcpy(fnt, buf);
226                                 }
227                         }
228                 }
229                 fclose(fp);
230         } else
231                 fprintf(stderr, "Could not open %s for reading\n", sysconfig);
232
233         return fnt;
234 }
235
236 /*
237  * Set a font using 'vidcontrol'
238  */
239 static void
240 vidcontrol(const char *fnt)
241 {
242         char *tmp, *p, *q;
243         char ch;
244         int i;
245
246         /* syscons test failed */
247         if (x11)
248                 return;
249
250         tmp = strdup(fnt);
251
252         /* Extract font size */
253         p = strrchr(tmp, '-');
254         if (p && p[1] != '\0') {
255                 p++;
256                 /* Remove any '.fnt' extension */
257                 if ((q = strstr(p, ".fnt")))
258                         *q = '\0';
259
260                 /*
261                  * Check font size is valid, with no trailing characters
262                  *  ('&ch' should not be matched)
263                  */
264                 if (sscanf(p, "%dx%d%c", &i, &i, &ch) != 2)
265                         fprintf(stderr, "Which font size? %s\n", fnt);
266                 else {
267                         char *cmd;
268                         asprintf(&cmd, "vidcontrol -f %s %s", p, fnt);
269                         if (verbose)
270                                 fprintf(stderr, "%s\n", cmd);
271                         system(cmd);
272                         free(cmd);
273                 }
274         } else
275                 fprintf(stderr, "Which font size? %s\n", fnt);
276
277         free(tmp);
278 }
279
280 /*
281  * Execute 'kbdcontrol' with the appropriate arguments
282  */
283 static void
284 do_kbdcontrol(struct keymap *km)
285 {
286         char *kbd_cmd;
287         asprintf(&kbd_cmd, "kbdcontrol -l %s/%s", dir, km->keym);
288
289         if (!x11)
290                 system(kbd_cmd);
291
292         fprintf(stderr, "keymap=\"%s\"\n", km->keym);
293         free(kbd_cmd);
294 }
295
296 /*
297  * Call 'vidcontrol' with the appropriate arguments
298  */
299 static void
300 do_vidfont(struct keymap *km)
301 {
302         char *vid_cmd, *tmp, *p, *q;
303
304         asprintf(&vid_cmd, "%s/%s", dir, km->keym);
305         vidcontrol(vid_cmd);
306         free(vid_cmd);
307
308         tmp = strdup(km->keym);
309         p = strrchr(tmp, '-');
310         if (p && p[1]!='\0') {
311                 p++;
312                 q = get_extension(p);
313                 if (q) {
314                         *q = '\0';
315                         printf("font%s=%s\n", p, km->keym);
316                 }
317         }
318         free(tmp);
319 }
320
321 /*
322  * Display dialog from 'keymaps[]'
323  */
324 static void
325 show_dialog(struct keymap **km_sorted, int num_keymaps)
326 {
327         FILE *fp;
328         char *cmd, *dialog;
329         char tmp_name[] = "/tmp/_kbd_lang.XXXX";
330         int fd, i, size;
331
332         fd = mkstemp(tmp_name);
333         if (fd == -1) {
334                 fprintf(stderr, "Could not open temporary file \"%s\"\n",
335                     tmp_name);
336                 exit(1);
337         }
338         asprintf(&dialog, "/usr/bin/dialog --clear --title \"Keyboard Menu\" "
339                           "--menu \"%s\" 0 0 0", menu);
340
341         /* start right font, assume that current font is equal
342          * to default font in /etc/rc.conf
343          *      
344          * $font is the font which require the language $lang; e.g.
345          * russian *need* a koi8 font
346          * $font_current is the current font from /etc/rc.conf
347          */
348         if (font && strcmp(font, font_current))
349                 vidcontrol(font);
350
351         /* Build up the command */
352         size = 0;
353         for (i=0; i<num_keymaps; i++) {
354                 /*
355                  * Each 'font' is passed as ' "font" ""', so allow the
356                  * extra space
357                  */
358                 size += strlen(km_sorted[i]->desc) + 6;
359         }
360
361         /* Allow the space for '2> tmpfilename' redirection */
362         size += strlen(tmp_name) + 3;
363
364         cmd = (char *) malloc(strlen(dialog) + size + 1);
365         strcpy(cmd, dialog);
366
367         for (i=0; i<num_keymaps; i++) {
368                 strcat(cmd, " \"");
369                 strcat(cmd, km_sorted[i]->desc);
370                 strcat(cmd, "\"");
371                 strcat(cmd, " \"\"");
372         }
373
374         strcat(cmd, " 2>");
375         strcat(cmd, tmp_name);
376
377         /* Show the dialog.. */
378         system(cmd);
379
380         fp = fopen(tmp_name, "r");
381         if (fp) {
382                 char choice[64];
383                 if (fgets(choice, sizeof(choice), fp) != NULL) {
384                         /* Find key for desc */
385                         for (i=0; i<num_keymaps; i++) {
386                                 if (!strcmp(choice, km_sorted[i]->desc)) {
387                                         if (!strcmp(program, "kbdmap"))
388                                                 do_kbdcontrol(km_sorted[i]);
389                                         else
390                                                 do_vidfont(km_sorted[i]);
391                                         break;
392                                 }
393                         }
394                 } else {
395                         if (font != NULL && strcmp(font, font_current))
396                                 /* Cancelled, restore old font */
397                                 vidcontrol(font_current);
398                 }
399                 fclose(fp);
400         } else
401                 fprintf(stderr, "Failed to open temporary file");
402
403         /* Tidy up */
404         remove(tmp_name);
405         free(cmd);
406         free(dialog);
407         close(fd);
408 }
409
410 /*
411  * Search for 'token' in comma delimited array 'buffer'.
412  * Return true for found, false for not found.
413  */
414 static int
415 find_token(const char *buffer, const char *token)
416 {
417         char *buffer_tmp, *buffer_copy, *inputstring;
418         char **ap;
419         int found;
420
421         buffer_copy = strdup(buffer);
422         buffer_tmp = buffer_copy;
423         inputstring = buffer_copy;
424         ap = &buffer_tmp;
425
426         found = 0;
427
428         while ((*ap = strsep(&inputstring, ",")) != NULL) {
429                 if (strcmp(buffer_tmp, token) == 0) {
430                         found = 1;
431                         break;
432                 }
433         }
434
435         free(buffer_copy);
436
437         return found;
438 }
439
440 /*
441  * Compare function for qsort
442  */
443 static int
444 compare_keymap(const void *a, const void *b)
445 {
446
447         /* We've been passed pointers to pointers, so: */
448         const struct keymap *km1 = *((const struct keymap * const *) a);
449         const struct keymap *km2 = *((const struct keymap * const *) b);
450
451         return strcmp(km1->desc, km2->desc);
452 }
453
454 /*
455  * Compare function for qsort
456  */
457 static int
458 compare_lang(const void *a, const void *b)
459 {
460         const char *l1 = *((const char * const *) a);
461         const char *l2 = *((const char * const *) b);
462
463         return strcmp(l1, l2);
464 }
465
466 /*
467  * Change '8x8' to '8x08' so qsort will put it before eg. '8x14'
468  */
469 static void
470 kludge_desc(struct keymap **km_sorted, int num_keymaps)
471 {
472         int i;
473
474         for (i=0; i<num_keymaps; i++) {
475                 char *p;
476                 char *km = km_sorted[i]->desc;
477                 if ((p = strstr(km, "8x8")) != NULL) {
478                         int len;
479                         int j;
480                         int offset;
481
482                         offset = p - km;
483
484                         /* Make enough space for the extra '0' */
485                         len = strlen(km);
486                         km = realloc(km, len + 2);
487
488                         for (j=len; j!=offset+1; j--)
489                                 km[j + 1] = km[j];
490
491                         km[offset+2] = '0';
492
493                         km_sorted[i]->desc = km;
494                 }
495         }
496 }
497
498 /*
499  * Reverse 'kludge_desc()' - change '8x08' back to '8x8'
500  */
501 static void
502 unkludge_desc(struct keymap **km_sorted, int num_keymaps)
503 {
504         int i;
505
506         for (i=0; i<num_keymaps; i++) {
507                 char *p;
508                 char *km = km_sorted[i]->desc;
509                 if ((p = strstr(km, "8x08")) != NULL) {
510                         p += 2;
511                         while (*p++)
512                                 p[-1] = p[0];
513
514                         km = realloc(km, p - km - 1);
515                         km_sorted[i]->desc = km;
516                 }
517         }
518 }
519
520 /*
521  * Return 0 if file exists and is readable, else -1
522  */
523 static int
524 check_file(const char *keym)
525 {
526         int status = 0;
527
528         if (access(keym, R_OK) == -1) {
529                 char *fn;
530                 asprintf(&fn, "%s/%s", dir, keym);
531                 if (access(fn, R_OK) == -1) {
532                         if (verbose)
533                                 fprintf(stderr, "%s not found!\n", fn);
534                         status = -1;
535                 }
536                 free(fn);
537         } else {
538                 if (verbose)
539                         fprintf(stderr, "No read permission for %s!\n", keym);
540                 status = -1;
541         }
542
543         return status;
544 }
545
546 /*
547  * Read options from the relevant configuration file, then
548  *  present to user.
549  */
550 static void
551 menu_read(void)
552 {
553         const char *lg;
554         char *p;
555         int mark, num_keymaps, items, i;
556         char buffer[256], filename[PATH_MAX];
557         char keym[64], lng[64], desc[64];
558         char dialect[64], lang_abk[64];
559         struct keymap *km;
560         struct keymap **km_sorted;
561         struct dirent *dp;
562         StringList *lang_list;
563         FILE *fp;
564         DIR *dirp;
565
566         lang_list = sl_init();
567
568         sprintf(filename, "%s/INDEX.%s", dir, extract_name(dir));
569
570         /* en_US.ISO8859-1 -> en_..\.ISO8859-1 */
571         strlcpy(dialect, lang, sizeof(dialect));
572         if (strlen(dialect) >= 6 && dialect[2] == '_') {
573                 dialect[3] = '.';
574                 dialect[4] = '.';
575         }
576
577
578         /* en_US.ISO8859-1 -> en */
579         strlcpy(lang_abk, lang, sizeof(lang_abk));
580         if (strlen(lang_abk) >= 3 && lang_abk[2] == '_')
581                 lang_abk[2] = '\0';
582
583         fprintf(stderr, "lang_default = %s\n", lang_default);
584         fprintf(stderr, "dialect = %s\n", dialect);
585         fprintf(stderr, "lang_abk = %s\n", lang_abk);
586
587         fp = fopen(filename, "r");
588         if (fp) {
589                 int matches;
590                 while (fgets(buffer, sizeof(buffer), fp)) {
591                         p = buffer;
592                         if (p[0] == '#')
593                                 continue;
594
595                         while (isspace(*p))
596                                 p++;
597
598                         if (*p == '\0')
599                                 continue;
600
601                         /* Parse input, removing newline */
602                         matches = sscanf(p, "%64[^:]:%64[^:]:%64[^:\n]", 
603                             keym, lng, desc);
604                         if (matches == 3) {
605                                 if (strcmp(keym, "FONT")
606                                     && strcmp(keym, "MENU")) {
607                                         /* Check file exists & is readable */
608                                         if (check_file(keym) == -1)
609                                                 continue;
610                                 }
611                         }
612
613                         if (show) {
614                                 /*
615                                  * Take note of supported languages, which
616                                  * might be in a comma-delimited list
617                                  */
618                                 char *tmp = strdup(lng);
619                                 char *delim = tmp;
620
621                                 for (delim = tmp; ; ) {
622                                         char ch = *delim++;
623                                         if (ch == ',' || ch == '\0') {
624                                                 delim[-1] = '\0';
625                                                 if (!sl_find(lang_list, tmp))
626                                                         sl_add(lang_list, tmp);
627                                                 if (ch == '\0')
628                                                         break;
629                                                 tmp = delim;
630                                         }
631                                 }
632                         }
633                         /* Set empty language to default language */
634                         if (lng[0] == '\0')
635                                 lg = lang_default;
636                         else
637                                 lg = lng;
638
639
640                         /* 4) Your choice if it exists
641                          * 3) Long match eg. en_GB.ISO8859-1 is equal to
642                          *      en_..\.ISO8859-1
643                          * 2) short match 'de'
644                          * 1) default langlist 'en'
645                          * 0) any language
646                          *
647                          * Language may be a comma separated list
648                          * A higher match overwrites a lower
649                          * A later entry overwrites a previous if it exists
650                          *     twice in the database
651                          */
652
653                         /* Check for favoured language */
654                         km = get_keymap(keym);
655                         mark = (km) ? km->mark : 0;
656
657                         if (find_token(lg, lang))
658                                 add_keymap(desc, 4, keym);
659                         else if (mark <= 3 && find_token(lg, dialect))
660                                 add_keymap(desc, 3, keym);
661                         else if (mark <= 2 && find_token(lg, lang_abk))
662                                 add_keymap(desc, 2, keym);
663                         else if (mark <= 1 && find_token(lg, lang_default))
664                                 add_keymap(desc, 1, keym);
665                         else if (mark <= 0)
666                                 add_keymap(desc, 0, keym);
667                 }
668                 fclose(fp);
669
670         } else
671                 printf("Could not open file\n");
672
673         if (show) {
674                 qsort(lang_list->sl_str, lang_list->sl_cur, sizeof(char*),
675                     compare_lang);
676                 printf("Currently supported languages: ");
677                 for (i=0; i< (int) lang_list->sl_cur; i++)
678                         printf("%s ", lang_list->sl_str[i]);
679                 puts("");
680                 exit(0);
681         }
682
683         km = get_keymap("MENU");
684         if (km)
685                 /* Take note of menu title */
686                 menu = strdup(km->desc);
687         km = get_keymap("FONT");
688         if (km)
689                 /* Take note of language font */
690                 font = strdup(km->desc);
691
692         /* Remove unwanted items from list */
693         remove_keymap("MENU");
694         remove_keymap("FONT");
695
696         /* Look for keymaps not in database */
697         dirp = opendir(dir);
698         if (dirp) {
699                 while ((dp = readdir(dirp)) != NULL) {
700                         const char *ext = get_extension(dp->d_name);
701                         if (ext) {
702                                 if ((!strcmp(ext, ".fnt") ||
703                                     !strcmp(ext, ".kbd")) &&
704                                     !get_keymap(dp->d_name)) {
705                                         char *q;
706
707                                         /* Remove any .fnt or .kbd extension */
708                                         q = strdup(dp->d_name);
709                                         *(get_extension(q)) = '\0';
710                                         add_keymap(q, 0, dp->d_name);
711                                         free(q);
712
713                                         if (verbose)
714                                                 fprintf(stderr,
715                                                     "'%s' not in database\n",
716                                                     dp->d_name);
717                                 }
718                         }
719                 }
720                 closedir(dirp);
721         } else
722                 fprintf(stderr, "Could not open directory '%s'\n", dir);
723
724         /* Sort items in keymap */
725         num_keymaps = get_num_keymaps();
726
727         km_sorted = (struct keymap **)
728             malloc(num_keymaps*sizeof(struct keymap *));
729
730         /* Make array of pointers to items in hash */
731         items = 0;
732         SLIST_FOREACH(km, &head, entries)
733                 km_sorted[items++] = km;
734
735         /* Change '8x8' to '8x08' so sort works as we might expect... */
736         kludge_desc(km_sorted, num_keymaps);
737
738         qsort(km_sorted, num_keymaps, sizeof(struct keymap *), compare_keymap);
739
740         /* ...change back again */
741         unkludge_desc(km_sorted, num_keymaps);
742
743         if (print) {
744                 for (i=0; i<num_keymaps; i++)
745                         printf("%s\n", km_sorted[i]->desc);
746                 exit(0);
747         }
748
749         show_dialog(km_sorted, num_keymaps);
750
751         free(km_sorted);
752 }
753
754 /*
755  * Display usage information and exit
756  */
757 static void
758 usage(void)
759 {
760
761         fprintf(stderr, "usage: %s\t[-K] [-V] [-d|-default] [-h|-help] "
762             "[-l|-lang language]\n\t\t[-p|-print] [-r|-restore] [-s|-show] "
763             "[-v|-verbose]\n", program);
764         exit(1);
765 }
766
767 static void
768 parse_args(int argc, char **argv)
769 {
770         int i;
771
772         for (i=1; i<argc; i++) {
773                 if (argv[i][0] != '-')
774                         usage();
775                 else if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h"))
776                         usage();
777                 else if (!strcmp(argv[i], "-verbose") || !strcmp(argv[i], "-v"))
778                         verbose = 1;
779                 else if (!strcmp(argv[i], "-lang") || !strcmp(argv[i], "-l"))
780                         if (i + 1 == argc)
781                                 usage();
782                         else
783                                 lang = argv[++i];
784                 else if (!strcmp(argv[i], "-default") || !strcmp(argv[i], "-d"))
785                         lang = lang_default;
786                 else if (!strcmp(argv[i], "-show") || !strcmp(argv[i], "-s"))
787                         show = 1;
788                 else if (!strcmp(argv[i], "-print") || !strcmp(argv[i], "-p"))
789                         print = 1;
790                 else if (!strcmp(argv[i], "-restore") ||
791                     !strcmp(argv[i], "-r")) {
792                         vidcontrol(font_current);
793                         exit(0);
794                 } else if (!strcmp(argv[i], "-K"))
795                         dir = keymapdir;
796                 else if (!strcmp(argv[i], "-V"))
797                         dir = fontdir;
798                 else
799                         usage();
800         }
801 }
802
803 /*
804  * A front-end for the 'vidfont' and 'kbdmap' programs.
805  */
806 int
807 main(int argc, char **argv)
808 {
809
810         x11 = system("kbdcontrol -d >/dev/null");
811
812         if (x11) {
813                 fprintf(stderr, "You are not on a virtual console - "
814                                 "expect certain strange side-effects\n");
815                 sleep(2);
816         }
817
818         SLIST_INIT(&head);
819
820         lang = get_locale();
821
822         program = extract_name(argv[0]);
823
824         font_current = get_font();
825         if (font_current == NULL)
826                 font_current = font_default;
827
828         if (strcmp(program, "kbdmap"))
829                 dir = fontdir;
830         else
831                 dir = keymapdir;
832
833         /* Parse command line arguments */
834         parse_args(argc, argv);
835
836         /* Read and display options */
837         menu_read();
838
839         return 0;
840 }