/************************************************************************
 *    This  is  a utility for remapping the keyboard under Unix V.3.2	*
 *    (only  tested  on  386/ix).  It  allows  me  to  map the switch	*
 *    screen  characters  to  things  like ALT-F1, ALT-F2 rather than	*
 *    the brain damaged way Interactive did the keyboard layout.	*
 *									*
 *    This  code  is modelled after the Xenix 386/2.2 mapkey program.	*
 *    For  some  reason  Interactive  left  this  program  out of the	*
 *    supplied software.						*
 *									*
 *    This  program  replaces the previous mapstr.c file in the utils	*
 *    directory.							*
 ************************************************************************/

# include	<stdio.h>
# include	<ctype.h>
# include	<sys/types.h>
# if	defined(M_XENIX)
#	include	<sys/keyboard.h>
# else
#	include	<sys/at_ansi.h>
#	include	<sys/kd.h>
# endif

# define	FALSE	0
# define	TRUE	1
# define	MAYBE	2

# define	XDIGIT(x)	(isdigit(x) ? x - '0' : (x >= 'A' && x <= 'F') ? x - 'A' + 10 : (x - 'a' + 10))

strmap_t	strmap;
char	*fkeys[NSTRKEYS];
keymap_t	kdkeymap;
srqtab_t	sreq;
struct kbentry  kbentry;
int	f_flag = MAYBE;
int	k_flag = MAYBE;

struct map {
	char	*name;
	int	value;
	};
char *msg2[] = { "O", "C", "N", "B" };
struct map ascii_msg[] = {
	"nul",	0,
	"soh",	1,
	"stx",	2,
	"etx",	3,
	"eot",	4,
	"enq",	5,
	"ack",	6,
	"bel",	7,
	"bs",	8,
	"ht",	9,
	"nl",	10,
	"vt",	11,
	"np",	12,
	"cr",	13,
	"so",	14,
	"si",	15,
	"dle",	16,
	"dc1",	17,
	"dc2",	18,
	"dc3",	19,
	"dc4",	20,
	"nak",	21,
	"syn",	22,
	"etb",	23,
	"can",	24,
	"em",	25,
	"sub",	26,
	"esc",	27,
	"fs",	28,
	"gs",	29,
	"rs",	30,
	"us",	31,
	"del",	127,
	NULL
	};
