/*
 * loadkeys.y
 *
 * Changes for 0.82:
 * Merged from version 0.81 of loadkeys.y and mktable.c - aeb@cwi.nl
 * Reason for change:
 *   The original version of mktable would use the current kernel
 *   for getting at the entries. However, this does not work if
 *   e.g. NR_FUNC in keyboard.h has changed. So, instead of getting
 *   the information from the current kernel, we must get it out of
 *   defkeymap.map, just like loadkeys. Thus, mktable is now an
 *   option of loadkeys.
 * (Other advantage: we first do the parsing, and then the key binding.
 *  No bindings are changed in case of a syntax error.)
 * Fix: contrary to the documentation in keytables.5 it was assumed
 * by default that the AltGr modifier had no effect. This assumption
 * has been removed.
 *
 * Changes for 0.83:
 * Added the intl_con patch by Eugene G. Crosser:
 * The modifier + in front of a keysym means that it is a letter,
 * and susceptible for change under CapsLock. For ASCII 'a'-'z'
 * and 'A'-'Z' no + is required (when given as a single entry).
 *
 * Changes for 0.84:
 * More compose key support. Default search path. Option -d.
 *
 * Change for 0.85:
 * Do not change compose key table when no definitions given.
 * Option -c to override this.
 *
 * Changes for 0.86:
 * Added a few synonyms. Comment may now contain non-ASCII symbols.
 *
 * Changes for 0.87:
 * Accept the "charset iso-8859-x" directive.
 *
 * Changes for 0.88:
 * Handle sparse keymaps and many strings. Accept "keymaps" directive.
 * Handle 8 modifiers. Handle Unicode.
 *
 * Change for 0.93:
 * Set K_UNICODE during the loading of keymaps requiring that.
 */

%token EOL NUMBER LITERAL CHARSET KEYMAPS KEYCODE EQUALS
%token PLAIN SHIFT CONTROL ALT ALTGR SHIFTL SHIFTR CTRLL CTRLR
%token COMMA DASH STRING STRLITERAL COMPOSE TO CCHAR ERROR PLUS
%token UNUMBER

%{
#include <errno.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <linux/keyboard.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include "paths.h"

#ifndef KT_LETTER
#define KT_LETTER KT_LATIN
#endif

/* name to use in error messages */
char *progname;

/* What keymaps are we defining? */
char defining[MAX_NR_KEYMAPS];
char keymaps_line_seen = 0;
int max_keymap = 0;		/* from here on, defining[] is false */

/* the kernel structures we want to set or print */
u_short *key_map[MAX_NR_KEYMAPS];
char *func_table[MAX_NR_FUNC];
struct kbdiacr accent_table[MAX_DIACR];
unsigned int accent_table_size = 0;

char key_is_constant[NR_KEYS];
char *keymap_was_set[MAX_NR_KEYMAPS];
char func_buf[4096];		/* should be allocated dynamically */
char *fp = func_buf;

#define U(x) ((x) ^ 0xf000)

#undef ECHO
#include "analyze.c"

#define VERSION "0.93"

static void addmap(int map, int explicit);
static void addkey(int index, int table, int keycode);
static void addfunc(struct kbsentry kbs_buf);
static void compose(int diacr, int base, int res);
static void do_constant(void);
static void loadkeys(void);
static void mktable(void);
extern void set_charset(char *charset);
extern int getfd(void);
int key_buf[MAX_NR_KEYMAPS];
int mod;
int unicode_used;
%}

%%
keytable	:
		| keytable line
		;
line		: EOL
		| charsetline
		| keymapline
		| fullline
		| singleline
		| strline
                | compline
		;
charsetline	: CHARSET STRLITERAL EOL
			{
			    set_charset(kbs_buf.kb_string);
			}
		;
keymapline	: KEYMAPS range EOL
			{
			    keymaps_line_seen = 1;
			}
		;
range		: range COMMA range0
		| range0
		;
range0		: NUMBER DASH NUMBER
			{
			    int i;
			    for (i = $1; i<= $3; i++)
			      addmap(i,1);
			}
		| NUMBER
			{
			    addmap($1,1);
			}
		;
strline		: STRING LITERAL EQUALS STRLITERAL EOL
			{
			    if (KTYP($2) != KT_FN) {
				fprintf(stderr, "%s: '%s' is not a function key symbol\n",
					progname,
					syms[KTYP($2)].table[KVAL($2)]);
				exit(1);
			    }
			    kbs_buf.kb_func = KVAL($2);
			    addfunc(kbs_buf);
			}
		;
