blob: c4e2513552f6d06feb64d58b5b07f42cea53f6e0 [file] [log] [blame]
/* dump.c
*
* This file is part of kbd project.
* Copyright (C) 1993 Risto Kankkunen.
* Copyright (C) 1993 Eugene G. Crosser.
* Copyright (C) 1994-2007 Andries E. Brouwer.
* Copyright (C) 2007-2013 Alexey Gladkov <gladkov.alexey@gmail.com>
*
* This file is covered by the GNU General Public License,
* which should be included with kbd as the file COPYING.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "keymap.h"
#include "contextP.h"
#include "ksyms.h"
#include "modifiers.h"
#include "nls.h"
#define U(x) ((x) ^ 0xf000)
static void
outchar(FILE *fd, unsigned char c, int comma)
{
fprintf(fd, "'");
fprintf(fd, (c == '\'' || c == '\\') ? "\\%c"
: isgraph(c) ? "%c"
: "\\%03o",
c);
fprintf(fd, comma ? "', " : "'");
}
// FIXME: Merge outchar ?
static void
dumpchar(FILE *fd, unsigned char c, int comma)
{
fprintf(fd, "'");
fprintf(fd, (c == '\'' || c == '\\') ? "\\%c"
: (isgraph(c) || c == ' ' || c >= 0200) ? "%c"
: "\\%03o",
c);
fprintf(fd, comma ? "', " : "'");
}
int lk_dump_bkeymap(struct lk_ctx *ctx, FILE *fd)
{
unsigned int i, j;
char magic[] = "bkeymap";
if (lk_add_constants(ctx) < 0)
return -1;
if (fwrite(magic, 7, 1, fd) != 1)
goto fail;
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
char flag;
flag = lk_map_exists(ctx, i);
if (fwrite(&flag, sizeof(flag), 1, fd) != 1)
goto fail;
}
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
if (!lk_map_exists(ctx, i))
continue;
for (j = 0; j < NR_KEYS / 2; j++) {
unsigned short v;
v = lk_get_key(ctx, i, j);
if (fwrite(&v, sizeof(v), 1, fd) != 1)
goto fail;
}
}
return 0;
fail:
ERR(ctx, _("Error writing map to file"));
return -1;
}
static char *
mk_mapname(char modifier)
{
static char *mods[8] = {
"shift", "altgr", "ctrl", "alt", "shl", "shr", "ctl", "ctr"
};
static char buf[60];
int i;
if (!modifier)
return "plain";
buf[0] = 0;
for (i = 0; i < 8; i++)
if (modifier & (1 << i)) {
if (buf[0])
strcat(buf, "_");
strcat(buf, mods[i]);
}
return buf;
}
int lk_dump_ctable(struct lk_ctx *ctx, FILE *fd)
{
int j;
unsigned int i, imax;
unsigned int maxfunc;
unsigned int func_table_offs[MAX_NR_FUNC];
unsigned int func_buf_offset = 0;
struct lk_kbdiacr *kddiac;
if (lk_add_constants(ctx) < 0)
return -1;
fprintf(fd,
/* not to be translated... */
"/* Do not edit this file! It was automatically generated by */\n");
fprintf(fd, "/* loadkeys --mktable defkeymap.map > defkeymap.c */\n\n");
fprintf(fd, "#include <linux/keyboard.h>\n");
fprintf(fd, "#include <linux/kd.h>\n\n");
for (i = 0; i < MAX_NR_KEYMAPS; i++)
if (lk_map_exists(ctx, i)) {
if (i)
fprintf(fd, "static ");
fprintf(fd, "unsigned short %s_map[NR_KEYS] = {", mk_mapname(i));
for (j = 0; j < NR_KEYS; j++) {
if (!(j % 8))
fprintf(fd, "\n");
fprintf(fd, "\t0x%04x,", U(lk_get_key(ctx, i, j)));
}
fprintf(fd, "\n};\n\n");
}
for (imax = MAX_NR_KEYMAPS - 1; imax > 0; imax--)
if (lk_map_exists(ctx, imax))
break;
fprintf(fd, "ushort *key_maps[MAX_NR_KEYMAPS] = {");
for (i = 0; i <= imax; i++) {
fprintf(fd, (i % 4) ? " " : "\n\t");
if (lk_map_exists(ctx, i))
fprintf(fd, "%s_map,", mk_mapname(i));
else
fprintf(fd, "0,");
}
if (imax < MAX_NR_KEYMAPS - 1)
fprintf(fd, "\t0");
fprintf(fd, "\n};\n\nunsigned int keymap_count = %u;\n\n", (unsigned int)ctx->keymap->count);
/* uglified just for xgettext - it complains about nonterminated strings */
fprintf(fd,
"/*\n"
" * Philosophy: most people do not define more strings, but they who do\n"
" * often want quite a lot of string space. So, we statically allocate\n"
" * the default and allocate dynamically in chunks of 512 bytes.\n"
" */\n"
"\n");
for (maxfunc = MAX_NR_FUNC; maxfunc; maxfunc--)
if (lk_array_get_ptr(ctx->func_table, maxfunc - 1))
break;
fprintf(fd, "char func_buf[] = {\n");
for (i = 0; i < maxfunc; i++) {
char *ptr, *func;
func = ptr = lk_array_get_ptr(ctx->func_table, i);
if (!ptr)
continue;
func_table_offs[i] = func_buf_offset;
fprintf(fd, "\t");
for (; *ptr; ptr++)
outchar(fd, *ptr, 1);
fprintf(fd, "0, \n");
func_buf_offset += (ptr - func + 1);
}
if (!maxfunc)
fprintf(fd, "\t0\n");
fprintf(fd, "};\n\n");
fprintf(fd,
"char *funcbufptr = func_buf;\n"
"int funcbufsize = sizeof(func_buf);\n"
"int funcbufleft = 0; /* space left */\n"
"\n");
fprintf(fd, "char *func_table[MAX_NR_FUNC] = {\n");
for (i = 0; i < maxfunc; i++) {
if (lk_array_get_ptr(ctx->func_table, i))
fprintf(fd, "\tfunc_buf + %u,\n", func_table_offs[i]);
else
fprintf(fd, "\t0,\n");
}
if (maxfunc < MAX_NR_FUNC)
fprintf(fd, "\t0,\n");
fprintf(fd, "};\n");
if (ctx->flags & LK_FLAG_PREFER_UNICODE) {
fprintf(fd, "\nstruct kbdiacruc accent_table[MAX_DIACR] = {\n");
for (i = 0; i < ctx->accent_table->count; i++) {
kddiac = lk_array_get_ptr(ctx->accent_table, i);
fprintf(fd, "\t{");
outchar(fd, kddiac->diacr, 1);
outchar(fd, kddiac->base, 1);
fprintf(fd, "0x%04x},", kddiac->result);
if (i % 2)
fprintf(fd, "\n");
}
if (i % 2)
fprintf(fd, "\n");
fprintf(fd, "};\n\n");
} else {
fprintf(fd, "\nstruct kbdiacr accent_table[MAX_DIACR] = {\n");
for (i = 0; i < ctx->accent_table->count; i++) {
kddiac = lk_array_get_ptr(ctx->accent_table, i);
fprintf(fd, "\t{");
outchar(fd, kddiac->diacr, 1);
outchar(fd, kddiac->base, 1);
outchar(fd, kddiac->result, 0);
fprintf(fd, "},");
if (i % 2)
fprintf(fd, "\n");
}
if (i % 2)
fprintf(fd, "\n");
fprintf(fd, "};\n\n");
}
fprintf(fd, "unsigned int accent_table_size = %u;\n",
(unsigned int)ctx->accent_table->count);
return 0;
}
/* void dump_funcs(void) */
void lk_dump_funcs(struct lk_ctx *ctx, FILE *fd)
{
unsigned int i;
for (i = 0; i < ctx->func_table->total; i++) {
char *ptr = lk_array_get_ptr(ctx->func_table, i);
if (!ptr)
continue;
fprintf(fd, "string %s = \"", get_sym(ctx, KT_FN, i));
for (; *ptr; ptr++) {
if (*ptr == '"' || *ptr == '\\') {
fputc('\\', fd);
fputc(*ptr, fd);
} else if (isgraph(*ptr) || *ptr == ' ') {
fputc(*ptr, fd);
} else {
fprintf(fd, "\\%03hho", *ptr);
}
}
fputc('"', fd);
fputc('\n', fd);
}
}
/* void dump_diacs(void) */
void lk_dump_diacs(struct lk_ctx *ctx, FILE *fd)
{
unsigned int i;
struct lk_kbdiacr *ptr;
const char *ksym;
for (i = 0; i < ctx->accent_table->count; i++) {
ptr = lk_array_get_ptr(ctx->accent_table, i);
if (!ptr)
continue;
fprintf(fd, "compose ");
dumpchar(fd, ptr->diacr, 0);
fprintf(fd, " ");
dumpchar(fd, ptr->base, 0);
#ifdef KDGKBDIACRUC
if (ctx->flags & LK_FLAG_PREFER_UNICODE) {
ksym = codetoksym(ctx, ptr->result ^ 0xf000);
if (ksym) {
fprintf(fd, " to %s\n", ksym);
} else {
fprintf(fd, " to U+%04x\n", ptr->result);
}
} else
#endif
if (ptr->result > 0xff) {
// impossible case?
fprintf(fd, " to 0x%x\n", ptr->result);
} else {
ksym = codetoksym(ctx, ptr->result);
if (ksym) {
fprintf(fd, " to %s\n", ksym);
} else {
fprintf(fd, " to ");
dumpchar(fd, ptr->result, 0);
fprintf(fd, "\n");
}
}
}
}
void lk_dump_keymaps(struct lk_ctx *ctx, FILE *fd)
{
unsigned int i;
int n, m, s;
i = n = m = s = 0;
fprintf(fd, "keymaps");
for (i = 0; i < ctx->keymap->total; i++) {
if (ctx->keywords & LK_KEYWORD_ALTISMETA && i == (i | M_ALT))
continue;
if (!lk_map_exists(ctx, i)) {
if (!m)
continue;
n--, m--;
(n == m)
? fprintf(fd, "%c%d", (s ? ',' : ' '), n)
: fprintf(fd, "%c%d-%d", (s ? ',' : ' '), n, m);
n = m = 0;
s = 1;
} else {
if (!n)
n = i + 1;
m = i + 1;
}
}
if (m) {
n--, m--;
(n == m)
? fprintf(fd, "%c%d", (s ? ',' : ' '), n)
: fprintf(fd, "%c%d-%d", (s ? ',' : ' '), n, m);
}
fprintf(fd, "\n");
}
static void
print_mod(FILE *fd, int x)
{
if (x) {
modifier_t *mod = (modifier_t *)modifiers;
while (mod->name) {
if (x & (1 << mod->bit))
fprintf(fd, "%s\t", mod->name);
mod++;
}
} else {
fprintf(fd, "plain\t");
}
}
static void
print_keysym(struct lk_ctx *ctx, FILE *fd, int code, char numeric)
{
unsigned int t, v;
const char *p;
int plus;
fprintf(fd, " ");
t = KTYP(code);
v = KVAL(code);
if (t >= syms_size) {
if (!numeric && (p = codetoksym(ctx, code)) != NULL)
fprintf(fd, "%-16s", p);
else
fprintf(fd, "U+%04x ", code ^ 0xf000);
return;
}
plus = 0;
if (t == KT_LETTER) {
t = KT_LATIN;
fprintf(fd, "+");
plus++;
}
if (!numeric && t == KT_LATIN &&
(p = codetoksym(ctx, code)))
fprintf(fd, "%-*s", 16 - plus, p);
else if (!numeric && t < syms_size && v < get_sym_size(ctx, t) &&
(p = get_sym(ctx, t, v))[0])
fprintf(fd, "%-*s", 16 - plus, p);
else if (!numeric && t == KT_META && v < 128 && v < get_sym_size(ctx, KT_LATIN) &&
(p = get_sym(ctx, KT_LATIN, v))[0])
fprintf(fd, "Meta_%-11s", p);
else
fprintf(fd, "0x%04x %s", code, plus ? "" : " ");
}
static void
print_bind(struct lk_ctx *ctx, FILE *fd, int bufj, int i, int j, char numeric)
{
if (j)
fprintf(fd, "\t");
print_mod(fd, j);
fprintf(fd, "keycode %3d =", i);
print_keysym(ctx, fd, bufj, numeric);
fprintf(fd, "\n");
}
void lk_dump_keys(struct lk_ctx *ctx, FILE *fd, lk_table_shape table, char numeric)
{
unsigned int i, j;
int buf[MAX_NR_KEYMAPS];
int isletter, islatin, isasexpected;
int typ, val;
int alt_is_meta = 0;
int all_holes;
int zapped[MAX_NR_KEYMAPS];
unsigned int keymapnr = ctx->keymap->total;
if (!keymapnr)
return;
if (table == LK_SHAPE_FULL_TABLE || table == LK_SHAPE_SEPARATE_LINES)
goto no_shorthands;
/* first pass: determine whether to set alt_is_meta */
for (j = 0; j < ctx->keymap->total; j++) {
unsigned int ja = (j | M_ALT);
if (!(j != ja && lk_map_exists(ctx, j) && lk_map_exists(ctx, ja)))
continue;
for (i = 1; i < NR_KEYS; i++) {
int buf0, buf1, type;
buf0 = lk_get_key(ctx, j, i);
if (buf0 == -1)
break;
type = KTYP(buf0);
if ((type == KT_LATIN || type == KT_LETTER) && KVAL(buf0) < 128) {
buf1 = lk_map_exists(ctx, ja)
? lk_get_key(ctx, ja, i)
: -1;
if (buf1 != K(KT_META, KVAL(buf0)))
goto not_alt_is_meta;
}
}
}
alt_is_meta = 1;
fprintf(fd, "alt_is_meta\n");
not_alt_is_meta:
no_shorthands:
for (i = 1; i < NR_KEYS; i++) {
all_holes = 1;
for (j = 0; j < keymapnr; j++) {
buf[j] = K_HOLE;
if (lk_map_exists(ctx, j))
buf[j] = lk_get_key(ctx, j, i);
if (buf[j] != K_HOLE)
all_holes = 0;
}
if (all_holes && table != LK_SHAPE_FULL_TABLE)
continue;
if (table == LK_SHAPE_FULL_TABLE) {
fprintf(fd, "keycode %3d =", i);
for (j = 0; j < keymapnr; j++) {
if (lk_map_exists(ctx, j))
print_keysym(ctx, fd, buf[j], numeric);
}
fprintf(fd, "\n");
continue;
}
if (table == LK_SHAPE_SEPARATE_LINES) {
for (j = 0; j < keymapnr; j++) {
//if (buf[j] != K_HOLE)
print_bind(ctx, fd, buf[j], i, j, numeric);
}
fprintf(fd, "\n");
continue;
}
typ = KTYP(buf[0]);
val = KVAL(buf[0]);
islatin = (typ == KT_LATIN || typ == KT_LETTER);
isletter = (islatin &&
((val >= 'A' && val <= 'Z') ||
(val >= 'a' && val <= 'z')));
isasexpected = 0;
if (isletter) {
unsigned 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 < keymapnr; j++) {
if ((j >= 16 && buf[j] != K_HOLE) || (j < 16 && buf[j] != defs[j]))
goto unexpected;
}
isasexpected = 1;
}
unexpected:
/* wipe out predictable meta bindings */
for (j = 0; j < keymapnr; j++)
zapped[j] = 0;
if (alt_is_meta) {
for (j = 0; j < keymapnr; j++) {
unsigned int ja, ktyp;
ja = (j | M_ALT);
if (j != ja && lk_map_exists(ctx, ja) && ((ktyp = KTYP(buf[j])) == KT_LATIN || ktyp == KT_LETTER) && KVAL(buf[j]) < 128) {
if (buf[ja] != K(KT_META, KVAL(buf[j])))
fprintf(stderr, _("impossible: not meta?\n"));
buf[ja] = K_HOLE;
zapped[ja] = 1;
}
}
}
fprintf(fd, "keycode %3d =", i);
if (isasexpected) {
/* print only a single entry */
/* suppress the + for ordinary a-zA-Z */
print_keysym(ctx, fd, K(KT_LATIN, val), numeric);
fprintf(fd, "\n");
} else {
/* choose between single entry line followed by exceptions,
and long line followed by exceptions; avoid VoidSymbol */
unsigned int bad, count;
bad = count = 0;
for (j = 1; j < keymapnr; j++) {
if (zapped[j])
continue;
if (buf[j] != buf[0])
bad++;
if (buf[j] != K_HOLE)
count++;
}
if (bad <= count && bad < keymapnr - 1) {
if (buf[0] != K_HOLE) {
print_keysym(ctx, fd, buf[0], numeric);
}
fprintf(fd, "\n");
for (j = 1; j < keymapnr; j++) {
if (buf[j] != buf[0] && !zapped[j]) {
print_bind(ctx, fd, buf[j], i, j, numeric);
}
}
} else {
for (j = 0;
j < keymapnr && buf[j] != K_HOLE &&
(table != LK_SHAPE_UNTIL_HOLE || lk_map_exists(ctx, j));
j++) {
//print_bind(ctx, fd, buf[j], i, j, numeric);
print_keysym(ctx, fd, buf[j], numeric);
}
fprintf(fd, "\n");
for (; j < keymapnr; j++) {
if (buf[j] != K_HOLE) {
print_bind(ctx, fd, buf[j], i, j, numeric);
}
}
}
}
}
}
void lk_dump_keymap(struct lk_ctx *ctx, FILE *fd, lk_table_shape table, char numeric)
{
lk_dump_keymaps(ctx, fd);
lk_dump_keys(ctx, fd, table, numeric);
lk_dump_funcs(ctx, fd);
}