1ece80b1dSMark Murray /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
4ece80b1dSMark Murray * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
5ece80b1dSMark Murray * All rights reserved.
6ece80b1dSMark Murray *
7ece80b1dSMark Murray * Redistribution and use in source and binary forms, with or without
8ece80b1dSMark Murray * modification, are permitted provided that the following conditions
9ece80b1dSMark Murray * are met:
10ece80b1dSMark Murray * 1. Redistributions of source code must retain the above copyright
11ece80b1dSMark Murray * notice, this list of conditions and the following disclaimer.
12ece80b1dSMark Murray * 2. Redistributions in binary form must reproduce the above copyright
13ece80b1dSMark Murray * notice, this list of conditions and the following disclaimer in the
14ece80b1dSMark Murray * documentation and/or other materials provided with the distribution.
15ece80b1dSMark Murray *
16ece80b1dSMark Murray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17ece80b1dSMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18ece80b1dSMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ece80b1dSMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20ece80b1dSMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21ece80b1dSMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22ece80b1dSMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23ece80b1dSMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24ece80b1dSMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25ece80b1dSMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26ece80b1dSMark Murray * SUCH DAMAGE.
27ece80b1dSMark Murray */
28ece80b1dSMark Murray
29ece80b1dSMark Murray #include <sys/types.h>
30ece80b1dSMark Murray #include <sys/queue.h>
316fe6b12fSStefan Eßer #include <sys/sysctl.h>
32ece80b1dSMark Murray
33ece80b1dSMark Murray #include <assert.h>
3424b05d7aSBaptiste Daroussin #include <bsddialog.h>
35ece80b1dSMark Murray #include <ctype.h>
36ece80b1dSMark Murray #include <dirent.h>
37688dfe45SGarrett Wollman #include <limits.h>
38ece80b1dSMark Murray #include <stdio.h>
39ece80b1dSMark Murray #include <stdlib.h>
40ece80b1dSMark Murray #include <string.h>
41ece80b1dSMark Murray #include <stringlist.h>
42ece80b1dSMark Murray #include <unistd.h>
43ece80b1dSMark Murray
44ece80b1dSMark Murray #include "kbdmap.h"
45ece80b1dSMark Murray
46ece80b1dSMark Murray
47ece80b1dSMark Murray static const char *lang_default = DEFAULT_LANG;
48ece80b1dSMark Murray static const char *font;
49ece80b1dSMark Murray static const char *lang;
50ece80b1dSMark Murray static const char *program;
516fe6b12fSStefan Eßer static const char *keymapdir = DEFAULT_VT_KEYMAP_DIR;
526fe6b12fSStefan Eßer static const char *fontdir = DEFAULT_VT_FONT_DIR;
536fe6b12fSStefan Eßer static const char *font_default = DEFAULT_VT_FONT;
54ece80b1dSMark Murray static const char *sysconfig = DEFAULT_SYSCONFIG;
55ece80b1dSMark Murray static const char *font_current;
56ece80b1dSMark Murray static const char *dir;
57ece80b1dSMark Murray static const char *menu = "";
5829f64e28SEd Maste static const char *title = "Keyboard Menu";
59ece80b1dSMark Murray
60ece80b1dSMark Murray static int x11;
611b4e3694SEd Maste static int using_vt;
62ece80b1dSMark Murray static int show;
63ece80b1dSMark Murray static int verbose;
64ece80b1dSMark Murray static int print;
65ece80b1dSMark Murray
66ece80b1dSMark Murray
67ece80b1dSMark Murray struct keymap {
68ece80b1dSMark Murray char *desc;
69ece80b1dSMark Murray char *keym;
70ece80b1dSMark Murray int mark;
71ece80b1dSMark Murray SLIST_ENTRY(keymap) entries;
72ece80b1dSMark Murray };
73ece80b1dSMark Murray static SLIST_HEAD(slisthead, keymap) head = SLIST_HEAD_INITIALIZER(head);
74ece80b1dSMark Murray
75ece80b1dSMark Murray
76ece80b1dSMark Murray /*
77ece80b1dSMark Murray * Get keymap entry for 'key', or NULL of not found
78ece80b1dSMark Murray */
79ece80b1dSMark Murray static struct keymap *
get_keymap(const char * key)80ece80b1dSMark Murray get_keymap(const char *key)
81ece80b1dSMark Murray {
82ece80b1dSMark Murray struct keymap *km;
83ece80b1dSMark Murray
84ece80b1dSMark Murray SLIST_FOREACH(km, &head, entries)
85ece80b1dSMark Murray if (!strcmp(km->keym, key))
86ece80b1dSMark Murray return km;
87ece80b1dSMark Murray
88ece80b1dSMark Murray return NULL;
89ece80b1dSMark Murray }
90ece80b1dSMark Murray
91ece80b1dSMark Murray /*
92ece80b1dSMark Murray * Count the number of keymaps we found
93ece80b1dSMark Murray */
94ece80b1dSMark Murray static int
get_num_keymaps(void)95ece80b1dSMark Murray get_num_keymaps(void)
96ece80b1dSMark Murray {
97ece80b1dSMark Murray struct keymap *km;
98ece80b1dSMark Murray int count = 0;
99ece80b1dSMark Murray
100ece80b1dSMark Murray SLIST_FOREACH(km, &head, entries)
101ece80b1dSMark Murray count++;
102ece80b1dSMark Murray
103ece80b1dSMark Murray return count;
104ece80b1dSMark Murray }
105ece80b1dSMark Murray
106ece80b1dSMark Murray /*
107ece80b1dSMark Murray * Remove any keymap with given keym
108ece80b1dSMark Murray */
109ece80b1dSMark Murray static void
remove_keymap(const char * keym)110ece80b1dSMark Murray remove_keymap(const char *keym)
111ece80b1dSMark Murray {
112ece80b1dSMark Murray struct keymap *km;
113ece80b1dSMark Murray
114ece80b1dSMark Murray SLIST_FOREACH(km, &head, entries) {
115ece80b1dSMark Murray if (!strcmp(keym, km->keym)) {
116ece80b1dSMark Murray SLIST_REMOVE(&head, km, keymap, entries);
117ece80b1dSMark Murray free(km);
118ece80b1dSMark Murray break;
119ece80b1dSMark Murray }
120ece80b1dSMark Murray }
121ece80b1dSMark Murray }
122ece80b1dSMark Murray
123ece80b1dSMark Murray /*
124ece80b1dSMark Murray * Add to hash with 'key'
125ece80b1dSMark Murray */
126ece80b1dSMark Murray static void
add_keymap(const char * desc,int mark,const char * keym)127ece80b1dSMark Murray add_keymap(const char *desc, int mark, const char *keym)
128ece80b1dSMark Murray {
129ece80b1dSMark Murray struct keymap *km, *km_new;
130ece80b1dSMark Murray
131ece80b1dSMark Murray /* Is there already an entry with this key? */
132ece80b1dSMark Murray SLIST_FOREACH(km, &head, entries) {
133ece80b1dSMark Murray if (!strcmp(km->keym, keym)) {
134ece80b1dSMark Murray /* Reuse this entry */
135ece80b1dSMark Murray free(km->desc);
136ece80b1dSMark Murray km->desc = strdup(desc);
137ece80b1dSMark Murray km->mark = mark;
138ece80b1dSMark Murray return;
139ece80b1dSMark Murray }
140ece80b1dSMark Murray }
141ece80b1dSMark Murray
142ece80b1dSMark Murray km_new = (struct keymap *) malloc (sizeof(struct keymap));
143ece80b1dSMark Murray km_new->desc = strdup(desc);
144ece80b1dSMark Murray km_new->keym = strdup(keym);
145ece80b1dSMark Murray km_new->mark = mark;
146ece80b1dSMark Murray
147ece80b1dSMark Murray /* Add to keymap list */
148ece80b1dSMark Murray SLIST_INSERT_HEAD(&head, km_new, entries);
149ece80b1dSMark Murray }
150ece80b1dSMark Murray
151ece80b1dSMark Murray /*
1526fe6b12fSStefan Eßer * Return 0 if syscons is in use (to select legacy defaults).
1536fe6b12fSStefan Eßer */
1546fe6b12fSStefan Eßer static int
check_vt(void)1551b4e3694SEd Maste check_vt(void)
1566fe6b12fSStefan Eßer {
1576fe6b12fSStefan Eßer size_t len;
1586fe6b12fSStefan Eßer char term[3];
1596fe6b12fSStefan Eßer
1606fe6b12fSStefan Eßer len = 3;
1616fe6b12fSStefan Eßer if (sysctlbyname("kern.vty", &term, &len, NULL, 0) != 0 ||
1626fe6b12fSStefan Eßer strcmp(term, "vt") != 0)
1636fe6b12fSStefan Eßer return 0;
1641b4e3694SEd Maste return 1;
1656fe6b12fSStefan Eßer }
1666fe6b12fSStefan Eßer
1676fe6b12fSStefan Eßer /*
168ece80b1dSMark Murray * Figure out the default language to use.
169ece80b1dSMark Murray */
170ece80b1dSMark Murray static const char *
get_locale(void)171ece80b1dSMark Murray get_locale(void)
172ece80b1dSMark Murray {
173ece80b1dSMark Murray const char *locale;
174ece80b1dSMark Murray
175ece80b1dSMark Murray if ((locale = getenv("LC_ALL")) == NULL &&
176ece80b1dSMark Murray (locale = getenv("LC_CTYPE")) == NULL &&
177ece80b1dSMark Murray (locale = getenv("LANG")) == NULL)
178ece80b1dSMark Murray locale = lang_default;
179ece80b1dSMark Murray
180ece80b1dSMark Murray /* Check for alias */
181ece80b1dSMark Murray if (!strcmp(locale, "C"))
182ece80b1dSMark Murray locale = DEFAULT_LANG;
183ece80b1dSMark Murray
184ece80b1dSMark Murray return locale;
185ece80b1dSMark Murray }
186ece80b1dSMark Murray
187ece80b1dSMark Murray /*
188ece80b1dSMark Murray * Extract filename part
189ece80b1dSMark Murray */
190ece80b1dSMark Murray static const char *
extract_name(const char * name)191ece80b1dSMark Murray extract_name(const char *name)
192ece80b1dSMark Murray {
193ece80b1dSMark Murray char *p;
194ece80b1dSMark Murray
195ece80b1dSMark Murray p = strrchr(name, '/');
196ece80b1dSMark Murray if (p != NULL && p[1] != '\0')
197ece80b1dSMark Murray return p + 1;
198ece80b1dSMark Murray
199ece80b1dSMark Murray return name;
200ece80b1dSMark Murray }
201ece80b1dSMark Murray
202ece80b1dSMark Murray /*
203ece80b1dSMark Murray * Return file extension or NULL
204ece80b1dSMark Murray */
205ece80b1dSMark Murray static char *
get_extension(const char * name)206ece80b1dSMark Murray get_extension(const char *name)
207ece80b1dSMark Murray {
208ece80b1dSMark Murray char *p;
209ece80b1dSMark Murray
210ece80b1dSMark Murray p = strrchr(name, '.');
211ece80b1dSMark Murray
212ece80b1dSMark Murray if (p != NULL && p[1] != '\0')
213ece80b1dSMark Murray return p;
214ece80b1dSMark Murray
215ece80b1dSMark Murray return NULL;
216ece80b1dSMark Murray }
217ece80b1dSMark Murray
218ece80b1dSMark Murray /*
219ece80b1dSMark Murray * Read font from /etc/rc.conf else return default.
220ece80b1dSMark Murray * Freeing the memory is the caller's responsibility.
221ece80b1dSMark Murray */
222ece80b1dSMark Murray static char *
get_font(void)223ece80b1dSMark Murray get_font(void)
224ece80b1dSMark Murray {
225e17fede8SDimitry Andric char line[256], buf[21];
226ece80b1dSMark Murray char *fnt = NULL;
227ece80b1dSMark Murray
228ece80b1dSMark Murray FILE *fp = fopen(sysconfig, "r");
229ece80b1dSMark Murray if (fp) {
230ece80b1dSMark Murray while (fgets(line, sizeof(line), fp)) {
231ece80b1dSMark Murray int a, b, matches;
232ece80b1dSMark Murray
233ece80b1dSMark Murray if (line[0] == '#')
234ece80b1dSMark Murray continue;
235ece80b1dSMark Murray
236ece80b1dSMark Murray matches = sscanf(line,
237ece80b1dSMark Murray " font%dx%d = \"%20[-.0-9a-zA-Z_]",
238ece80b1dSMark Murray &a, &b, buf);
239ece80b1dSMark Murray if (matches==3) {
240ece80b1dSMark Murray if (strcmp(buf, "NO")) {
241ece80b1dSMark Murray if (fnt)
242ece80b1dSMark Murray free(fnt);
2435f81babdSStefan Eßer fnt = strdup(buf);
244ece80b1dSMark Murray }
245ece80b1dSMark Murray }
246ece80b1dSMark Murray }
247b2ce2d69SJaakko Heinonen fclose(fp);
248ece80b1dSMark Murray } else
249ece80b1dSMark Murray fprintf(stderr, "Could not open %s for reading\n", sysconfig);
250ece80b1dSMark Murray
251ece80b1dSMark Murray return fnt;
252ece80b1dSMark Murray }
253ece80b1dSMark Murray
254ece80b1dSMark Murray /*
255ece80b1dSMark Murray * Set a font using 'vidcontrol'
256ece80b1dSMark Murray */
257ece80b1dSMark Murray static void
vidcontrol(const char * fnt)258ece80b1dSMark Murray vidcontrol(const char *fnt)
259ece80b1dSMark Murray {
2601b4e3694SEd Maste char *tmp, *p, *q, *cmd;
261ece80b1dSMark Murray char ch;
262ece80b1dSMark Murray int i;
263ece80b1dSMark Murray
264ece80b1dSMark Murray /* syscons test failed */
265ece80b1dSMark Murray if (x11)
266ece80b1dSMark Murray return;
267ece80b1dSMark Murray
2681b4e3694SEd Maste if (using_vt) {
2691b4e3694SEd Maste asprintf(&cmd, "vidcontrol -f %s", fnt);
2701b4e3694SEd Maste system(cmd);
2711b4e3694SEd Maste free(cmd);
2721b4e3694SEd Maste return;
2731b4e3694SEd Maste }
2741b4e3694SEd Maste
275ece80b1dSMark Murray tmp = strdup(fnt);
276ece80b1dSMark Murray
277ece80b1dSMark Murray /* Extract font size */
278ece80b1dSMark Murray p = strrchr(tmp, '-');
279ece80b1dSMark Murray if (p && p[1] != '\0') {
280ece80b1dSMark Murray p++;
281ece80b1dSMark Murray /* Remove any '.fnt' extension */
282ece80b1dSMark Murray if ((q = strstr(p, ".fnt")))
283ece80b1dSMark Murray *q = '\0';
284ece80b1dSMark Murray
285ece80b1dSMark Murray /*
286ece80b1dSMark Murray * Check font size is valid, with no trailing characters
287ece80b1dSMark Murray * ('&ch' should not be matched)
288ece80b1dSMark Murray */
289ece80b1dSMark Murray if (sscanf(p, "%dx%d%c", &i, &i, &ch) != 2)
290ece80b1dSMark Murray fprintf(stderr, "Which font size? %s\n", fnt);
291ece80b1dSMark Murray else {
292ece80b1dSMark Murray asprintf(&cmd, "vidcontrol -f %s %s", p, fnt);
293ece80b1dSMark Murray if (verbose)
294ece80b1dSMark Murray fprintf(stderr, "%s\n", cmd);
295ece80b1dSMark Murray system(cmd);
296ece80b1dSMark Murray free(cmd);
297ece80b1dSMark Murray }
298ece80b1dSMark Murray } else
299ece80b1dSMark Murray fprintf(stderr, "Which font size? %s\n", fnt);
300ece80b1dSMark Murray
301ece80b1dSMark Murray free(tmp);
302ece80b1dSMark Murray }
303ece80b1dSMark Murray
304ece80b1dSMark Murray /*
305ece80b1dSMark Murray * Execute 'kbdcontrol' with the appropriate arguments
306ece80b1dSMark Murray */
307ece80b1dSMark Murray static void
do_kbdcontrol(struct keymap * km)308ece80b1dSMark Murray do_kbdcontrol(struct keymap *km)
309ece80b1dSMark Murray {
310ece80b1dSMark Murray char *kbd_cmd;
311ece80b1dSMark Murray asprintf(&kbd_cmd, "kbdcontrol -l %s/%s", dir, km->keym);
312ece80b1dSMark Murray
313ece80b1dSMark Murray if (!x11)
314ece80b1dSMark Murray system(kbd_cmd);
315ece80b1dSMark Murray
3165d920ef7SNathan Whitehorn fprintf(stderr, "keymap=\"%s\"\n", km->keym);
317ece80b1dSMark Murray free(kbd_cmd);
318ece80b1dSMark Murray }
319ece80b1dSMark Murray
320ece80b1dSMark Murray /*
321ece80b1dSMark Murray * Call 'vidcontrol' with the appropriate arguments
322ece80b1dSMark Murray */
323ece80b1dSMark Murray static void
do_vidfont(struct keymap * km)324ece80b1dSMark Murray do_vidfont(struct keymap *km)
325ece80b1dSMark Murray {
326ece80b1dSMark Murray char *vid_cmd, *tmp, *p, *q;
327ece80b1dSMark Murray
328ece80b1dSMark Murray asprintf(&vid_cmd, "%s/%s", dir, km->keym);
329ece80b1dSMark Murray vidcontrol(vid_cmd);
330ece80b1dSMark Murray free(vid_cmd);
331ece80b1dSMark Murray
332ece80b1dSMark Murray tmp = strdup(km->keym);
333ece80b1dSMark Murray p = strrchr(tmp, '-');
334ece80b1dSMark Murray if (p && p[1]!='\0') {
335ece80b1dSMark Murray p++;
336ece80b1dSMark Murray q = get_extension(p);
337ece80b1dSMark Murray if (q) {
338ece80b1dSMark Murray *q = '\0';
339ece80b1dSMark Murray printf("font%s=%s\n", p, km->keym);
340ece80b1dSMark Murray }
341ece80b1dSMark Murray }
342ece80b1dSMark Murray free(tmp);
343ece80b1dSMark Murray }
344ece80b1dSMark Murray
345ece80b1dSMark Murray /*
346ece80b1dSMark Murray * Display dialog from 'keymaps[]'
347ece80b1dSMark Murray */
348ece80b1dSMark Murray static void
show_dialog(struct keymap ** km_sorted,int num_keymaps)349ece80b1dSMark Murray show_dialog(struct keymap **km_sorted, int num_keymaps)
350ece80b1dSMark Murray {
35124b05d7aSBaptiste Daroussin struct bsddialog_conf conf;
35224b05d7aSBaptiste Daroussin struct bsddialog_menuitem *listitems;
35324b05d7aSBaptiste Daroussin int i, result;
354ece80b1dSMark Murray
355263660c0SAlfonso Siciliano if (bsddialog_init() == BSDDIALOG_ERROR) {
356263660c0SAlfonso Siciliano fprintf(stderr, "Error bsddialog: %s\n", bsddialog_geterror());
357ece80b1dSMark Murray exit(1);
358ece80b1dSMark Murray }
35924b05d7aSBaptiste Daroussin conf.title = __DECONST(char *, title);
36024b05d7aSBaptiste Daroussin
36124b05d7aSBaptiste Daroussin listitems = calloc(num_keymaps + 1, sizeof(struct bsddialog_menuitem));
36224b05d7aSBaptiste Daroussin if (listitems == NULL) {
36324b05d7aSBaptiste Daroussin fprintf(stderr, "Failed to allocate memory in show_dialog");
364263660c0SAlfonso Siciliano bsddialog_end();
36524b05d7aSBaptiste Daroussin exit(1);
36624b05d7aSBaptiste Daroussin }
367ece80b1dSMark Murray
368ece80b1dSMark Murray /* start right font, assume that current font is equal
369ece80b1dSMark Murray * to default font in /etc/rc.conf
370ece80b1dSMark Murray *
371ece80b1dSMark Murray * $font is the font which require the language $lang; e.g.
372ece80b1dSMark Murray * russian *need* a koi8 font
373ece80b1dSMark Murray * $font_current is the current font from /etc/rc.conf
374ece80b1dSMark Murray */
375ece80b1dSMark Murray if (font && strcmp(font, font_current))
376ece80b1dSMark Murray vidcontrol(font);
377ece80b1dSMark Murray
37824b05d7aSBaptiste Daroussin /* Build up the menu */
379ece80b1dSMark Murray for (i=0; i<num_keymaps; i++) {
380263660c0SAlfonso Siciliano listitems[i].prefix = "";
38124b05d7aSBaptiste Daroussin listitems[i].depth = 0;
382263660c0SAlfonso Siciliano listitems[i].bottomdesc = "";
38324b05d7aSBaptiste Daroussin listitems[i].on = false;
38424b05d7aSBaptiste Daroussin listitems[i].name = km_sorted[i]->desc;
385263660c0SAlfonso Siciliano listitems[i].desc = "";
386ece80b1dSMark Murray }
387263660c0SAlfonso Siciliano bsddialog_initconf(&conf);
388263660c0SAlfonso Siciliano conf.title = title;
389263660c0SAlfonso Siciliano conf.clear = true;
390263660c0SAlfonso Siciliano conf.key.enable_esc = true;
391263660c0SAlfonso Siciliano result = bsddialog_menu(&conf, menu, 0, 0, 0, num_keymaps, listitems,
392263660c0SAlfonso Siciliano NULL);
393263660c0SAlfonso Siciliano if (result == BSDDIALOG_ERROR)
394263660c0SAlfonso Siciliano fprintf(stderr, "Error bsddialog: %s\n", bsddialog_geterror());
39524b05d7aSBaptiste Daroussin bsddialog_end();
396263660c0SAlfonso Siciliano
39724b05d7aSBaptiste Daroussin switch (result) {
3981de4665dSAlfonso Siciliano case BSDDIALOG_OK:
399ece80b1dSMark Murray for (i = 0; i < num_keymaps; i++) {
40024b05d7aSBaptiste Daroussin if (listitems[i].on) {
4010f4e8037SBaptiste Daroussin if (!strcmp(program, "kbdmap"))
402ece80b1dSMark Murray do_kbdcontrol(km_sorted[i]);
403ece80b1dSMark Murray else
404ece80b1dSMark Murray do_vidfont(km_sorted[i]);
405ece80b1dSMark Murray break;
406ece80b1dSMark Murray }
407ece80b1dSMark Murray }
40824b05d7aSBaptiste Daroussin break;
40924b05d7aSBaptiste Daroussin default:
410ece80b1dSMark Murray if (font != NULL && strcmp(font, font_current))
411ece80b1dSMark Murray /* Cancelled, restore old font */
412ece80b1dSMark Murray vidcontrol(font_current);
41324b05d7aSBaptiste Daroussin break;
414ece80b1dSMark Murray }
415ece80b1dSMark Murray }
416ece80b1dSMark Murray
417ece80b1dSMark Murray /*
418ece80b1dSMark Murray * Search for 'token' in comma delimited array 'buffer'.
419ece80b1dSMark Murray * Return true for found, false for not found.
420ece80b1dSMark Murray */
421ece80b1dSMark Murray static int
find_token(const char * buffer,const char * token)422ece80b1dSMark Murray find_token(const char *buffer, const char *token)
423ece80b1dSMark Murray {
424ece80b1dSMark Murray char *buffer_tmp, *buffer_copy, *inputstring;
425ece80b1dSMark Murray char **ap;
426ece80b1dSMark Murray int found;
427ece80b1dSMark Murray
428ece80b1dSMark Murray buffer_copy = strdup(buffer);
429ece80b1dSMark Murray buffer_tmp = buffer_copy;
430ece80b1dSMark Murray inputstring = buffer_copy;
431ece80b1dSMark Murray ap = &buffer_tmp;
432ece80b1dSMark Murray
433ece80b1dSMark Murray found = 0;
434ece80b1dSMark Murray
435ece80b1dSMark Murray while ((*ap = strsep(&inputstring, ",")) != NULL) {
436ece80b1dSMark Murray if (strcmp(buffer_tmp, token) == 0) {
437ece80b1dSMark Murray found = 1;
438ece80b1dSMark Murray break;
439ece80b1dSMark Murray }
440ece80b1dSMark Murray }
441ece80b1dSMark Murray
442ece80b1dSMark Murray free(buffer_copy);
443ece80b1dSMark Murray
444ece80b1dSMark Murray return found;
445ece80b1dSMark Murray }
446ece80b1dSMark Murray
447ece80b1dSMark Murray /*
448ece80b1dSMark Murray * Compare function for qsort
449ece80b1dSMark Murray */
450ece80b1dSMark Murray static int
compare_keymap(const void * a,const void * b)451ece80b1dSMark Murray compare_keymap(const void *a, const void *b)
452ece80b1dSMark Murray {
453ece80b1dSMark Murray
454ece80b1dSMark Murray /* We've been passed pointers to pointers, so: */
455ece80b1dSMark Murray const struct keymap *km1 = *((const struct keymap * const *) a);
456ece80b1dSMark Murray const struct keymap *km2 = *((const struct keymap * const *) b);
457ece80b1dSMark Murray
458ece80b1dSMark Murray return strcmp(km1->desc, km2->desc);
459ece80b1dSMark Murray }
460ece80b1dSMark Murray
461ece80b1dSMark Murray /*
462ece80b1dSMark Murray * Compare function for qsort
463ece80b1dSMark Murray */
464ece80b1dSMark Murray static int
compare_lang(const void * a,const void * b)465ece80b1dSMark Murray compare_lang(const void *a, const void *b)
466ece80b1dSMark Murray {
467ece80b1dSMark Murray const char *l1 = *((const char * const *) a);
468ece80b1dSMark Murray const char *l2 = *((const char * const *) b);
469ece80b1dSMark Murray
470ece80b1dSMark Murray return strcmp(l1, l2);
471ece80b1dSMark Murray }
472ece80b1dSMark Murray
473ece80b1dSMark Murray /*
474ece80b1dSMark Murray * Change '8x8' to '8x08' so qsort will put it before eg. '8x14'
475ece80b1dSMark Murray */
476ece80b1dSMark Murray static void
kludge_desc(struct keymap ** km_sorted,int num_keymaps)477ece80b1dSMark Murray kludge_desc(struct keymap **km_sorted, int num_keymaps)
478ece80b1dSMark Murray {
479ece80b1dSMark Murray int i;
480ece80b1dSMark Murray
481ece80b1dSMark Murray for (i=0; i<num_keymaps; i++) {
482ece80b1dSMark Murray char *p;
483ece80b1dSMark Murray char *km = km_sorted[i]->desc;
484ece80b1dSMark Murray if ((p = strstr(km, "8x8")) != NULL) {
485ece80b1dSMark Murray int len;
486ece80b1dSMark Murray int j;
487ece80b1dSMark Murray int offset;
488ece80b1dSMark Murray
489ece80b1dSMark Murray offset = p - km;
490ece80b1dSMark Murray
491ece80b1dSMark Murray /* Make enough space for the extra '0' */
492ece80b1dSMark Murray len = strlen(km);
493ece80b1dSMark Murray km = realloc(km, len + 2);
494ece80b1dSMark Murray
495ece80b1dSMark Murray for (j=len; j!=offset+1; j--)
496ece80b1dSMark Murray km[j + 1] = km[j];
497ece80b1dSMark Murray
498ece80b1dSMark Murray km[offset+2] = '0';
499ece80b1dSMark Murray
500ece80b1dSMark Murray km_sorted[i]->desc = km;
501ece80b1dSMark Murray }
502ece80b1dSMark Murray }
503ece80b1dSMark Murray }
504ece80b1dSMark Murray
505ece80b1dSMark Murray /*
506ece80b1dSMark Murray * Reverse 'kludge_desc()' - change '8x08' back to '8x8'
507ece80b1dSMark Murray */
508ece80b1dSMark Murray static void
unkludge_desc(struct keymap ** km_sorted,int num_keymaps)509ece80b1dSMark Murray unkludge_desc(struct keymap **km_sorted, int num_keymaps)
510ece80b1dSMark Murray {
511ece80b1dSMark Murray int i;
512ece80b1dSMark Murray
513ece80b1dSMark Murray for (i=0; i<num_keymaps; i++) {
514ece80b1dSMark Murray char *p;
515ece80b1dSMark Murray char *km = km_sorted[i]->desc;
516ece80b1dSMark Murray if ((p = strstr(km, "8x08")) != NULL) {
517ece80b1dSMark Murray p += 2;
518ece80b1dSMark Murray while (*p++)
519ece80b1dSMark Murray p[-1] = p[0];
520ece80b1dSMark Murray
521ece80b1dSMark Murray km = realloc(km, p - km - 1);
522ece80b1dSMark Murray km_sorted[i]->desc = km;
523ece80b1dSMark Murray }
524ece80b1dSMark Murray }
525ece80b1dSMark Murray }
526ece80b1dSMark Murray
527ece80b1dSMark Murray /*
528ece80b1dSMark Murray * Return 0 if file exists and is readable, else -1
529ece80b1dSMark Murray */
530ece80b1dSMark Murray static int
check_file(const char * keym)531ece80b1dSMark Murray check_file(const char *keym)
532ece80b1dSMark Murray {
533ece80b1dSMark Murray int status = 0;
534ece80b1dSMark Murray
535ece80b1dSMark Murray if (access(keym, R_OK) == -1) {
536ece80b1dSMark Murray char *fn;
537ece80b1dSMark Murray asprintf(&fn, "%s/%s", dir, keym);
538ece80b1dSMark Murray if (access(fn, R_OK) == -1) {
539ece80b1dSMark Murray if (verbose)
540ece80b1dSMark Murray fprintf(stderr, "%s not found!\n", fn);
541ece80b1dSMark Murray status = -1;
542ece80b1dSMark Murray }
543ece80b1dSMark Murray free(fn);
544ece80b1dSMark Murray } else {
545ece80b1dSMark Murray if (verbose)
546ece80b1dSMark Murray fprintf(stderr, "No read permission for %s!\n", keym);
547ece80b1dSMark Murray status = -1;
548ece80b1dSMark Murray }
549ece80b1dSMark Murray
550ece80b1dSMark Murray return status;
551ece80b1dSMark Murray }
552ece80b1dSMark Murray
553ece80b1dSMark Murray /*
5543df5ecacSUlrich Spörlein * Read options from the relevant configuration file, then
555ece80b1dSMark Murray * present to user.
556ece80b1dSMark Murray */
557ece80b1dSMark Murray static void
menu_read(void)558ece80b1dSMark Murray menu_read(void)
559ece80b1dSMark Murray {
560ece80b1dSMark Murray const char *lg;
561ece80b1dSMark Murray char *p;
562ece80b1dSMark Murray int mark, num_keymaps, items, i;
563ece80b1dSMark Murray char buffer[256], filename[PATH_MAX];
564e17fede8SDimitry Andric char keym[65], lng[65], desc[257];
565ece80b1dSMark Murray char dialect[64], lang_abk[64];
566ece80b1dSMark Murray struct keymap *km;
567ece80b1dSMark Murray struct keymap **km_sorted;
568ece80b1dSMark Murray struct dirent *dp;
569ece80b1dSMark Murray StringList *lang_list;
570ece80b1dSMark Murray FILE *fp;
571ece80b1dSMark Murray DIR *dirp;
572ece80b1dSMark Murray
573ece80b1dSMark Murray lang_list = sl_init();
574ece80b1dSMark Murray
575ece80b1dSMark Murray sprintf(filename, "%s/INDEX.%s", dir, extract_name(dir));
576ece80b1dSMark Murray
577ece80b1dSMark Murray /* en_US.ISO8859-1 -> en_..\.ISO8859-1 */
578ece80b1dSMark Murray strlcpy(dialect, lang, sizeof(dialect));
57976c61349SRuslan Ermilov if (strlen(dialect) >= 6 && dialect[2] == '_') {
580ece80b1dSMark Murray dialect[3] = '.';
581ece80b1dSMark Murray dialect[4] = '.';
582ece80b1dSMark Murray }
583ece80b1dSMark Murray
584ece80b1dSMark Murray
585ece80b1dSMark Murray /* en_US.ISO8859-1 -> en */
586ece80b1dSMark Murray strlcpy(lang_abk, lang, sizeof(lang_abk));
58776c61349SRuslan Ermilov if (strlen(lang_abk) >= 3 && lang_abk[2] == '_')
58876c61349SRuslan Ermilov lang_abk[2] = '\0';
589ece80b1dSMark Murray
590ece80b1dSMark Murray fprintf(stderr, "lang_default = %s\n", lang_default);
591ece80b1dSMark Murray fprintf(stderr, "dialect = %s\n", dialect);
592ece80b1dSMark Murray fprintf(stderr, "lang_abk = %s\n", lang_abk);
593ece80b1dSMark Murray
594ece80b1dSMark Murray fp = fopen(filename, "r");
595ece80b1dSMark Murray if (fp) {
596ece80b1dSMark Murray int matches;
597ece80b1dSMark Murray while (fgets(buffer, sizeof(buffer), fp)) {
598ece80b1dSMark Murray p = buffer;
599ece80b1dSMark Murray if (p[0] == '#')
600ece80b1dSMark Murray continue;
601ece80b1dSMark Murray
602ece80b1dSMark Murray while (isspace(*p))
603ece80b1dSMark Murray p++;
604ece80b1dSMark Murray
605ece80b1dSMark Murray if (*p == '\0')
606ece80b1dSMark Murray continue;
607ece80b1dSMark Murray
608ece80b1dSMark Murray /* Parse input, removing newline */
60992ee8e8fSEd Maste matches = sscanf(p, "%64[^:]:%64[^:]:%256[^:\n]",
610ece80b1dSMark Murray keym, lng, desc);
611ece80b1dSMark Murray if (matches == 3) {
61229f64e28SEd Maste if (strcmp(keym, "FONT") != 0 &&
61329f64e28SEd Maste strcmp(keym, "MENU") != 0 &&
61429f64e28SEd Maste strcmp(keym, "TITLE") != 0) {
615ece80b1dSMark Murray /* Check file exists & is readable */
616ece80b1dSMark Murray if (check_file(keym) == -1)
617ece80b1dSMark Murray continue;
618ece80b1dSMark Murray }
619ece80b1dSMark Murray }
620ece80b1dSMark Murray
621ece80b1dSMark Murray if (show) {
622ece80b1dSMark Murray /*
623ece80b1dSMark Murray * Take note of supported languages, which
624ece80b1dSMark Murray * might be in a comma-delimited list
625ece80b1dSMark Murray */
626ece80b1dSMark Murray char *tmp = strdup(lng);
627ece80b1dSMark Murray char *delim = tmp;
628ece80b1dSMark Murray
629ece80b1dSMark Murray for (delim = tmp; ; ) {
630ece80b1dSMark Murray char ch = *delim++;
631ece80b1dSMark Murray if (ch == ',' || ch == '\0') {
632ece80b1dSMark Murray delim[-1] = '\0';
633ece80b1dSMark Murray if (!sl_find(lang_list, tmp))
634ece80b1dSMark Murray sl_add(lang_list, tmp);
635ece80b1dSMark Murray if (ch == '\0')
636ece80b1dSMark Murray break;
637ece80b1dSMark Murray tmp = delim;
638ece80b1dSMark Murray }
639ece80b1dSMark Murray }
640ece80b1dSMark Murray }
641ece80b1dSMark Murray /* Set empty language to default language */
642ece80b1dSMark Murray if (lng[0] == '\0')
643ece80b1dSMark Murray lg = lang_default;
644ece80b1dSMark Murray else
645ece80b1dSMark Murray lg = lng;
646ece80b1dSMark Murray
647ece80b1dSMark Murray
648ece80b1dSMark Murray /* 4) Your choice if it exists
649ece80b1dSMark Murray * 3) Long match eg. en_GB.ISO8859-1 is equal to
650ece80b1dSMark Murray * en_..\.ISO8859-1
651ece80b1dSMark Murray * 2) short match 'de'
652ece80b1dSMark Murray * 1) default langlist 'en'
653ece80b1dSMark Murray * 0) any language
654ece80b1dSMark Murray *
655ece80b1dSMark Murray * Language may be a comma separated list
656ece80b1dSMark Murray * A higher match overwrites a lower
657ece80b1dSMark Murray * A later entry overwrites a previous if it exists
658ece80b1dSMark Murray * twice in the database
659ece80b1dSMark Murray */
660ece80b1dSMark Murray
661ece80b1dSMark Murray /* Check for favoured language */
662ece80b1dSMark Murray km = get_keymap(keym);
663ece80b1dSMark Murray mark = (km) ? km->mark : 0;
664ece80b1dSMark Murray
665ece80b1dSMark Murray if (find_token(lg, lang))
666ece80b1dSMark Murray add_keymap(desc, 4, keym);
667ece80b1dSMark Murray else if (mark <= 3 && find_token(lg, dialect))
668ece80b1dSMark Murray add_keymap(desc, 3, keym);
669ece80b1dSMark Murray else if (mark <= 2 && find_token(lg, lang_abk))
670ece80b1dSMark Murray add_keymap(desc, 2, keym);
671ece80b1dSMark Murray else if (mark <= 1 && find_token(lg, lang_default))
672ece80b1dSMark Murray add_keymap(desc, 1, keym);
673ece80b1dSMark Murray else if (mark <= 0)
674ece80b1dSMark Murray add_keymap(desc, 0, keym);
675ece80b1dSMark Murray }
676ece80b1dSMark Murray fclose(fp);
677ece80b1dSMark Murray
678ece80b1dSMark Murray } else
679632ddb87SEd Maste fprintf(stderr, "Could not open %s for reading\n", filename);
680ece80b1dSMark Murray
681ece80b1dSMark Murray if (show) {
682ece80b1dSMark Murray qsort(lang_list->sl_str, lang_list->sl_cur, sizeof(char*),
683ece80b1dSMark Murray compare_lang);
684ece80b1dSMark Murray printf("Currently supported languages: ");
685ece80b1dSMark Murray for (i=0; i< (int) lang_list->sl_cur; i++)
686ece80b1dSMark Murray printf("%s ", lang_list->sl_str[i]);
687ece80b1dSMark Murray puts("");
688ece80b1dSMark Murray exit(0);
689ece80b1dSMark Murray }
690ece80b1dSMark Murray
69129f64e28SEd Maste km = get_keymap("TITLE");
69229f64e28SEd Maste if (km)
69329f64e28SEd Maste /* Take note of dialog title */
69429f64e28SEd Maste title = strdup(km->desc);
695ece80b1dSMark Murray km = get_keymap("MENU");
696ece80b1dSMark Murray if (km)
697ece80b1dSMark Murray /* Take note of menu title */
698ece80b1dSMark Murray menu = strdup(km->desc);
699ece80b1dSMark Murray km = get_keymap("FONT");
700ece80b1dSMark Murray if (km)
701ece80b1dSMark Murray /* Take note of language font */
702ece80b1dSMark Murray font = strdup(km->desc);
703ece80b1dSMark Murray
704ece80b1dSMark Murray /* Remove unwanted items from list */
705ece80b1dSMark Murray remove_keymap("FONT");
70629f64e28SEd Maste remove_keymap("MENU");
70729f64e28SEd Maste remove_keymap("TITLE");
708ece80b1dSMark Murray
709ece80b1dSMark Murray /* Look for keymaps not in database */
710ece80b1dSMark Murray dirp = opendir(dir);
711ece80b1dSMark Murray if (dirp) {
712ece80b1dSMark Murray while ((dp = readdir(dirp)) != NULL) {
713ece80b1dSMark Murray const char *ext = get_extension(dp->d_name);
714ece80b1dSMark Murray if (ext) {
715ece80b1dSMark Murray if ((!strcmp(ext, ".fnt") ||
716ece80b1dSMark Murray !strcmp(ext, ".kbd")) &&
717ece80b1dSMark Murray !get_keymap(dp->d_name)) {
718ece80b1dSMark Murray char *q;
719ece80b1dSMark Murray
720ece80b1dSMark Murray /* Remove any .fnt or .kbd extension */
721ece80b1dSMark Murray q = strdup(dp->d_name);
722ece80b1dSMark Murray *(get_extension(q)) = '\0';
723ece80b1dSMark Murray add_keymap(q, 0, dp->d_name);
724ece80b1dSMark Murray free(q);
725ece80b1dSMark Murray
726ece80b1dSMark Murray if (verbose)
727ece80b1dSMark Murray fprintf(stderr,
728ece80b1dSMark Murray "'%s' not in database\n",
729ece80b1dSMark Murray dp->d_name);
730ece80b1dSMark Murray }
731ece80b1dSMark Murray }
732ece80b1dSMark Murray }
733ece80b1dSMark Murray closedir(dirp);
734ece80b1dSMark Murray } else
735ece80b1dSMark Murray fprintf(stderr, "Could not open directory '%s'\n", dir);
736ece80b1dSMark Murray
737ece80b1dSMark Murray /* Sort items in keymap */
738ece80b1dSMark Murray num_keymaps = get_num_keymaps();
739ece80b1dSMark Murray
740ece80b1dSMark Murray km_sorted = (struct keymap **)
741ece80b1dSMark Murray malloc(num_keymaps*sizeof(struct keymap *));
742ece80b1dSMark Murray
743ece80b1dSMark Murray /* Make array of pointers to items in hash */
744ece80b1dSMark Murray items = 0;
745ece80b1dSMark Murray SLIST_FOREACH(km, &head, entries)
746ece80b1dSMark Murray km_sorted[items++] = km;
747ece80b1dSMark Murray
748ece80b1dSMark Murray /* Change '8x8' to '8x08' so sort works as we might expect... */
749ece80b1dSMark Murray kludge_desc(km_sorted, num_keymaps);
750ece80b1dSMark Murray
751ece80b1dSMark Murray qsort(km_sorted, num_keymaps, sizeof(struct keymap *), compare_keymap);
752ece80b1dSMark Murray
753ece80b1dSMark Murray /* ...change back again */
754ece80b1dSMark Murray unkludge_desc(km_sorted, num_keymaps);
755ece80b1dSMark Murray
756ece80b1dSMark Murray if (print) {
757ece80b1dSMark Murray for (i=0; i<num_keymaps; i++)
758ece80b1dSMark Murray printf("%s\n", km_sorted[i]->desc);
759ece80b1dSMark Murray exit(0);
760ece80b1dSMark Murray }
761ece80b1dSMark Murray
762ece80b1dSMark Murray show_dialog(km_sorted, num_keymaps);
763ece80b1dSMark Murray
764ece80b1dSMark Murray free(km_sorted);
765ece80b1dSMark Murray }
766ece80b1dSMark Murray
767ece80b1dSMark Murray /*
768ece80b1dSMark Murray * Display usage information and exit
769ece80b1dSMark Murray */
770ece80b1dSMark Murray static void
usage(void)771ece80b1dSMark Murray usage(void)
772ece80b1dSMark Murray {
773ece80b1dSMark Murray
774ece80b1dSMark Murray fprintf(stderr, "usage: %s\t[-K] [-V] [-d|-default] [-h|-help] "
775ece80b1dSMark Murray "[-l|-lang language]\n\t\t[-p|-print] [-r|-restore] [-s|-show] "
776ece80b1dSMark Murray "[-v|-verbose]\n", program);
777ece80b1dSMark Murray exit(1);
778ece80b1dSMark Murray }
779ece80b1dSMark Murray
780ece80b1dSMark Murray static void
parse_args(int argc,char ** argv)781ece80b1dSMark Murray parse_args(int argc, char **argv)
782ece80b1dSMark Murray {
783ece80b1dSMark Murray int i;
784ece80b1dSMark Murray
785ece80b1dSMark Murray for (i=1; i<argc; i++) {
786ece80b1dSMark Murray if (argv[i][0] != '-')
787ece80b1dSMark Murray usage();
788ece80b1dSMark Murray else if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h"))
789ece80b1dSMark Murray usage();
790ece80b1dSMark Murray else if (!strcmp(argv[i], "-verbose") || !strcmp(argv[i], "-v"))
791ece80b1dSMark Murray verbose = 1;
792ece80b1dSMark Murray else if (!strcmp(argv[i], "-lang") || !strcmp(argv[i], "-l"))
793ece80b1dSMark Murray if (i + 1 == argc)
794ece80b1dSMark Murray usage();
795ece80b1dSMark Murray else
796ece80b1dSMark Murray lang = argv[++i];
797ece80b1dSMark Murray else if (!strcmp(argv[i], "-default") || !strcmp(argv[i], "-d"))
798ece80b1dSMark Murray lang = lang_default;
799ece80b1dSMark Murray else if (!strcmp(argv[i], "-show") || !strcmp(argv[i], "-s"))
800ece80b1dSMark Murray show = 1;
801ece80b1dSMark Murray else if (!strcmp(argv[i], "-print") || !strcmp(argv[i], "-p"))
802ece80b1dSMark Murray print = 1;
803ece80b1dSMark Murray else if (!strcmp(argv[i], "-restore") ||
804ece80b1dSMark Murray !strcmp(argv[i], "-r")) {
805ece80b1dSMark Murray vidcontrol(font_current);
806ece80b1dSMark Murray exit(0);
807ece80b1dSMark Murray } else if (!strcmp(argv[i], "-K"))
808ece80b1dSMark Murray dir = keymapdir;
809ece80b1dSMark Murray else if (!strcmp(argv[i], "-V"))
810ece80b1dSMark Murray dir = fontdir;
811ece80b1dSMark Murray else
812ece80b1dSMark Murray usage();
813ece80b1dSMark Murray }
814ece80b1dSMark Murray }
815ece80b1dSMark Murray
816ece80b1dSMark Murray /*
817ece80b1dSMark Murray * A front-end for the 'vidfont' and 'kbdmap' programs.
818ece80b1dSMark Murray */
819ece80b1dSMark Murray int
main(int argc,char ** argv)820ece80b1dSMark Murray main(int argc, char **argv)
821ece80b1dSMark Murray {
822ece80b1dSMark Murray
823ece80b1dSMark Murray x11 = system("kbdcontrol -d >/dev/null");
824ece80b1dSMark Murray
825ece80b1dSMark Murray if (x11) {
826ece80b1dSMark Murray fprintf(stderr, "You are not on a virtual console - "
827ece80b1dSMark Murray "expect certain strange side-effects\n");
828ece80b1dSMark Murray sleep(2);
829ece80b1dSMark Murray }
830ece80b1dSMark Murray
8311b4e3694SEd Maste using_vt = check_vt();
8321b4e3694SEd Maste if (using_vt == 0) {
8336fe6b12fSStefan Eßer keymapdir = DEFAULT_SC_KEYMAP_DIR;
8346fe6b12fSStefan Eßer fontdir = DEFAULT_SC_FONT_DIR;
8356fe6b12fSStefan Eßer font_default = DEFAULT_SC_FONT;
8366fe6b12fSStefan Eßer }
8376fe6b12fSStefan Eßer
838ece80b1dSMark Murray SLIST_INIT(&head);
839ece80b1dSMark Murray
840ece80b1dSMark Murray lang = get_locale();
841ece80b1dSMark Murray
842ece80b1dSMark Murray program = extract_name(argv[0]);
843ece80b1dSMark Murray
844ece80b1dSMark Murray font_current = get_font();
845ece80b1dSMark Murray if (font_current == NULL)
846ece80b1dSMark Murray font_current = font_default;
847ece80b1dSMark Murray
848ece80b1dSMark Murray if (strcmp(program, "kbdmap"))
849ece80b1dSMark Murray dir = fontdir;
850ece80b1dSMark Murray else
851ece80b1dSMark Murray dir = keymapdir;
852ece80b1dSMark Murray
85361927b82SFlorent Thoumie /* Parse command line arguments */
85461927b82SFlorent Thoumie parse_args(argc, argv);
85561927b82SFlorent Thoumie
856ece80b1dSMark Murray /* Read and display options */
857ece80b1dSMark Murray menu_read();
858ece80b1dSMark Murray
859ece80b1dSMark Murray return 0;
860ece80b1dSMark Murray }
861