blob: ed437f025961289b631290909a070c726c179f07 [file] [log] [blame]
/*
* setleds.c - aeb, 940130, 940909, 991008
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sysexits.h>
#include <linux/kd.h>
#include <sys/ioctl.h>
#include "array_size.h"
#include "libcommon.h"
/**
* BITWISE_NOT is the bitwise complement of x with cancelled out integer
* promotions.
*/
#define BITWISE_NOT(x) (_Generic((x), \
unsigned char: (unsigned char)(~(x)) \
))
#define BITMASK_UNSET(x, mask) ((x) & BITWISE_NOT(mask))
#define BITMASK_SET(x, mask) ((x) | (mask))
static void KBD_ATTR_NORETURN
usage(int rc, const struct kbd_help *options)
{
fprintf(stderr, _("Usage: %s [option...] [[+|-][ num | caps | scroll %s]]\n"),
program_invocation_short_name,
#ifdef __sparc__
"| compose "
#else
""
#endif
);
fprintf(stderr, "\n");
fprintf(stderr, _("Thus,\n"
" setleds +caps -num\n"
"will set CapsLock, clear NumLock and leave ScrollLock unchanged.\n"
"The settings before and after the change (if any) are reported\n"
"when the -v option is given or when no change is requested.\n"
"Normally, setleds influences the vt flag settings\n"
"(and these are usually reflected in the leds).\n"
"With -L, setleds only sets the leds, and leaves the flags alone.\n"
"With -D, setleds sets both the flags and the default flags, so\n"
"that a subsequent reset will not change the flags.\n"));
print_options(options);
print_report_bugs();
exit(rc);
}
#define onoff(a) ((a) ? _("on ") : _("off"))
/* report the bits, in the order seen on the (my) keyboard */
#define LED_NLOCK 1
#define LED_CMPOSE 2
#define LED_SCRLCK 4
#define LED_CLOCK 8
static void
sunreport(int leds)
{
printf("NumLock %s Compose %s ScrollLock %s CapsLock %s\n",
onoff(leds & LED_NLOCK),
onoff(leds & LED_CMPOSE),
onoff(leds & LED_SCRLCK),
onoff(leds & LED_CLOCK));
}
static void
report(int leds)
{
printf("NumLock %s CapsLock %s ScrollLock %s\n",
onoff(leds & LED_NUM),
onoff(leds & LED_CAP),
onoff(leds & LED_SCR));
}
static struct led {
const char *name;
unsigned char bit;
unsigned char sunbit;
} leds[] = {
{ "scroll", LED_SCR, LED_SCRLCK },
{ "num", LED_NUM, LED_NLOCK },
{ "caps", LED_CAP, LED_CLOCK },
#ifdef __sparc__
{ "compose", 0, LED_CMPOSE }
#endif
};
static void
getleds(unsigned char *cur_leds)
{
if (ioctl(0, KDGETLED, cur_leds)) {
kbd_error(EX_OSERR, errno, _("Error reading current led setting. "
"Maybe stdin is not a VT?: "
"ioctl KDGETLED"));
}
}
static int
setleds(unsigned char cur_leds)
{
if (ioctl(0, KDSETLED, cur_leds)) {
kbd_warning(errno, "ioctl KDSETLED");
return -1;
}
return 0;
}
static void
getflags(unsigned char *flags)
{
if (ioctl(0, KDGKBLED, flags))
kbd_error(EX_OSERR, errno, _("Unable to read keyboard flags"));
}
static int sunkbdfd = -1;
static void
sungetleds(unsigned char *cur_leds KBD_ATTR_UNUSED)
{
#ifdef KIOCGLED
if (ioctl(sunkbdfd, KIOCGLED, cur_leds)) {
kbd_error(EX_OSERR, errno, _("Error reading current led setting from /dev/kbd: "
"ioctl %s"), "KIOCGLED");
}
#else
kbd_error(EX_OSERR, 0, _("ioctl %s unavailable?"), "KIOCGLED");
#endif
}
static void
sunsetleds(unsigned char *cur_leds KBD_ATTR_UNUSED)
{
#ifdef KIOCSLED
if (ioctl(sunkbdfd, KIOCSLED, cur_leds)) {
kbd_error(EX_OSERR, errno, _("Error reading current led setting from /dev/kbd: "
"ioctl %s"), "KIOCSLED");
}
#else
kbd_error(EX_OSERR, 0, _("ioctl %s unavailable?"), "KIOCSLED");
#endif
}
static int
parse_let_option(char *ap, unsigned char *nval, unsigned char *ndef,
unsigned char *nsunval, unsigned char *nsundef)
{
struct led *lp;
unsigned char sign = 1;
switch (ap[0]) {
case '+':
/* by default: set */
ap++;
break;
case '-':
sign = 0;
ap++;
break;
}
for (lp = leds; (unsigned)(lp - leds) < ARRAY_SIZE(leds); lp++) {
if (!strcmp(ap, lp->name)) {
if (sign) {
*nval |= lp->bit;
*nsunval |= lp->sunbit;
}
*ndef |= lp->bit;
*nsundef |= lp->sunbit;
return 0;
}
}
return -1;
}
struct kbd_option {
const char *shrtopt;
const char *longopt;
int val;
};
int main(int argc, char **argv)
{
int has_changes = 0;
int optL = 0, optD = 0, optF = 0, verbose = 0;
unsigned char oleds, nleds, oflags, nflags, odefflags, ndefflags;
unsigned char nval, ndef;
unsigned char osunleds = 0, nsunleds, nsunval, nsundef;
setuplocale();
const struct kbd_option opts[] = {
{ "-D", "--default", 'D' },
{ "-F", "--flags", 'F' },
{ "-L", "--leds", 'L' },
{ "-h", "--help", 'h' },
{ "-v", "--verbose", 'v' },
{ "-V", "--version", 'V' },
{ NULL, NULL, 0 }
};
const struct kbd_help opthelp[] = {
{ "-D, --default", _("change both the VT flags and their default settings.") },
{ "-F, --flags", _("change the VT flags.") },
{ "-L, --leds", _("change only the leds.") },
{ "-V, --version", _("print version number.") },
{ "-v, --verbose", _("be more verbose.") },
{ "-h, --help", _("print this usage message.") },
{ NULL, NULL }
};
nval = 0;
ndef = 0;
nsunval = 0;
nsundef = 0;
while (argc > 1) {
const struct kbd_option *opt;
for (opt = opts; opt->longopt; opt++) {
if (strcmp(argv[1], opt->shrtopt) && strcmp(argv[1], opt->longopt))
continue;
switch (opt->val) {
case 'D':
optD = 1;
break;
case 'F':
optF = 1;
break;
case 'L':
optL = 1;
break;
case 'v':
verbose = 1;
break;
case 'V':
print_version_and_exit();
break;
case 'h':
usage(EX_OK, opthelp);
break;
default:
kbd_warning(0, _("Unrecognized argument: %s"), argv[1]);
usage(EX_USAGE, opthelp);
}
goto next_arg;
}
if (parse_let_option(argv[1], &nval, &ndef, &nsunval, &nsundef) < 0) {
kbd_warning(0, _("Unrecognized argument: %s"), argv[1]);
usage(EX_USAGE, opthelp);
}
has_changes = 1;
next_arg:
argc--;
argv++;
}
#ifdef __sparc__
if ((sunkbdfd = open("/dev/kbd", O_RDONLY)) < 0) {
kbd_error(EX_OSFILE, errno, "open /dev/kbd");
/* exit(1); */
}
#endif
getflags(&oflags);
getleds(&oleds);
if (sunkbdfd >= 0)
sungetleds(&osunleds);
odefflags = ndefflags = ((oflags >> 4) & 7);
oflags = nflags = (oflags & 7);
if (!has_changes) {
if (optL) {
nleds = 0xff;
if (setleds(nleds)) {
kbd_error(EX_OSERR, 0, _("Error resetting ledmode"));
}
}
/* If nothing to do, report, even if not verbose */
if (!optD && !optL && !optF)
optD = optL = optF = 1;
if (optD) {
printf(_("Current default flags: "));
report(odefflags);
}
if (optF) {
printf(_("Current flags: "));
report(oflags & 07);
}
if (optL) {
printf(_("Current leds: "));
if (sunkbdfd >= 0)
sunreport(osunleds);
else
report(oleds);
}
return EX_OK;
}
if (!optL)
optF = 1;
if (optD) {
ndefflags = BITMASK_SET(BITMASK_UNSET(odefflags, ndef), nval);
if (verbose) {
printf(_("Old default flags: "));
report(odefflags);
printf(_("New default flags: "));
report(ndefflags);
}
}
if (optF) {
nflags = BITMASK_SET(BITMASK_UNSET(oflags, ndef), nval);
if (verbose) {
printf(_("Old flags: "));
report(oflags & 07);
printf(_("New flags: "));
report(nflags & 07);
}
}
if (optD || optF) {
if (ioctl(0, KDSKBLED, (ndefflags << 4) | nflags)) {
kbd_error(EX_OSERR, errno, "ioctl KDSKBLED");
}
}
if (optL) {
if (sunkbdfd >= 0) {
nsunleds = BITMASK_SET(BITMASK_UNSET(osunleds, nsundef), nsunval);
if (verbose) {
printf(_("Old leds: "));
sunreport(osunleds);
printf(_("New leds: "));
sunreport(nsunleds);
}
sunsetleds(&nsunleds);
} else {
nleds = BITMASK_SET(BITMASK_UNSET(oleds, ndef), nval);
if (verbose) {
printf(_("Old leds: "));
report(oleds);
printf(_("New leds: "));
report(nleds);
}
if (setleds(nleds))
return EX_OSERR;
}
}
return EX_OK;
}