struct map msg1[] = {
	"nop",			K_NOP,
	"lshft",		K_LSH,
	"rshft",		K_RSH,
	"cplck",		K_CLK,
	"nmlck",		K_NLK,
	"sclck",		K_SLK,
	"alt",			K_ALT,
	"bktab",		K_BTAB,
	"ctrl",			K_CTL,
	"lalt",			K_LAL,
	"ralt",			K_RAL,
	"lctrl",		K_LCT,
	"rctrl",		K_RCT,
	"SYSREQ",		K_SRQ,
	"brk",			K_BRK,
	"escN", 		K_ESN,
	"escO", 		K_ESO,
	"escL", 		K_ESL,
	"REBOOT",		K_RBT,
	"DEBUG",		K_DBG,
	"NEXT",			K_NEXT,
	"PREV",			K_PREV,
	"FRCNXT",		K_FRCNEXT,
	"FRCPRV",		K_FRCPREV,
	"vt01",			K_VTF-1+1,
	"vt02",			K_VTF-1+2,
	"vt03",			K_VTF-1+3,
	"vt04",			K_VTF-1+4,
	"vt05",			K_VTF-1+5,
	"vt06",			K_VTF-1+6,
	"vt07",			K_VTF-1+7,
	"vt08",			K_VTF-1+8,
	"vt09",			K_VTF-1+9,
	"vt10",			K_VTF-1+10,
	"vt11",			K_VTF-1+11,
	"vt12",			K_VTF-1+12,
	"vt13",			K_VTF-1+13,
	"vt14",			K_VTF-1+14,
	"MGRF",			K_MGRF,
	"fkey1",		K_FUNF-1+1,
	"fkey2",		K_FUNF-1+2,
	"fkey3",		K_FUNF-1+3,
	"fkey4",		K_FUNF-1+4,
	"fkey5",		K_FUNF-1+5,
	"fkey6",		K_FUNF-1+6,
	"fkey7",		K_FUNF-1+7,
	"fkey8",		K_FUNF-1+8,
	"fkey9",		K_FUNF-1+9,
	"fkey10",		K_FUNF-1+10,
	"fkey11",		K_FUNF-1+11,
	"fkey12",		K_FUNF-1+12,
	"fkey13",		K_FUNF-1+13,
	"fkey14",		K_FUNF-1+14,
	"fkey15",		K_FUNF-1+15,
	"fkey16",		K_FUNF-1+16,
	"fkey17",		K_FUNF-1+17,
	"fkey18",		K_FUNF-1+18,
	"fkey19",		K_FUNF-1+19,
	"fkey20",		K_FUNF-1+20,
	"fkey21",		K_FUNF-1+21,
	"fkey22",		K_FUNF-1+22,
	"fkey23",		K_FUNF-1+23,
	"fkey24",		K_FUNF-1+24,
	"fkey25",		K_FUNF-1+25,
	"fkey26",		K_FUNF-1+26,
	"fkey27",		K_FUNF-1+27,
	"fkey28",		K_FUNF-1+28,
	"fkey29",		K_FUNF-1+29,
	"fkey30",		K_FUNF-1+30,
	"fkey31",		K_FUNF-1+31,
	"fkey32",		K_FUNF-1+32,
	"fkey33",		K_FUNF-1+33,
	"fkey34",		K_FUNF-1+34,
	"fkey35",		K_FUNF-1+35,
	"fkey36",		K_FUNF-1+36,
	"fkey37",		K_FUNF-1+37,
	"fkey38",		K_FUNF-1+38,
	"fkey39",		K_FUNF-1+39,
	"fkey40",		K_FUNF-1+40,
	"fkey41",		K_FUNF-1+41,
	"fkey42",		K_FUNF-1+42,
	"fkey43",		K_FUNF-1+43,
	"fkey44",		K_FUNF-1+44,
	"fkey45",		K_FUNF-1+45,
	"fkey46",		K_FUNF-1+46,
	"fkey47",		K_FUNF-1+47,
	"fkey48",		K_FUNF-1+48,
	"fkey49",		K_FUNF-1+49,
	"fkey50",		K_FUNF-1+50,
	"fkey51",		K_FUNF-1+51,
	"fkey52",		K_FUNF-1+52,
	"fkey53",		K_FUNF-1+53,
	"fkey54",		K_FUNF-1+54,
	"fkey55",		K_FUNF-1+55,
	"fkey56",		K_FUNF-1+56,
	"fkey57",		K_FUNF-1+57,
	"fkey58",		K_FUNF-1+58,
	"fkey59",		K_FUNF-1+59,
	"fkey60",		K_FUNF-1+60,
	"fkey61",		K_FUNF-1+61,
	"fkey62",		K_FUNF-1+62,
	"fkey63",		K_FUNF-1+63,
	"fkey64",		K_FUNF-1+64,
	"fkey65",		K_FUNF-1+65,
	"fkey66",		K_FUNF-1+66,
	"fkey67",		K_FUNF-1+67,
	"fkey68",		K_FUNF-1+68,
	"fkey69",		K_FUNF-1+69,
	"fkey70",		K_FUNF-1+70,
	"fkey71",		K_FUNF-1+71,
	"fkey72",		K_FUNF-1+72,
	"fkey73",		K_FUNF-1+73,
	"fkey74",		K_FUNF-1+74,
	"fkey75",		K_FUNF-1+75,
	"fkey76",		K_FUNF-1+76,
	"fkey77",		K_FUNF-1+77,
	"fkey78",		K_FUNF-1+78,
	"fkey79",		K_FUNF-1+79,
	"fkey80",		K_FUNF-1+80,
	"fkey81",		K_FUNF-1+81,
	"fkey82",		K_FUNF-1+82,
	"fkey83",		K_FUNF-1+83,
	"fkey84",		K_FUNF-1+84,
	"fkey85",		K_FUNF-1+85,
	"fkey86",		K_FUNF-1+86,
	"fkey87",		K_FUNF-1+87,
	"fkey88",		K_FUNF-1+88,
	"fkey89",		K_FUNF-1+89,
	"fkey90",		K_FUNF-1+90,
	"fkey91",		K_FUNF-1+91,
	"fkey92",		K_FUNF-1+92,
	"fkey93",		K_FUNF-1+93,
	"fkey94",		K_FUNF-1+94,
	"fkey95",		K_FUNF-1+95,
	"fkey96",		K_FUNF-1+96,
	"pfx1",			K_PFXF-1+1,
	"pfx2",			K_PFXF-1+2,
	"pfx3",			K_PFXF-1+3,
	"pfx4",			K_PFXF-1+4,
	"pfx5",			K_PFXF-1+5,
	"pfx6",			K_PFXF-1+6,
	"pfx7",			K_PFXF-1+7,
	"pfx8",			K_PFXF-1+8,
	"pfx9",			K_PFXF-1+9,
	"pfx10",		K_PFXF-1+10,
	"pfx11",		K_PFXF-1+11,
	"pfx12",		K_PFXF-1+12,
	"pfx13",		K_PFXF-1+13,
	"pfx14",		K_PFXF-1+14,
	"pfx15",		K_PFXF-1+15,
	"pfx16",		K_PFXF-1+16,
	"pfx17",		K_PFXF-1+17,
	"pfx18",		K_PFXF-1+18,
	"pfx19",		K_PFXF-1+19,
	"pfx20",		K_PFXF-1+20,
	"pfx21",		K_PFXF-1+21,
	"pfx22",		K_PFXF-1+22,
	"pfx23",		K_PFXF-1+23,
	"pfx24",		K_PFXF-1+24,
	"pfx25",		K_PFXF-1+25,
	"pfx26",		K_PFXF-1+26,
	"pfx27",		K_PFXF-1+27,
	"pfx28",		K_PFXF-1+28,
	"pfx29",		K_PFXF-1+29,
	"pfx30",		K_PFXF-1+30,
	"pfx31",		K_PFXF-1+31,
	"pfx32",		K_PFXF-1+32,
	"pfx33",		K_PFXF-1+33,
	"pfx34",		K_PFXF-1+34,
	"pfx35",		K_PFXF-1+35,
	"pfx36",		K_PFXF-1+36,
	"pfx37",		K_PFXF-1+37,
	"pfx38",		K_PFXF-1+38,
	"pfx39",		K_PFXF-1+39,
	"pfx40",		K_PFXF-1+40,
	"pfx41",		K_PFXF-1+41,
	"pfx42",		K_PFXF-1+42,
	"pfx43",		K_PFXF-1+43,
	"pfx44",		K_PFXF-1+44,
	"pfx45",		K_PFXF-1+45,
	"pfx46",		K_PFXF-1+46,
	"pfx47",		K_PFXF-1+47,
	"pfx48",		K_PFXF-1+48,
	"pfx49",		K_PFXF-1+49,
	"pfx50",		K_PFXF-1+50,
	"pfx51",		K_PFXF-1+51,
	"pfx52",		K_PFXF-1+52,
	"pfx53",		K_PFXF-1+53,
	"pfx54",		K_PFXF-1+54,
	"pfx55",		K_PFXF-1+55,
	"pfx56",		K_PFXF-1+56,
	"pfx57",		K_PFXF-1+57,
	"pfx58",		K_PFXF-1+58,
	"pfx59",		K_PFXF-1+59,
	NULL,
	};