compline        : COMPOSE CCHAR CCHAR TO CCHAR EOL
                        {
			    compose($2, $3, $5);
			}
                ;
singleline	:	{ mod = 0; }
		  modifiers KEYCODE NUMBER EQUALS rvalue EOL
			{
			    addkey($4, mod, $6);
			}
		| PLAIN KEYCODE NUMBER EQUALS rvalue EOL
			{
			    addkey($4, 0, $6);
			}
		;
modifiers	: modifiers modifier
		| modifier
		;
modifier	: SHIFT		{ mod |= (1 << KG_SHIFT);	}
		| CONTROL	{ mod |= (1 << KG_CTRL);	}
		| ALT		{ mod |= (1 << KG_ALT);		}
		| ALTGR		{ mod |= (1 << KG_ALTGR);	}
		| SHIFTL	{ mod |= (1 << KG_SHIFTL);	}
		| SHIFTR	{ mod |= (1 << KG_SHIFTR);	}
		| CTRLL		{ mod |= (1 << KG_CTRLL);	}
		| CTRLR		{ mod |= (1 << KG_CTRLR);	}
		;
fullline	: KEYCODE NUMBER EQUALS rvalue0 EOL
	{
	    int i, j;

	    if (keymaps_line_seen) {
		i = 0;
		for (j = 0; j < max_keymap; j++)
		  if (defining[j]) {
		      if (rvalct != 1 || i == 0)
			addkey($2, j, (i < rvalct) ? key_buf[i] : K_HOLE);
		      i++;
		  }
		if (i < rvalct) {
		    fprintf(stderr, "%s: too many (%d) entries on one line\n",
			    progname, rvalct);
		    exit(1);
		}
	    } else
	      for (i = 0; i < rvalct; i++)
		addkey($2, i, key_buf[i]);

	    if (rvalct == 1)
	      key_is_constant[$2] = 1;
	}
		;

rvalue0		: 
		| rvalue1 rvalue0
		;
rvalue1		: rvalue
			{
			    if (rvalct >= MAX_NR_KEYMAPS) {
				fprintf(stderr, "%s: too many keydefinitions on one line\n",
					progname);
				exit(1);
			    }
			    key_buf[rvalct++] = $1;
			}
		;
rvalue		: NUMBER
			{$$=$1;}
		| UNUMBER
			{$$=($1 ^ 0xf000); unicode_used=1;}
                | PLUS NUMBER
                        {$$=K(KT_LETTER, KVAL($2));}
		| LITERAL
			{$$=$1;}
                | PLUS LITERAL
                        {$$=K(KT_LETTER, KVAL($2));}
		;
%%			

