/*
 * setleds.c - aeb, 940130
 *
 * Call: setleds [{+|-}{num|caps|scroll}]*
 * will set or clear the indicated leds (on the stdin tty),
 * and report the settings before and after.
 * In particular, setleds without arguments will only report.
 */

#include <stdio.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <sys/ioctl.h>

void
usage(void)
{
    fprintf(stderr, "\
Usage:
	setleds [options]
with options one or more of 'num', 'caps' or 'lock',
possibly preceded by a sign ('+' or '-').
Thus,
	setleds +caps -num
will set CapsLock, clear NumLock and leave ScrollLock unchanged.
Each vt has his own copy of these three bits. Use
	setleds [options] < /dev/ttyn
to change the settings of another vt.
The settings before and after the change are reported.
");
    exit(1);
}

    

#define onoff(a) ((a) ? "on " : "off")

void
report(leds) int leds;
{
    printf("NumLock %s   CapsLock %s   ScrollLock %s\n",
	   onoff(leds & LED_NUM),
	   onoff(leds & LED_CAP),
	   onoff(leds & LED_SCR));
}

struct led {
    char *name;
    int bit;
} leds[3] = {
    "scroll", LED_SCR,
    "num", LED_NUM,
    "caps", LED_CAP
};

int
main(argc,argv) int argc; char **argv;
{
    char oleds, nleds, sign;
    char *ap;
    struct led *lp;

    if (ioctl(0, KDGETLED, &oleds)) {
	perror("KDGETLED");
	fprintf(stderr, "Error reading current setting. Maybe stdin is not a VT?\n");
	exit(1);
    }

    if (argc <= 1) {
	report(oleds);
	exit(0);
    }

    nleds = oleds;
    while(--argc) {
	ap = *++argv;
	sign = 1;		/* by default: set */
	if(*ap == '+')
	  ap++;
	else if(*ap == '-') {
	    sign = 0;
	    ap++;
	}
	for (lp = leds; lp-leds < 3; lp++) {
	    if(!strcmp(ap, lp->name)) {
		if(sign)
		  nleds |= lp->bit;
		else
		  nleds &= ~(lp->bit);
		goto nxtarg;
	    }
	}
	fprintf(stderr, "unrecognized argument: _%s_\n\n", ap);
	usage();

      nxtarg: ;
    }
    printf("old state:    ");
    report(oleds);
    if (ioctl(0, KDSETLED, nleds)) {
	perror("KDSETLED");
	exit(1);
    }
    printf("new state:    ");
    report(nleds);
    exit(0);
}
