SchOS/cmd/loadkeys/main.c
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

184 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// loadkeys -- choose the keyboard layout.
//
// loadkeys print the active layout
// loadkeys -l list the layouts the kernel has
// loadkeys pl switch to Polish (programmers)
// loadkeys -c apply KEYMAP from /etc/vconsole.conf
//
// Named after the Linux tool because that is what anyone would type, but it is
// not the same program and takes no keymap files. Linux's loadkeys compiles a
// text keymap and uploads it a key at a time through KDSKBENT, which means the
// data lives in userspace and a machine whose /usr is not mounted has no
// layout at all. The layouts here are in the kernel and this only names one.
//
// The setting is machine-wide, not per-terminal: there is one keyboard, and
// switching to tty3 does not move anybody's fingers.
#include "core/types.h"
#include "uapi/user.h"
#include "uapi/fcntl.h"
#include "uapi/posix.h"
// No printf, memset or strcpy in lib/; this needs a little of each.
static void
puts_(const char *s)
{
int n = 0;
while (s[n]) n++;
write(1, (void *)s, n);
}
static void
zero(void *p, int n)
{
char *c = p;
while (n-- > 0) *c++ = 0;
}
static void
copy_bounded(char *dst, const char *src, int cap)
{
int i = 0;
while (i < cap - 1 && src[i]) { dst[i] = src[i]; i++; }
dst[i] = 0;
}
// /dev/console rather than fd 0: the caller may have redirected its input,
// and the layout being set belongs to the machine's keyboard, not to whichever
// terminal this process happens to be reading from.
static int
console(void)
{
int fd = open("/dev/console", O_RDWR);
if (fd < 0) {
puts_("loadkeys: no console to ask\n");
exit();
}
return fd;
}
#define VCONSOLE "/etc/vconsole.conf"
// Pull KEYMAP= out of /etc/vconsole.conf.
//
// Deliberately not a config parser. One key, whole file in one buffer, first
// match wins -- and a missing or unreadable file is not an error, because the
// kernel's default is already a working keyboard and refusing to boot over a
// preference would be absurd.
static int
keymap_from_file(char *out, int cap)
{
char buf[512];
int fd, n, i, j;
fd = open(VCONSOLE, O_RDONLY);
if (fd < 0)
return -1;
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n <= 0)
return -1;
buf[n] = 0;
for (i = 0; i < n; i++) {
if (buf[i] == '#') { // skip to end of comment
while (i < n && buf[i] != '\n') i++;
continue;
}
if (buf[i] != 'K')
continue;
const char *k = "KEYMAP=";
for (j = 0; k[j] && buf[i + j] == k[j]; j++)
;
if (k[j])
continue;
i += j;
for (j = 0; j < cap - 1 && buf[i + j] &&
buf[i + j] != '\n' && buf[i + j] != ' '; j++)
out[j] = buf[i + j];
out[j] = 0;
return j > 0 ? 0 : -1;
}
return -1;
}
int
main(int argc, char *argv[])
{
struct kb_layout k;
char want[16];
int fd, i;
if (argc > 2 || (argc == 2 && argv[1][0] == '-' &&
argv[1][1] != 'l' && argv[1][1] != 'c')) {
puts_("usage: loadkeys [-l | -c | <layout>]\n"
" -l list the available layouts\n"
" -c apply KEYMAP from " VCONSOLE "\n"
" <layout> switch to it; with no argument, print the active one\n");
return 1;
}
fd = console();
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'c') {
if (keymap_from_file(want, sizeof(want)) < 0) {
close(fd);
return 0; // no preference recorded; not a failure
}
zero(&k, sizeof(k));
copy_bounded(k.name, want, sizeof(k.name));
if (ioctl(fd, KB_SETLAYOUT, &k) < 0) {
puts_("loadkeys: " VCONSOLE " asks for '");
puts_(want);
puts_("', which this kernel does not have\n");
close(fd);
return 1;
}
close(fd);
return 0;
}
if (argc == 2 && argv[1][0] == '-') { // -l
for (i = 0; ; i++) {
zero(&k, sizeof(k));
k.index = i;
if (ioctl(fd, KB_LISTLAYOUT, &k) < 0)
break; // past the end
puts_(k.name);
puts_("\t");
puts_(k.desc);
puts_("\n");
}
close(fd);
return 0;
}
if (argc == 2) { // set
zero(&k, sizeof(k));
copy_bounded(k.name, argv[1], sizeof(k.name));
if (ioctl(fd, KB_SETLAYOUT, &k) < 0) {
puts_("loadkeys: no layout called '");
puts_(argv[1]);
puts_("' -- try loadkeys -l\n");
close(fd);
return 1;
}
close(fd);
return 0;
}
zero(&k, sizeof(k)); // report
if (ioctl(fd, KB_GETLAYOUT, &k) < 0) {
puts_("loadkeys: the kernel has no layout to report\n");
close(fd);
return 1;
}
puts_(k.name);
puts_("\t");
puts_(k.desc);
puts_("\n");
close(fd);
return 0;
}