void
usage(void) {
	fprintf(stderr, "\
loadkeys version " VERSION "\

Usage: loadkeys [option...] [mapfile...]

valid options are:

        -c --clearcompose clear kernel compose table
	-d --default	  load \"" DEFMAP "\"
	-h --help	  display this help text
        -m --mktable      output a \"defkeymap.c\" to stdout
	-s --clearstrings clear kernel string table
        -v --verbose      report the changes
");
	exit(1);
}

char **args;
int optd = 0;
int optm = 0;
int opts = 0;
int verbose = 0;
int nocompose = 0;

int
main(unsigned int argc, char *argv[]) {
	const char *short_opts = "cdhmsv";
	const struct option long_opts[] = {
		{ "clearcompose", no_argument, NULL, 'c' },
	        { "default",    no_argument, NULL, 'd' },
		{ "help",	no_argument, NULL, 'h' },
		{ "mktable",    no_argument, NULL, 'm' },
		{ "clearstrings", no_argument, NULL, 's' },
		{ "verbose",    no_argument, NULL, 'v' },
		{ NULL, 0, NULL, 0 }
	};
	int c;

	progname = argv[0];

	while ((c = getopt_long(argc, argv,
		short_opts, long_opts, NULL)) != -1) {
		switch (c) {
		        case 'c':
		                nocompose = 1;
				break;
		        case 'd':
		    		optd = 1;
				break;
		        case 'm':
		                optm = 1;
				break;
			case 's':
				opts = 1;
				break;
			case 'v':
				verbose = 1;
				break;
			case 'h':
			case '?':
				usage();
		}
	}

	args = argv + optind - 1;
	unicode_used = 0;
	yywrap();	/* set up the first input file, if any */
	if (yyparse()) {
		fprintf(stderr, "syntax error in map file\n");
		if(!optm)
		  fprintf(stderr, "key bindings not changed\n");
		exit(1);
	}
	do_constant();
	if(optm)
	        mktable();
	else
	        loadkeys();
	exit(0);
}

extern char pathname[];

int
yyerror(char *s) {
	fprintf(stderr, "%s:%d: %s\n",
		pathname, line_nr, s);
	return(0);
}

char *dirpath[] = { "", DATADIR "/" KEYMAPDIR "/", KERNDIR "/", 0 };
char *suffixes[] = { "", ".map", 0 };
extern FILE *findfile(char *fnam, char **dirpath, char **suffixes);

#undef yywrap
int
yywrap(void) {
	FILE *f;
	static int first_file = 1; /* ugly kludge flag */

	line_nr = 1;
	if (optd) {
	        /* first read default map */
	        optd = 0;
	        if((f = findfile(DEFMAP, dirpath, suffixes)) == NULL) {
		    fprintf(stderr, "Cannot find %s\n", DEFMAP);
		    exit(1);
		}
		goto gotf;
	}
	if (*args)
		args++;
	if (!*args)
		return 1;
	if (!strcmp(*args, "-")) {
		f = stdin;
		strcpy(pathname, "<stdin>");
	} else if ((f = findfile(*args, dirpath, suffixes)) == NULL) {
		fprintf(stderr, "cannot open file %s\n", *args);
		exit(1);
	}
	/*
		Can't use yyrestart if this is called before entering yyparse()
		I think assigning directly to yyin isn't necessarily safe in
		other situations, hence the flag.
	*/
      gotf:
	fprintf(stderr, "Loading %s\n", pathname);
	if (first_file) {
		yyin = f;
		first_file = 0;
	} else
		yyrestart(f);
	return 0;
}

static void
addmap(int i, int explicit) {
	if (i < 0 || i >= MAX_NR_KEYMAPS) {
	    fprintf(stderr, "%s: addmap called with bad index %d\n",
		    progname, i);
	    exit(1);
	}
	if (!defining[i]) {
	    if (keymaps_line_seen && !explicit) {
		fprintf(stderr, "%s: adding map %d violates explicit keymaps line\n",
			progname, i);
		exit(1);
	    }
	    defining[i] = 1;
	    if (max_keymap <= i)
	      max_keymap = i+1;
	}
}

static void
addkey(int index, int table, int keycode) {
	int i;

	if (keycode == -1)
		return;
        if (index < 0 || index >= NR_KEYS) {
	        fprintf(stderr, "%s: addkey called with bad index %d\n",
			progname, index);
		exit(1);
	}
        if (table < 0 || table >= MAX_NR_KEYMAPS) {
	        fprintf(stderr, "%s: addkey called with bad table %d\n",
			progname, table);
		exit(1);
	}
	if (!defining[table])
	  addmap(table, 0);
	if (!key_map[table]) {
	        key_map[table] = (u_short *) malloc(NR_KEYS * sizeof(u_short));
		if (!key_map[table]) {
		    fprintf(stderr, "%s: Out of memory.\n", progname);
		    exit(1);
		}
		for (i = 0; i < NR_KEYS; i++)
		  (key_map[table])[i] = K_HOLE;
	}
	if (!keymap_was_set[table]) {
	        keymap_was_set[table] = (char *) malloc(NR_KEYS);
		if (!keymap_was_set[table]) {
		    fprintf(stderr, "%s: Out of memory.\n", progname);
		    exit(1);
		}
		for (i = 0; i < NR_KEYS; i++)
		  (keymap_was_set[table])[i] = 0;
	}

	(key_map[table])[index] = keycode;
	(keymap_was_set[table])[index] = 1;
}

static void
addfunc(struct kbsentry kbs) {
        int sh, i;
	char *p, *q, *r;

        if (kbs.kb_func >= MAX_NR_FUNC) {
	        fprintf(stderr, "%s: addfunc called with bad func %d\n",
			progname, kbs.kb_func);
		exit(1);
	}
	if ((q = func_table[kbs.kb_func])) { /* throw out old previous def */
	        sh = strlen(q) + 1;
		p = q + sh;
		while (p < fp)
		        *q++ = *p++;
		fp -= sh;
	}
	p = func_buf;                        /* find place for new def */
	for (i = 0; i < kbs.kb_func; i++)
	        if (func_table[i]) {
		        p = func_table[i];
			while(*p++);
		}
	func_table[kbs.kb_func] = p;
        sh = strlen(kbs.kb_string) + 1;
	if (fp + sh > func_buf + sizeof(func_buf)) {
	        fprintf(stderr, "%s: addfunc: func_buf overflow\n", progname);
		exit(1);
	}
	q = fp;
	fp += sh;
	r = fp;
	while (q > p)
	        *--r = *--q;
	strcpy(p, kbs.kb_string);
	for (i++; i < MAX_NR_FUNC; i++)
	        if (func_table[i])
		        func_table[i] += sh;
}

static void
compose(int diacr, int base, int res) {
        struct kbdiacr *p;
        if (accent_table_size == MAX_DIACR) {
	        fprintf(stderr, "compose table overflow\n");
		exit(1);
	}
	p = &accent_table[accent_table_size++];
	p->diacr = diacr;
	p->base = base;
	p->result = res;
}

static int
defkeys(int fd) {
	struct kbentry ke;
	int ct = 0;
	int i,j,fail;
	int oldm;

	if (unicode_used) {
	     /* Switch keyboard mode for a moment -
		do not complain about errors.
		Do not attempt a reset if the change failed. */
	     if (ioctl(fd, KDGKBMODE, &oldm)
	        || (oldm != K_UNICODE && ioctl(fd, KDSKBMODE, K_UNICODE)))
		  oldm = K_UNICODE;
	}

	for(i=0; i<MAX_NR_KEYMAPS; i++) {
	    if (key_map[i]) {
		for(j=0; j<NR_KEYS; j++) {
		    if ((keymap_was_set[i])[j]) {
			ke.kb_index = j;
			ke.kb_table = i;
			ke.kb_value = (key_map[i])[j];

			fail = ioctl(fd, KDSKBENT, (unsigned long)&ke);
			if (fail) {
			    if (errno == EPERM) {
				fprintf(stderr, "Keymap %d: Permission denied\n", i);
				j = NR_KEYS;
				continue;
			    }
			    perror("KDSKBENT");
			} else
			  ct++;
			if(verbose)
			  printf("keycode %d, table %d = %d%s\n", j, i, (key_map[i])[j],
				 fail ? "    FAILED" : "");
			else if (fail)
			  fprintf(stderr, "failed to bind key %d to value %d\n",
				  j, (key_map[i])[j]);
		    }
		}
	    } else if (keymaps_line_seen && !defining[i]) {
		/* deallocate keymap */
		ke.kb_index = 0;
		ke.kb_table = i;
		ke.kb_value = K_NOSUCHMAP;
#if 0
		if (verbose)
		  printf("deallocate keymap %d\n", i);
#endif
		if(ioctl(fd, KDSKBENT, (unsigned long)&ke)) {
		    if (errno != EINVAL) {
			perror("KDSKBENT");
			fprintf(stderr,
				"%s: could not deallocate keymap %d\n",
				progname, i);
			exit(1);
		    }
		    /* probably an old kernel */
		    /* clear keymap by hand */
		    for (j = 0; j < NR_KEYS; j++) {
			ke.kb_index = j;
			ke.kb_table = i;
			ke.kb_value = K_HOLE;
			if(ioctl(fd, KDSKBENT, (unsigned long)&ke)) {
			    if (errno == EINVAL && i >= 16)
			      break; /* old kernel */
			    perror("KDSKBENT");
			    fprintf(stderr,
				    "%s: cannot deallocate or clear keymap\n",
				    progname);
			    exit(1);
			}
		    }
		}
	    }
	}

	if(unicode_used && oldm != K_UNICODE) {
	     if (ioctl(fd, KDSKBMODE, oldm)) {
		  fprintf(stderr, "%s: failed to restore keyboard mode\n",
			  progname);
	     }
	     fprintf(stderr, "%s: warning: this map uses Unicode symbols\n"
		             "    (perhaps you want to do `kbd_mode -u'?)\n",
		     progname);
	}
	return ct;
}

static char *
ostr(char *s) {
	int lth = strlen(s);
	char *ns0 = malloc(4*lth + 1);
	char *ns = ns0;

	if (!ns0) {
	  fprintf(stderr, "out of memory?\n");
	  return "";
	}
	while(*s) {
	  switch(*s) {
	  case '\n':
	    *ns++ = '\\';
	    *ns++ = 'n';
	    break;
	  case '\033':
	    *ns++ = '\\';
	    *ns++ = '0';
	    *ns++ = '3';
	    *ns++ = '3';
	    break;
	  default:
	    *ns++ = *s;
	  }
	  s++;
	}
	*ns = 0;
	return ns0;
}

static int
deffuncs(int fd){
        int i, ct = 0;
	char *p;

        for (i = 0; i < MAX_NR_FUNC; i++) {
	    kbs_buf.kb_func = i;
	    if ((p = func_table[i])) {
		strcpy(kbs_buf.kb_string, p);
		if (ioctl(fd, KDSKBSENT, (unsigned long)&kbs_buf))
		  fprintf(stderr, "failed to bind string '%s' to function %s\n",
			  ostr(kbs_buf.kb_string), syms[KT_FN].table[kbs_buf.kb_func]);
		else
		  ct++;
	    } else if (opts) {
		kbs_buf.kb_string[0] = 0;
		if (ioctl(fd, KDSKBSENT, (unsigned long)&kbs_buf))
		  fprintf(stderr, "failed to clear string %s\n",
			  syms[KT_FN].table[kbs_buf.kb_func]);
		else
		  ct++;
	    }
	  }
	return ct;
}

static int
defdiacs(int fd){
        struct kbdiacrs kd;
	int i;

	kd.kb_cnt = accent_table_size;
	if (kd.kb_cnt > MAX_DIACR) {
	    kd.kb_cnt = MAX_DIACR;
	    fprintf(stderr, "too many compose definitions\n");
	}
	for (i = 0; i < kd.kb_cnt; i++)
	    kd.kbdiacr[i] = accent_table[i];

	if(ioctl(fd, KDSKBDIACR, (unsigned long) &kd)) {
	    fprintf(stderr, "KDSKBDIACR failed\n");
	    perror("");
	    exit(1);
	}
	return kd.kb_cnt;
}

static void
do_constant (void) {
	int i, j, r0 = 0;

	if (keymaps_line_seen)
	    while (r0 < max_keymap && !defining[r0])
	      r0++;

	for (i=0; i<NR_KEYS; i++)
	  if (key_is_constant[i]) {
	      u_short key;
	      int typ, val;
	      if (!key_map[r0]) {
		  fprintf(stderr, "%s: impossible error in do_constant\n", progname);
		  exit(1);
	      }
	      key = (key_map[r0])[i];
	      typ = KTYP(key);
	      val = KVAL(key);
	      if (typ != KT_LATIN && typ != KT_LETTER) {
		  for (j=r0+1; j<max_keymap; j++)
		    if(defining[j] && (!(keymap_was_set[j]) || !(keymap_was_set[j])[i]))
		      addkey(i, j, key);
	      } else if ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z')) {
		  u_short defs[16];
		  defs[0] = K(KT_LETTER, val);
		  defs[1] = K(KT_LETTER, val ^ 32);
		  defs[2] = defs[0];
		  defs[3] = defs[1];
		  for(j=4; j<8; j++)
		    defs[j] = K(KT_LATIN, val & ~96);
		  for(j=8; j<16; j++)
		    defs[j] = K(KT_META, KVAL(defs[j-8]));
		  for(j=0; j<max_keymap; j++) {
		      if (!defining[j])
			continue;
		      if (j > 0 && keymap_was_set[j] && (keymap_was_set[j])[i])
			continue;
		      addkey(i, j, defs[j%16]);
		  }
	      }
	  }
}

