199357Smarkm/*-
299357Smarkm * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
399357Smarkm * All rights reserved.
499357Smarkm *
599357Smarkm * Redistribution and use in source and binary forms, with or without
699357Smarkm * modification, are permitted provided that the following conditions
799357Smarkm * are met:
899357Smarkm * 1. Redistributions of source code must retain the above copyright
999357Smarkm *    notice, this list of conditions and the following disclaimer.
1099357Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1199357Smarkm *    notice, this list of conditions and the following disclaimer in the
1299357Smarkm *    documentation and/or other materials provided with the distribution.
1399357Smarkm *
1499357Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1599357Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1699357Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1799357Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1899357Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1999357Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2099357Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2199357Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2299357Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2399357Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2499357Smarkm * SUCH DAMAGE.
2599357Smarkm */
2699357Smarkm
2799357Smarkm#include <sys/cdefs.h>
2899357Smarkm__FBSDID("$FreeBSD$");
2999357Smarkm
3099357Smarkm#include <sys/types.h>
3199357Smarkm#include <sys/queue.h>
32270309Sse#include <sys/sysctl.h>
3399357Smarkm
3499357Smarkm#include <assert.h>
3599357Smarkm#include <ctype.h>
3699357Smarkm#include <dirent.h>
37106053Swollman#include <limits.h>
3899357Smarkm#include <stdio.h>
3999357Smarkm#include <stdlib.h>
4099357Smarkm#include <string.h>
4199357Smarkm#include <stringlist.h>
4299357Smarkm#include <unistd.h>
4399357Smarkm
4499357Smarkm#include "kbdmap.h"
4599357Smarkm
4699357Smarkm
4799357Smarkmstatic const char *lang_default = DEFAULT_LANG;
4899357Smarkmstatic const char *font;
4999357Smarkmstatic const char *lang;
5099357Smarkmstatic const char *program;
51270309Ssestatic const char *keymapdir = DEFAULT_VT_KEYMAP_DIR;
52270309Ssestatic const char *fontdir = DEFAULT_VT_FONT_DIR;
53270309Ssestatic const char *font_default = DEFAULT_VT_FONT;
5499357Smarkmstatic const char *sysconfig = DEFAULT_SYSCONFIG;
5599357Smarkmstatic const char *font_current;
5699357Smarkmstatic const char *dir;
5799357Smarkmstatic const char *menu = "";
5899357Smarkm
5999357Smarkmstatic int x11;
6099357Smarkmstatic int show;
6199357Smarkmstatic int verbose;
6299357Smarkmstatic int print;
6399357Smarkm
6499357Smarkm
6599357Smarkmstruct keymap {
6699357Smarkm	char	*desc;
6799357Smarkm	char	*keym;
6899357Smarkm	int	mark;
6999357Smarkm	SLIST_ENTRY(keymap) entries;
7099357Smarkm};
7199357Smarkmstatic SLIST_HEAD(slisthead, keymap) head = SLIST_HEAD_INITIALIZER(head);
7299357Smarkm
7399357Smarkm
7499357Smarkm/*
7599357Smarkm * Get keymap entry for 'key', or NULL of not found
7699357Smarkm */
7799357Smarkmstatic struct keymap *
7899357Smarkmget_keymap(const char *key)
7999357Smarkm{
8099357Smarkm	struct keymap *km;
8199357Smarkm
8299357Smarkm	SLIST_FOREACH(km, &head, entries)
8399357Smarkm		if (!strcmp(km->keym, key))
8499357Smarkm			return km;
8599357Smarkm
8699357Smarkm	return NULL;
8799357Smarkm}
8899357Smarkm
8999357Smarkm/*
9099357Smarkm * Count the number of keymaps we found
9199357Smarkm */
9299357Smarkmstatic int
9399357Smarkmget_num_keymaps(void)
9499357Smarkm{
9599357Smarkm	struct keymap *km;
9699357Smarkm	int count = 0;
9799357Smarkm
9899357Smarkm	SLIST_FOREACH(km, &head, entries)
9999357Smarkm		count++;
10099357Smarkm
10199357Smarkm	return count;
10299357Smarkm}
10399357Smarkm
10499357Smarkm/*
10599357Smarkm * Remove any keymap with given keym
10699357Smarkm */
10799357Smarkmstatic void
10899357Smarkmremove_keymap(const char *keym)
10999357Smarkm{
11099357Smarkm	struct keymap *km;
11199357Smarkm
11299357Smarkm	SLIST_FOREACH(km, &head, entries) {
11399357Smarkm		if (!strcmp(keym, km->keym)) {
11499357Smarkm			SLIST_REMOVE(&head, km, keymap, entries);
11599357Smarkm			free(km);
11699357Smarkm			break;
11799357Smarkm		}
11899357Smarkm	}
11999357Smarkm}
12099357Smarkm
12199357Smarkm/*
12299357Smarkm * Add to hash with 'key'
12399357Smarkm */
12499357Smarkmstatic void
12599357Smarkmadd_keymap(const char *desc, int mark, const char *keym)
12699357Smarkm{
12799357Smarkm	struct keymap *km, *km_new;
12899357Smarkm
12999357Smarkm	/* Is there already an entry with this key? */
13099357Smarkm	SLIST_FOREACH(km, &head, entries) {
13199357Smarkm		if (!strcmp(km->keym, keym)) {
13299357Smarkm			/* Reuse this entry */
13399357Smarkm			free(km->desc);
13499357Smarkm			km->desc = strdup(desc);
13599357Smarkm			km->mark = mark;
13699357Smarkm			return;
13799357Smarkm		}
13899357Smarkm	}
13999357Smarkm
14099357Smarkm	km_new = (struct keymap *) malloc (sizeof(struct keymap));
14199357Smarkm	km_new->desc = strdup(desc);
14299357Smarkm	km_new->keym = strdup(keym);
14399357Smarkm	km_new->mark = mark;
14499357Smarkm
14599357Smarkm	/* Add to keymap list */
14699357Smarkm	SLIST_INSERT_HEAD(&head, km_new, entries);
14799357Smarkm}
14899357Smarkm
14999357Smarkm/*
150270309Sse * Return 0 if syscons is in use (to select legacy defaults).
151270309Sse */
152270309Ssestatic int
153270309Ssecheck_newcons(void)
154270309Sse{
155270309Sse	size_t len;
156270309Sse	char term[3];
157270309Sse
158270309Sse	len = 3;
159270309Sse	if (sysctlbyname("kern.vty", &term, &len, NULL, 0) != 0 ||
160270309Sse	    strcmp(term, "vt") != 0)
161270309Sse		return 0;
162270309Sse	return -1;
163270309Sse}
164270309Sse
165270309Sse/*
16699357Smarkm * Figure out the default language to use.
16799357Smarkm */
16899357Smarkmstatic const char *
16999357Smarkmget_locale(void)
17099357Smarkm{
17199357Smarkm	const char *locale;
17299357Smarkm
17399357Smarkm	if ((locale = getenv("LC_ALL")) == NULL &&
17499357Smarkm	    (locale = getenv("LC_CTYPE")) == NULL &&
17599357Smarkm	    (locale = getenv("LANG")) == NULL)
17699357Smarkm		locale = lang_default;
17799357Smarkm
17899357Smarkm	/* Check for alias */
17999357Smarkm	if (!strcmp(locale, "C"))
18099357Smarkm		locale = DEFAULT_LANG;
18199357Smarkm
18299357Smarkm	return locale;
18399357Smarkm}
18499357Smarkm
18599357Smarkm/*
18699357Smarkm * Extract filename part
18799357Smarkm */
18899357Smarkmstatic const char *
18999357Smarkmextract_name(const char *name)
19099357Smarkm{
19199357Smarkm	char *p;
19299357Smarkm
19399357Smarkm	p = strrchr(name, '/');
19499357Smarkm	if (p != NULL && p[1] != '\0')
19599357Smarkm		return p + 1;
19699357Smarkm
19799357Smarkm	return name;
19899357Smarkm}
19999357Smarkm
20099357Smarkm/*
20199357Smarkm * Return file extension or NULL
20299357Smarkm */
20399357Smarkmstatic char *
20499357Smarkmget_extension(const char *name)
20599357Smarkm{
20699357Smarkm	char *p;
20799357Smarkm
20899357Smarkm	p = strrchr(name, '.');
20999357Smarkm
21099357Smarkm	if (p != NULL && p[1] != '\0')
21199357Smarkm		return p;
21299357Smarkm
21399357Smarkm	return NULL;
21499357Smarkm}
21599357Smarkm
21699357Smarkm/*
21799357Smarkm * Read font from /etc/rc.conf else return default.
21899357Smarkm * Freeing the memory is the caller's responsibility.
21999357Smarkm */
22099357Smarkmstatic char *
22199357Smarkmget_font(void)
22299357Smarkm{
22399357Smarkm	char line[256], buf[20];
22499357Smarkm	char *fnt = NULL;
22599357Smarkm
22699357Smarkm	FILE *fp = fopen(sysconfig, "r");
22799357Smarkm	if (fp) {
22899357Smarkm		while (fgets(line, sizeof(line), fp)) {
22999357Smarkm			int a, b, matches;
23099357Smarkm
23199357Smarkm			if (line[0] == '#')
23299357Smarkm				continue;
23399357Smarkm
23499357Smarkm			matches = sscanf(line,
23599357Smarkm			    " font%dx%d = \"%20[-.0-9a-zA-Z_]",
23699357Smarkm			    &a, &b, buf);
23799357Smarkm			if (matches==3) {
23899357Smarkm				if (strcmp(buf, "NO")) {
23999357Smarkm					if (fnt)
24099357Smarkm						free(fnt);
24199357Smarkm					fnt = (char *) malloc(strlen(buf) + 1);
24299357Smarkm					strcpy(fnt, buf);
24399357Smarkm				}
24499357Smarkm			}
24599357Smarkm		}
246222568Sjh		fclose(fp);
24799357Smarkm	} else
24899357Smarkm		fprintf(stderr, "Could not open %s for reading\n", sysconfig);
24999357Smarkm
25099357Smarkm	return fnt;
25199357Smarkm}
25299357Smarkm
25399357Smarkm/*
25499357Smarkm * Set a font using 'vidcontrol'
25599357Smarkm */
25699357Smarkmstatic void
25799357Smarkmvidcontrol(const char *fnt)
25899357Smarkm{
25999357Smarkm	char *tmp, *p, *q;
26099357Smarkm	char ch;
26199357Smarkm	int i;
26299357Smarkm
26399357Smarkm	/* syscons test failed */
26499357Smarkm	if (x11)
26599357Smarkm		return;
26699357Smarkm
26799357Smarkm	tmp = strdup(fnt);
26899357Smarkm
26999357Smarkm	/* Extract font size */
27099357Smarkm	p = strrchr(tmp, '-');
27199357Smarkm	if (p && p[1] != '\0') {
27299357Smarkm		p++;
27399357Smarkm		/* Remove any '.fnt' extension */
27499357Smarkm		if ((q = strstr(p, ".fnt")))
27599357Smarkm			*q = '\0';
27699357Smarkm
27799357Smarkm		/*
27899357Smarkm		 * Check font size is valid, with no trailing characters
27999357Smarkm		 *  ('&ch' should not be matched)
28099357Smarkm		 */
28199357Smarkm		if (sscanf(p, "%dx%d%c", &i, &i, &ch) != 2)
28299357Smarkm			fprintf(stderr, "Which font size? %s\n", fnt);
28399357Smarkm		else {
28499357Smarkm			char *cmd;
28599357Smarkm			asprintf(&cmd, "vidcontrol -f %s %s", p, fnt);
28699357Smarkm			if (verbose)
28799357Smarkm				fprintf(stderr, "%s\n", cmd);
28899357Smarkm			system(cmd);
28999357Smarkm			free(cmd);
29099357Smarkm		}
29199357Smarkm	} else
29299357Smarkm		fprintf(stderr, "Which font size? %s\n", fnt);
29399357Smarkm
29499357Smarkm	free(tmp);
29599357Smarkm}
29699357Smarkm
29799357Smarkm/*
29899357Smarkm * Execute 'kbdcontrol' with the appropriate arguments
29999357Smarkm */
30099357Smarkmstatic void
30199357Smarkmdo_kbdcontrol(struct keymap *km)
30299357Smarkm{
30399357Smarkm	char *kbd_cmd;
30499357Smarkm	asprintf(&kbd_cmd, "kbdcontrol -l %s/%s", dir, km->keym);
30599357Smarkm
30699357Smarkm	if (!x11)
30799357Smarkm		system(kbd_cmd);
30899357Smarkm
309226439Snwhitehorn	fprintf(stderr, "keymap=\"%s\"\n", km->keym);
31099357Smarkm	free(kbd_cmd);
31199357Smarkm}
31299357Smarkm
31399357Smarkm/*
31499357Smarkm * Call 'vidcontrol' with the appropriate arguments
31599357Smarkm */
31699357Smarkmstatic void
31799357Smarkmdo_vidfont(struct keymap *km)
31899357Smarkm{
31999357Smarkm	char *vid_cmd, *tmp, *p, *q;
32099357Smarkm
32199357Smarkm	asprintf(&vid_cmd, "%s/%s", dir, km->keym);
32299357Smarkm	vidcontrol(vid_cmd);
32399357Smarkm	free(vid_cmd);
32499357Smarkm
32599357Smarkm	tmp = strdup(km->keym);
32699357Smarkm	p = strrchr(tmp, '-');
32799357Smarkm	if (p && p[1]!='\0') {
32899357Smarkm		p++;
32999357Smarkm		q = get_extension(p);
33099357Smarkm		if (q) {
33199357Smarkm			*q = '\0';
33299357Smarkm			printf("font%s=%s\n", p, km->keym);
33399357Smarkm		}
33499357Smarkm	}
33599357Smarkm	free(tmp);
33699357Smarkm}
33799357Smarkm
33899357Smarkm/*
33999357Smarkm * Display dialog from 'keymaps[]'
34099357Smarkm */
34199357Smarkmstatic void
34299357Smarkmshow_dialog(struct keymap **km_sorted, int num_keymaps)
34399357Smarkm{
34499357Smarkm	FILE *fp;
34599357Smarkm	char *cmd, *dialog;
34699357Smarkm	char tmp_name[] = "/tmp/_kbd_lang.XXXX";
34799357Smarkm	int fd, i, size;
34899357Smarkm
34999357Smarkm	fd = mkstemp(tmp_name);
35099357Smarkm	if (fd == -1) {
35199357Smarkm		fprintf(stderr, "Could not open temporary file \"%s\"\n",
35299357Smarkm		    tmp_name);
35399357Smarkm		exit(1);
35499357Smarkm	}
35599357Smarkm	asprintf(&dialog, "/usr/bin/dialog --clear --title \"Keyboard Menu\" "
356217359Snwhitehorn			  "--menu \"%s\" 0 0 0", menu);
35799357Smarkm
35899357Smarkm	/* start right font, assume that current font is equal
35999357Smarkm	 * to default font in /etc/rc.conf
36099357Smarkm	 *
36199357Smarkm	 * $font is the font which require the language $lang; e.g.
36299357Smarkm	 * russian *need* a koi8 font
36399357Smarkm	 * $font_current is the current font from /etc/rc.conf
36499357Smarkm	 */
36599357Smarkm	if (font && strcmp(font, font_current))
36699357Smarkm		vidcontrol(font);
36799357Smarkm
36899357Smarkm	/* Build up the command */
36999357Smarkm	size = 0;
37099357Smarkm	for (i=0; i<num_keymaps; i++) {
37199357Smarkm		/*
37299357Smarkm		 * Each 'font' is passed as ' "font" ""', so allow the
37399357Smarkm		 * extra space
37499357Smarkm		 */
37599357Smarkm		size += strlen(km_sorted[i]->desc) + 6;
37699357Smarkm	}
37799357Smarkm
37899357Smarkm	/* Allow the space for '2> tmpfilename' redirection */
37999357Smarkm	size += strlen(tmp_name) + 3;
38099357Smarkm
38199357Smarkm	cmd = (char *) malloc(strlen(dialog) + size + 1);
38299357Smarkm	strcpy(cmd, dialog);
38399357Smarkm
38499357Smarkm	for (i=0; i<num_keymaps; i++) {
38599357Smarkm		strcat(cmd, " \"");
38699357Smarkm		strcat(cmd, km_sorted[i]->desc);
38799357Smarkm		strcat(cmd, "\"");
38899357Smarkm		strcat(cmd, " \"\"");
38999357Smarkm	}
39099357Smarkm
39199357Smarkm	strcat(cmd, " 2>");
39299357Smarkm	strcat(cmd, tmp_name);
39399357Smarkm
39499357Smarkm	/* Show the dialog.. */
39599357Smarkm	system(cmd);
39699357Smarkm
39799357Smarkm	fp = fopen(tmp_name, "r");
39899357Smarkm	if (fp) {
39999357Smarkm		char choice[64];
400167260Skevlo		if (fgets(choice, sizeof(choice), fp) != NULL) {
40199357Smarkm			/* Find key for desc */
40299357Smarkm			for (i=0; i<num_keymaps; i++) {
40399357Smarkm				if (!strcmp(choice, km_sorted[i]->desc)) {
40499357Smarkm					if (!strcmp(program, "kbdmap"))
40599357Smarkm						do_kbdcontrol(km_sorted[i]);
40699357Smarkm					else
40799357Smarkm						do_vidfont(km_sorted[i]);
40899357Smarkm					break;
40999357Smarkm				}
41099357Smarkm			}
41199357Smarkm		} else {
41299357Smarkm			if (font != NULL && strcmp(font, font_current))
41399357Smarkm				/* Cancelled, restore old font */
41499357Smarkm				vidcontrol(font_current);
41599357Smarkm		}
41699357Smarkm		fclose(fp);
41799357Smarkm	} else
41899357Smarkm		fprintf(stderr, "Failed to open temporary file");
41999357Smarkm
42099357Smarkm	/* Tidy up */
42199357Smarkm	remove(tmp_name);
42299357Smarkm	free(cmd);
42399357Smarkm	free(dialog);
42499357Smarkm	close(fd);
42599357Smarkm}
42699357Smarkm
42799357Smarkm/*
42899357Smarkm * Search for 'token' in comma delimited array 'buffer'.
42999357Smarkm * Return true for found, false for not found.
43099357Smarkm */
43199357Smarkmstatic int
43299357Smarkmfind_token(const char *buffer, const char *token)
43399357Smarkm{
43499357Smarkm	char *buffer_tmp, *buffer_copy, *inputstring;
43599357Smarkm	char **ap;
43699357Smarkm	int found;
43799357Smarkm
43899357Smarkm	buffer_copy = strdup(buffer);
43999357Smarkm	buffer_tmp = buffer_copy;
44099357Smarkm	inputstring = buffer_copy;
44199357Smarkm	ap = &buffer_tmp;
44299357Smarkm
44399357Smarkm	found = 0;
44499357Smarkm
44599357Smarkm	while ((*ap = strsep(&inputstring, ",")) != NULL) {
44699357Smarkm		if (strcmp(buffer_tmp, token) == 0) {
44799357Smarkm			found = 1;
44899357Smarkm			break;
44999357Smarkm		}
45099357Smarkm	}
45199357Smarkm
45299357Smarkm	free(buffer_copy);
45399357Smarkm
45499357Smarkm	return found;
45599357Smarkm}
45699357Smarkm
45799357Smarkm/*
45899357Smarkm * Compare function for qsort
45999357Smarkm */
46099357Smarkmstatic int
46199357Smarkmcompare_keymap(const void *a, const void *b)
46299357Smarkm{
46399357Smarkm
46499357Smarkm	/* We've been passed pointers to pointers, so: */
46599357Smarkm	const struct keymap *km1 = *((const struct keymap * const *) a);
46699357Smarkm	const struct keymap *km2 = *((const struct keymap * const *) b);
46799357Smarkm
46899357Smarkm	return strcmp(km1->desc, km2->desc);
46999357Smarkm}
47099357Smarkm
47199357Smarkm/*
47299357Smarkm * Compare function for qsort
47399357Smarkm */
47499357Smarkmstatic int
47599357Smarkmcompare_lang(const void *a, const void *b)
47699357Smarkm{
47799357Smarkm	const char *l1 = *((const char * const *) a);
47899357Smarkm	const char *l2 = *((const char * const *) b);
47999357Smarkm
48099357Smarkm	return strcmp(l1, l2);
48199357Smarkm}
48299357Smarkm
48399357Smarkm/*
48499357Smarkm * Change '8x8' to '8x08' so qsort will put it before eg. '8x14'
48599357Smarkm */
48699357Smarkmstatic void
48799357Smarkmkludge_desc(struct keymap **km_sorted, int num_keymaps)
48899357Smarkm{
48999357Smarkm	int i;
49099357Smarkm
49199357Smarkm	for (i=0; i<num_keymaps; i++) {
49299357Smarkm		char *p;
49399357Smarkm		char *km = km_sorted[i]->desc;
49499357Smarkm		if ((p = strstr(km, "8x8")) != NULL) {
49599357Smarkm			int len;
49699357Smarkm			int j;
49799357Smarkm			int offset;
49899357Smarkm
49999357Smarkm			offset = p - km;
50099357Smarkm
50199357Smarkm			/* Make enough space for the extra '0' */
50299357Smarkm			len = strlen(km);
50399357Smarkm			km = realloc(km, len + 2);
50499357Smarkm
50599357Smarkm			for (j=len; j!=offset+1; j--)
50699357Smarkm				km[j + 1] = km[j];
50799357Smarkm
50899357Smarkm			km[offset+2] = '0';
50999357Smarkm
51099357Smarkm			km_sorted[i]->desc = km;
51199357Smarkm		}
51299357Smarkm	}
51399357Smarkm}
51499357Smarkm
51599357Smarkm/*
51699357Smarkm * Reverse 'kludge_desc()' - change '8x08' back to '8x8'
51799357Smarkm */
51899357Smarkmstatic void
51999357Smarkmunkludge_desc(struct keymap **km_sorted, int num_keymaps)
52099357Smarkm{
52199357Smarkm	int i;
52299357Smarkm
52399357Smarkm	for (i=0; i<num_keymaps; i++) {
52499357Smarkm		char *p;
52599357Smarkm		char *km = km_sorted[i]->desc;
52699357Smarkm		if ((p = strstr(km, "8x08")) != NULL) {
52799357Smarkm			p += 2;
52899357Smarkm			while (*p++)
52999357Smarkm				p[-1] = p[0];
53099357Smarkm
53199357Smarkm			km = realloc(km, p - km - 1);
53299357Smarkm			km_sorted[i]->desc = km;
53399357Smarkm		}
53499357Smarkm	}
53599357Smarkm}
53699357Smarkm
53799357Smarkm/*
53899357Smarkm * Return 0 if file exists and is readable, else -1
53999357Smarkm */
54099357Smarkmstatic int
54199357Smarkmcheck_file(const char *keym)
54299357Smarkm{
54399357Smarkm	int status = 0;
54499357Smarkm
54599357Smarkm	if (access(keym, R_OK) == -1) {
54699357Smarkm		char *fn;
54799357Smarkm		asprintf(&fn, "%s/%s", dir, keym);
54899357Smarkm		if (access(fn, R_OK) == -1) {
54999357Smarkm			if (verbose)
55099357Smarkm				fprintf(stderr, "%s not found!\n", fn);
55199357Smarkm			status = -1;
55299357Smarkm		}
55399357Smarkm		free(fn);
55499357Smarkm	} else {
55599357Smarkm		if (verbose)
55699357Smarkm			fprintf(stderr, "No read permission for %s!\n", keym);
55799357Smarkm		status = -1;
55899357Smarkm	}
55999357Smarkm
56099357Smarkm	return status;
56199357Smarkm}
56299357Smarkm
56399357Smarkm/*
564228990Suqs * Read options from the relevant configuration file, then
56599357Smarkm *  present to user.
56699357Smarkm */
56799357Smarkmstatic void
56899357Smarkmmenu_read(void)
56999357Smarkm{
57099357Smarkm	const char *lg;
57199357Smarkm	char *p;
57299357Smarkm	int mark, num_keymaps, items, i;
57399357Smarkm	char buffer[256], filename[PATH_MAX];
57499357Smarkm	char keym[64], lng[64], desc[64];
57599357Smarkm	char dialect[64], lang_abk[64];
57699357Smarkm	struct keymap *km;
57799357Smarkm	struct keymap **km_sorted;
57899357Smarkm	struct dirent *dp;
57999357Smarkm	StringList *lang_list;
58099357Smarkm	FILE *fp;
58199357Smarkm	DIR *dirp;
58299357Smarkm
58399357Smarkm	lang_list = sl_init();
58499357Smarkm
58599357Smarkm	sprintf(filename, "%s/INDEX.%s", dir, extract_name(dir));
58699357Smarkm
58799357Smarkm	/* en_US.ISO8859-1 -> en_..\.ISO8859-1 */
58899357Smarkm	strlcpy(dialect, lang, sizeof(dialect));
589147685Sru	if (strlen(dialect) >= 6 && dialect[2] == '_') {
59099357Smarkm		dialect[3] = '.';
59199357Smarkm		dialect[4] = '.';
59299357Smarkm	}
59399357Smarkm
59499357Smarkm
59599357Smarkm	/* en_US.ISO8859-1 -> en */
59699357Smarkm	strlcpy(lang_abk, lang, sizeof(lang_abk));
597147685Sru	if (strlen(lang_abk) >= 3 && lang_abk[2] == '_')
598147685Sru		lang_abk[2] = '\0';
59999357Smarkm
60099357Smarkm	fprintf(stderr, "lang_default = %s\n", lang_default);
60199357Smarkm	fprintf(stderr, "dialect = %s\n", dialect);
60299357Smarkm	fprintf(stderr, "lang_abk = %s\n", lang_abk);
60399357Smarkm
60499357Smarkm	fp = fopen(filename, "r");
60599357Smarkm	if (fp) {
60699357Smarkm		int matches;
60799357Smarkm		while (fgets(buffer, sizeof(buffer), fp)) {
60899357Smarkm			p = buffer;
60999357Smarkm			if (p[0] == '#')
61099357Smarkm				continue;
61199357Smarkm
61299357Smarkm			while (isspace(*p))
61399357Smarkm				p++;
61499357Smarkm
61599357Smarkm			if (*p == '\0')
61699357Smarkm				continue;
61799357Smarkm
61899357Smarkm			/* Parse input, removing newline */
61999357Smarkm			matches = sscanf(p, "%64[^:]:%64[^:]:%64[^:\n]",
62099357Smarkm			    keym, lng, desc);
62199357Smarkm			if (matches == 3) {
62299357Smarkm				if (strcmp(keym, "FONT")
62399357Smarkm				    && strcmp(keym, "MENU")) {
62499357Smarkm					/* Check file exists & is readable */
62599357Smarkm					if (check_file(keym) == -1)
62699357Smarkm						continue;
62799357Smarkm				}
62899357Smarkm			}
62999357Smarkm
63099357Smarkm			if (show) {
63199357Smarkm				/*
63299357Smarkm				 * Take note of supported languages, which
63399357Smarkm				 * might be in a comma-delimited list
63499357Smarkm				 */
63599357Smarkm				char *tmp = strdup(lng);
63699357Smarkm				char *delim = tmp;
63799357Smarkm
63899357Smarkm				for (delim = tmp; ; ) {
63999357Smarkm					char ch = *delim++;
64099357Smarkm					if (ch == ',' || ch == '\0') {
64199357Smarkm						delim[-1] = '\0';
64299357Smarkm						if (!sl_find(lang_list, tmp))
64399357Smarkm							sl_add(lang_list, tmp);
64499357Smarkm						if (ch == '\0')
64599357Smarkm							break;
64699357Smarkm						tmp = delim;
64799357Smarkm					}
64899357Smarkm				}
64999357Smarkm			}
65099357Smarkm			/* Set empty language to default language */
65199357Smarkm			if (lng[0] == '\0')
65299357Smarkm				lg = lang_default;
65399357Smarkm			else
65499357Smarkm				lg = lng;
65599357Smarkm
65699357Smarkm
65799357Smarkm			/* 4) Your choice if it exists
65899357Smarkm			 * 3) Long match eg. en_GB.ISO8859-1 is equal to
65999357Smarkm			 *      en_..\.ISO8859-1
66099357Smarkm			 * 2) short match 'de'
66199357Smarkm			 * 1) default langlist 'en'
66299357Smarkm			 * 0) any language
66399357Smarkm			 *
66499357Smarkm			 * Language may be a comma separated list
66599357Smarkm			 * A higher match overwrites a lower
66699357Smarkm			 * A later entry overwrites a previous if it exists
66799357Smarkm			 *     twice in the database
66899357Smarkm			 */
66999357Smarkm
67099357Smarkm			/* Check for favoured language */
67199357Smarkm			km = get_keymap(keym);
67299357Smarkm			mark = (km) ? km->mark : 0;
67399357Smarkm
67499357Smarkm			if (find_token(lg, lang))
67599357Smarkm				add_keymap(desc, 4, keym);
67699357Smarkm			else if (mark <= 3 && find_token(lg, dialect))
67799357Smarkm				add_keymap(desc, 3, keym);
67899357Smarkm			else if (mark <= 2 && find_token(lg, lang_abk))
67999357Smarkm				add_keymap(desc, 2, keym);
68099357Smarkm			else if (mark <= 1 && find_token(lg, lang_default))
68199357Smarkm				add_keymap(desc, 1, keym);
68299357Smarkm			else if (mark <= 0)
68399357Smarkm				add_keymap(desc, 0, keym);
68499357Smarkm		}
68599357Smarkm		fclose(fp);
68699357Smarkm
68799357Smarkm	} else
68899357Smarkm		printf("Could not open file\n");
68999357Smarkm
69099357Smarkm	if (show) {
69199357Smarkm		qsort(lang_list->sl_str, lang_list->sl_cur, sizeof(char*),
69299357Smarkm		    compare_lang);
69399357Smarkm		printf("Currently supported languages: ");
69499357Smarkm		for (i=0; i< (int) lang_list->sl_cur; i++)
69599357Smarkm			printf("%s ", lang_list->sl_str[i]);
69699357Smarkm		puts("");
69799357Smarkm		exit(0);
69899357Smarkm	}
69999357Smarkm
70099357Smarkm	km = get_keymap("MENU");
70199357Smarkm	if (km)
70299357Smarkm		/* Take note of menu title */
70399357Smarkm		menu = strdup(km->desc);
70499357Smarkm	km = get_keymap("FONT");
70599357Smarkm	if (km)
70699357Smarkm		/* Take note of language font */
70799357Smarkm		font = strdup(km->desc);
70899357Smarkm
70999357Smarkm	/* Remove unwanted items from list */
71099357Smarkm	remove_keymap("MENU");
71199357Smarkm	remove_keymap("FONT");
71299357Smarkm
71399357Smarkm	/* Look for keymaps not in database */
71499357Smarkm	dirp = opendir(dir);
71599357Smarkm	if (dirp) {
71699357Smarkm		while ((dp = readdir(dirp)) != NULL) {
71799357Smarkm			const char *ext = get_extension(dp->d_name);
71899357Smarkm			if (ext) {
71999357Smarkm				if ((!strcmp(ext, ".fnt") ||
72099357Smarkm				    !strcmp(ext, ".kbd")) &&
72199357Smarkm				    !get_keymap(dp->d_name)) {
72299357Smarkm					char *q;
72399357Smarkm
72499357Smarkm					/* Remove any .fnt or .kbd extension */
72599357Smarkm					q = strdup(dp->d_name);
72699357Smarkm					*(get_extension(q)) = '\0';
72799357Smarkm					add_keymap(q, 0, dp->d_name);
72899357Smarkm					free(q);
72999357Smarkm
73099357Smarkm					if (verbose)
73199357Smarkm						fprintf(stderr,
73299357Smarkm						    "'%s' not in database\n",
73399357Smarkm						    dp->d_name);
73499357Smarkm				}
73599357Smarkm			}
73699357Smarkm		}
73799357Smarkm		closedir(dirp);
73899357Smarkm	} else
73999357Smarkm		fprintf(stderr, "Could not open directory '%s'\n", dir);
74099357Smarkm
74199357Smarkm	/* Sort items in keymap */
74299357Smarkm	num_keymaps = get_num_keymaps();
74399357Smarkm
74499357Smarkm	km_sorted = (struct keymap **)
74599357Smarkm	    malloc(num_keymaps*sizeof(struct keymap *));
74699357Smarkm
74799357Smarkm	/* Make array of pointers to items in hash */
74899357Smarkm	items = 0;
74999357Smarkm	SLIST_FOREACH(km, &head, entries)
75099357Smarkm		km_sorted[items++] = km;
75199357Smarkm
75299357Smarkm	/* Change '8x8' to '8x08' so sort works as we might expect... */
75399357Smarkm	kludge_desc(km_sorted, num_keymaps);
75499357Smarkm
75599357Smarkm	qsort(km_sorted, num_keymaps, sizeof(struct keymap *), compare_keymap);
75699357Smarkm
75799357Smarkm	/* ...change back again */
75899357Smarkm	unkludge_desc(km_sorted, num_keymaps);
75999357Smarkm
76099357Smarkm	if (print) {
76199357Smarkm		for (i=0; i<num_keymaps; i++)
76299357Smarkm			printf("%s\n", km_sorted[i]->desc);
76399357Smarkm		exit(0);
76499357Smarkm	}
76599357Smarkm
76699357Smarkm	show_dialog(km_sorted, num_keymaps);
76799357Smarkm
76899357Smarkm	free(km_sorted);
76999357Smarkm}
77099357Smarkm
77199357Smarkm/*
77299357Smarkm * Display usage information and exit
77399357Smarkm */
77499357Smarkmstatic void
77599357Smarkmusage(void)
77699357Smarkm{
77799357Smarkm
77899357Smarkm	fprintf(stderr, "usage: %s\t[-K] [-V] [-d|-default] [-h|-help] "
77999357Smarkm	    "[-l|-lang language]\n\t\t[-p|-print] [-r|-restore] [-s|-show] "
78099357Smarkm	    "[-v|-verbose]\n", program);
78199357Smarkm	exit(1);
78299357Smarkm}
78399357Smarkm
78499357Smarkmstatic void
78599357Smarkmparse_args(int argc, char **argv)
78699357Smarkm{
78799357Smarkm	int i;
78899357Smarkm
78999357Smarkm	for (i=1; i<argc; i++) {
79099357Smarkm		if (argv[i][0] != '-')
79199357Smarkm			usage();
79299357Smarkm		else if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h"))
79399357Smarkm			usage();
79499357Smarkm		else if (!strcmp(argv[i], "-verbose") || !strcmp(argv[i], "-v"))
79599357Smarkm			verbose = 1;
79699357Smarkm		else if (!strcmp(argv[i], "-lang") || !strcmp(argv[i], "-l"))
79799357Smarkm			if (i + 1 == argc)
79899357Smarkm				usage();
79999357Smarkm			else
80099357Smarkm				lang = argv[++i];
80199357Smarkm		else if (!strcmp(argv[i], "-default") || !strcmp(argv[i], "-d"))
80299357Smarkm			lang = lang_default;
80399357Smarkm		else if (!strcmp(argv[i], "-show") || !strcmp(argv[i], "-s"))
80499357Smarkm			show = 1;
80599357Smarkm		else if (!strcmp(argv[i], "-print") || !strcmp(argv[i], "-p"))
80699357Smarkm			print = 1;
80799357Smarkm		else if (!strcmp(argv[i], "-restore") ||
80899357Smarkm		    !strcmp(argv[i], "-r")) {
80999357Smarkm			vidcontrol(font_current);
81099357Smarkm			exit(0);
81199357Smarkm		} else if (!strcmp(argv[i], "-K"))
81299357Smarkm			dir = keymapdir;
81399357Smarkm		else if (!strcmp(argv[i], "-V"))
81499357Smarkm			dir = fontdir;
81599357Smarkm		else
81699357Smarkm			usage();
81799357Smarkm	}
81899357Smarkm}
81999357Smarkm
82099357Smarkm/*
82199357Smarkm * A front-end for the 'vidfont' and 'kbdmap' programs.
82299357Smarkm */
82399357Smarkmint
82499357Smarkmmain(int argc, char **argv)
82599357Smarkm{
82699357Smarkm
82799357Smarkm	x11 = system("kbdcontrol -d >/dev/null");
82899357Smarkm
82999357Smarkm	if (x11) {
83099357Smarkm		fprintf(stderr, "You are not on a virtual console - "
83199357Smarkm				"expect certain strange side-effects\n");
83299357Smarkm		sleep(2);
83399357Smarkm	}
83499357Smarkm
835270309Sse	if (check_newcons() == 0) {
836270309Sse		keymapdir = DEFAULT_SC_KEYMAP_DIR;
837270309Sse		fontdir = DEFAULT_SC_FONT_DIR;
838270309Sse		font_default = DEFAULT_SC_FONT;
839270309Sse	}
840270309Sse
84199357Smarkm	SLIST_INIT(&head);
84299357Smarkm
84399357Smarkm	lang = get_locale();
84499357Smarkm
84599357Smarkm	program = extract_name(argv[0]);
84699357Smarkm
84799357Smarkm	font_current = get_font();
84899357Smarkm	if (font_current == NULL)
84999357Smarkm		font_current = font_default;
85099357Smarkm
85199357Smarkm	if (strcmp(program, "kbdmap"))
85299357Smarkm		dir = fontdir;
85399357Smarkm	else
85499357Smarkm		dir = keymapdir;
85599357Smarkm
856154151Sflz	/* Parse command line arguments */
857154151Sflz	parse_args(argc, argv);
858154151Sflz
85999357Smarkm	/* Read and display options */
86099357Smarkm	menu_read();
86199357Smarkm
86299357Smarkm	return 0;
86399357Smarkm}
864