char *
map(tbl, num)
struct map *tbl;
{	static char buf[12];

	while (tbl->name) {
		if (tbl->value == num)
			return tbl->name;
		tbl++;
		}
	sprintf(buf, "  %02x  ", num);
	return buf;
}
lookup(tbl, name)
register struct map *tbl;
register char *name;
{	register char ch = name[0];

	while (tbl->name) {
		if (tbl->name[0] == ch && strcmp(tbl->name, name) == 0)
			return tbl->value;
		tbl++;
		}
	return -1;
}
main(argc, argv)
char **argv;
{	int	i, j;
	int	arg_index = do_switches(argc, argv);
	
	get_fkey(FALSE);
	if (ioctl(0, GIO_KEYMAP, &kdkeymap) < 0)
		exit(1);

	get_sysreq_table();
	if (arg_index >= argc) {
		if (k_flag)
			print_kbd_table();
		get_fkey(TRUE);
		}
	else {
		readin_file(argv[arg_index]);
		if (argc > 2) {
			print_kbd_table();
			exit(0);
			}
		set_keyboard();
		set_fkey();
		}
	exit(0);
}
do_switches(argc, argv)
char **argv;
{	char	*cp;
	int	c;
	extern char *optarg;
	extern int optind;

	while ((c = getopt(argc, argv, "fk")) != EOF)
		switch (c) {
			case 'f':
				f_flag = TRUE;
				if (k_flag == MAYBE)
					k_flag = FALSE;
				break;
			case 'k':
				k_flag = TRUE;
				if (f_flag == MAYBE)
					f_flag = FALSE;
				break;
			default:
				usage();
			}
	
	return optind;
}
usage()
{
	fprintf(stderr, "Usage: kbd [-fk] [file]\n");
	fprintf(stderr, "	-f	Print function key string table.\n");
	fprintf(stderr, "	-k	Print keyboard mapping table.\n");
	exit(1);
}
set_fkey()
{	register int i;
	strmap_t	smap;
	register char *cp = (char *) smap;

	for (i = 0; i < NSTRKEYS; i++) {
		if (fkeys[i]) {
			strcpy(cp, fkeys[i]);
			cp += strlen(cp) + 1;
			continue;
			}
		*cp++ = NULL;
		}
	if (ioctl(0, PIO_STRMAP, smap) < 0) {
		perror("PIO_STRMAP");
		exit(1);
		}
}
set_keyboard()
{	register int i;

	if (ioctl(0, PIO_KEYMAP, &kdkeymap) < 0) {
		perror("PIO_KEYMAP");
		exit(1);
		}
	for (i = 0; i < kdkeymap.n_keys; i++) {
		kbentry.kb_table = K_SRQTAB;
		kbentry.kb_index = i;
		kbentry.kb_value = sreq[i];
		if (ioctl(0, KDSKBENT, &kbentry) < 0) {
			fprintf(stderr, "kb_table = %02x\n", kbentry.kb_table);
			fprintf(stderr, "kb_index = %02x\n", kbentry.kb_index);
			fprintf(stderr, "kb_value = %02x\n", kbentry.kb_value);
			perror("ioctl(KBSKBENT)");
			exit(1);
			}
		}
}
get_sysreq_table()
{	register int i;

	for (i = 0; i < kdkeymap.n_keys; i++) {
		kbentry.kb_table = K_SRQTAB;
		kbentry.kb_index = i;
		if (ioctl(0, KDGKBENT, &kbentry) < 0) {
			fprintf(stderr, "kb_table = %02x\n", kbentry.kb_table);
			fprintf(stderr, "kb_index = %02x\n", kbentry.kb_index);
			fprintf(stderr, "kb_value = %02x\n", kbentry.kb_value);
			perror("ioctl(KBGKBENT)");
			exit(1);
			}
		sreq[i] = kbentry.kb_value;
		}
}
readin_file(filename)
char *filename;
{	FILE	*fp;
	char	lbuf[BUFSIZ];
	char	*strtok();
	int	entry;
	char *cp;
	long	strtol();
	struct key_t key;
	char	*token;
	int	ch, i;
	int	line_no = 0;
			
	if ((fp = fopen(filename, "r")) == NULL) {
		perror(filename);
		exit(1);
		}
	while (!feof(fp)) {
		line_no++;
		if (fgets(lbuf, sizeof lbuf - 1, fp) == NULL)
			break;
		if (lbuf[0] == NULL || lbuf[0] == '#')
			continue;
		cp = lbuf;
		if (*cp == 'f') {
			char *malloc();
			register char *cp1;
			int	n;

			if (strncmp(cp, "fkey", 4) != 0) {
				fprintf(stderr, "%s:%d: syntax error\n", filename, line_no);
				exit(1);
				}
			cp += 4;
			entry = (int) strtol(cp, &cp, 0);
			entry--;
			while (*cp != '"')
				cp++;
			cp1 = cp+1;
			cp = malloc(30);
			fkeys[entry] = cp;
			for ( ; *cp1 != '"'; ) {
				if (*cp1 != '\\') {
					*cp++ = *cp1++;
					continue;
					}
				cp1++;
				if (*cp1 == 'x') {
					cp1++;
					n = XDIGIT(*cp1);
					cp1++;
					if (isxdigit(*cp1)) {
						n = n * 16 + XDIGIT(*cp1);
						cp1++;
						}
					*cp++ = n;
					continue;
					}
				if (!isdigit(*cp1)) {
					*cp++ = *cp1++;
					continue;
					}
				n = *cp1++ - '0';
				if (isdigit(*cp1)) {
					n = 8 * n + *cp1++ - '0';
					if (isdigit(*cp1))
						n = 8 * n + *cp1++ - '0';
					}
				*cp++ = n;
				}
			*cp = NULL;
			continue;
			}
		key.spcl = 0;
		entry = (int) strtol(cp, &cp, 0);
		for (i = 0; i < NUM_STATES; i++) {
			while (*cp == ':' || isspace(*cp))
				cp++;
			if (*cp == '\'') {
				cp++;
				if (*cp == '\\')
					cp++;
				key.map[i] = *cp++;
				cp++;
				continue;
				}
			token = strtok(cp, " \t");
			cp = token + strlen(token) + 1;
			if ((token[0] == 'f' && token[1] == 'k' && 
			    token[2] == 'e' && token[3] == 'y')) {
				key.map[i] = K_FUNF+atoi(token+4)-1;
				key.spcl |= (0x80 >> i);
				continue;
				}
			if ((ch = lookup(msg1, token)) >= 0) {
				key.map[i] = ch;
				key.spcl |= (0x80 >> i);
				continue;
				}
			if ((ch = lookup(ascii_msg, token)) >= 0) {
				key.map[i] = ch;
				continue;
				}
			fprintf(stderr, "%s:%d: syntax error in column %d '%s'\n",
				filename, line_no, i, token);
			exit(1);
			}
		while (isspace(*cp))
			cp++;
		switch (*cp) {
		  case 'O':
		  	key.flgs = 0;
		  	break;
		  case 'C':
		  	key.flgs = KMF_CLOCK;
		  	break;
		  case 'N':
		  	key.flgs = KMF_NLOCK;
		  	break;
		  case 'B':
		  	key.flgs = KMF_NLOCK | KMF_CLOCK;
		  	break;
		  default:
		  	fprintf(stderr, "%s:%d Invalid LOCK Column '%s'\n",
				filename, line_no, cp);
			exit(1);
		  }
		cp = strtok(cp+1, " \t\n");
		ch = lookup(msg1, cp);
		if (ch < 0) {
		  	fprintf(stderr, "%s:%d Invalid SYSREQ field '%s'\n",
				filename, line_no, cp);
			exit(1);
			}
		sreq[entry] = ch;
		kdkeymap.key[entry] = key;
		}
}
get_fkey(flag)
{
	register char *cp;
	int	fkey = 0;
	
	if (flag) {
		if (ioctl(0, GIO_STRMAP, strmap) < 0) {
			perror("GIO_STRMAP");
			exit(1);
			}
		}
	for (cp = (char *) strmap; fkey < NSTRKEYS; fkey++) {
		fkeys[fkey] = cp;
		if (!flag) {
			while (*cp)
				cp++;
			cp++;
			continue;
			}
		printf("fkey%d = \"", fkey+1);
		while (*cp) {
			if (*cp == '\\') {
				cp++;
				printf("\\\\");
				}
			else if (*cp == '"') {
				cp++;
				printf("\\\"");
				}
			else if (*cp >= ' ' && *cp <= 0x7e)
				putchar(*cp++);
			else
				printf("\\x%02x", *cp++ & 0xff);
			}
		cp++;
		printf("\"\n");
		}
}
print_kbd_table()
{	register int	i, j;
	print_header();
	for (i = 0; i < kdkeymap.n_keys; i++) {
		printf("0x%02x: ", i);
		for (j = 0; j < NUM_STATES; j++) {
			int ch = kdkeymap.key[i].map[j];
# if 0 /* Following macro wrong in 386/ix V.3.2.0.2 */
			if (IS_SPECIAL(i,j)) {
# else
			if (1) {
# endif
				printf("%-6s ", map(msg1, ch));
				}
			else {
				if (ch < ' ')
					printf("%-3s    ", ascii_msg[ch].name);
				else if (ch <= 0x7e) {
					if (ch == '\\' || ch == '\'')
						printf(" '\\%c'  ", ch);
					else
						printf(" '%c'   ", ch);
					}
				else if (ch == 0x7f)
					printf(" del   ");
				else 
					printf(" 0x%02x  ", ch);
				}
			}
		printf(" %s", msg2[kdkeymap.key[i].flgs & (KMF_CLOCK | KMF_NLOCK)]);
		printf("   %s\n", map(msg1, sreq[i]));
		}		
}
print_header()
{
	printf("#\n");
	printf("#                                                      ALT\n");
	printf("#                           CTRL         ALT     ALT   CTRL\n");
	printf("#     BASE   SHIFT   CTRL   SHIFT  ALT   SHIFT   CTRL  SHIFT  LOCK SRQTAB\n");
}