static void
loadkeys (void) {
        int fd;
        int keyct, funcct, diacct;

	fd = getfd();
	keyct = defkeys(fd);
	funcct = deffuncs(fd);
	if (accent_table_size > 0 || nocompose)
	  diacct = defdiacs(fd);
	if (verbose) {
	        printf("\nChanged %d key%s and %d string%s.\n",
		       keyct, (keyct == 1) ? "" : "s",
		       funcct, (funcct == 1) ? "" : "s");
		if (accent_table_size > 0 || nocompose)
		  printf("Loaded %d compose definition%s.\n",
			 diacct, (diacct == 1) ? "" : "s");
		else
		  printf("(No change in compose definitions.)\n");
	}
}

/*
 * mktable.c
 *
 */
static char *modifiers[8] = {
    "shift", "altgr", "ctrl", "alt", "shl", "shr", "ctl", "ctr"
};

static char *mk_mapname(char mod) {
    static char buf[60];
    int i;

    if (!mod)
      return "plain";
    buf[0] = 0;
    for (i=0; i<8; i++)
      if (mod & (1<<i)) {
	  if (buf[0])
	    strcat(buf, "_");
	  strcat(buf, modifiers[i]);
      }
    return buf;
}


static void
outchar (unsigned char c, int comma) {
        printf("'");
        printf((c == '\'' || c == '\\') ? "\\%c" : isgraph(c) ? "%c"
	       : "\\%03o", c);
	printf(comma ? "', " : "'");
}

