Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 "types.h"
|
|
#include "user.h"
|
|
#include "fcntl.h"
|
|
#include "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;
|
|
}
|