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
184 lines
5.1 KiB
C
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;
|
|
}
|