static void
mktable () {
	int i, imax, j;

	struct kbsentry kbs;
	u_char *p;
	int maxfunc;
	unsigned int keymap_count = 0;

	printf(
"/* Do not edit this file! It was automatically generated by   */\n");
	printf(
"/*    loadkeys --mktable defkeymap.map > defkeymap.c          */\n\n");
	printf("#include <linux/types.h>\n");
	printf("#include <linux/keyboard.h>\n");
	printf("#include <linux/kd.h>\n\n");

	for (i = 0; i < MAX_NR_KEYMAPS; i++)
	  if (key_map[i]) {
	      keymap_count++;
	      if (i)
		   printf("static ");
	      printf("u_short %s_map[NR_KEYS] = {", mk_mapname(i));
	      for (j = 0; j < NR_KEYS; j++) {
		  if (!(j % 8))
		    printf("\n");
		  printf("\t0x%04x,", U((key_map[i])[j]));
	      }
	      printf("\n};\n\n");
	  }

	for (imax = MAX_NR_KEYMAPS-1; imax > 0; imax--)
	  if (key_map[imax])
	    break;
	printf("ushort *key_maps[MAX_NR_KEYMAPS] = {");
	for (i = 0; i <= imax; i++) {
	    printf((i%4) ? " " : "\n\t");
	    if (key_map[i])
	      printf("%s_map,", mk_mapname(i));
	    else
	      printf("0,");
	}
	if (imax < MAX_NR_KEYMAPS-1)
	  printf("\t0");
	printf("\n};\n\nunsigned int keymap_count = %d;\n\n", keymap_count);

	printf("\
/*
 * Philosophy: most people do not define more strings, but they who do
 * often want quite a lot of string space. So, we statically allocate
 * the default and allocate dynamically in chunks of 512 bytes.
 */

");
	for (maxfunc = MAX_NR_FUNC; maxfunc; maxfunc--)
	  if(func_table[maxfunc-1])
	    break;

	printf("char func_buf[] = {\n");
	for (i = 0; i < maxfunc; i++) {
	    p = func_table[i];
	    if (p) {
		printf("\t");
		for ( ; *p; p++)
		        outchar(*p, 1);
		printf("0, \n");
	    }
	}
	if (!maxfunc)
	  printf("\t0\n");
	printf("};\n\n");

	printf("\
char *funcbufptr = func_buf;
int funcbufsize = sizeof(func_buf);
int funcbufleft = 0;          /* space left */

");

	printf("char *func_table[MAX_NR_FUNC] = {\n");
	for (i = 0; i < maxfunc; i++) {
	    if (func_table[i])
	      printf("\tfunc_buf + %d,\n", func_table[i] - func_buf);
	    else
	      printf("\t0,\n");
	}
	if (maxfunc < MAX_NR_FUNC)
	  printf("\t0,\n");
	printf("};\n");

	printf("\nstruct kbdiacr accent_table[MAX_DIACR] = {\n");
	for (i = 0; i < accent_table_size; i++) {
	        printf("\t{");
	        outchar(accent_table[i].diacr, 1);
		outchar(accent_table[i].base, 1);
		outchar(accent_table[i].result, 0);
		printf("},");
		if(i%2) printf("\n");
	}
	if(i%2) printf("\n");
	printf("};\n\n");
	printf("unsigned int accent_table_size = %d;\n",
	       accent_table_size);

	exit(0);
}


