diff options
| author | Haibo Huang <hhb@google.com> | 2020-09-08 17:10:03 -0700 |
|---|---|---|
| committer | Haibo Huang <hhb@google.com> | 2020-09-10 22:20:42 +0000 |
| commit | bffa8499cb8ce3cc4366055be8fe62d501d6a8e5 (patch) | |
| tree | 648dfaada5799a6227dd5f1af43d89ed8d71d96d /src | |
| parent | e4e474780d90ed6166f7113a7464371baa275007 (diff) | |
| download | platform_external_libxkbcommon-master.tar.gz platform_external_libxkbcommon-master.tar.bz2 platform_external_libxkbcommon-master.zip | |
1. Run meson build locally:
meson config -Denable-x11=false -Denable-wayland=false -Denable-docs=false
2. Copy over generated parser.h / parser.c
3. Copy over config.h, and remove defines for not supported functions.
Change-Id: Id7f3c822c1d958fa541685344961507bcfa03b17
Diffstat (limited to 'src')
64 files changed, 33632 insertions, 0 deletions
diff --git a/src/atom.c b/src/atom.c new file mode 100644 index 0000000..180b25f --- /dev/null +++ b/src/atom.c @@ -0,0 +1,197 @@ +/*********************************************************** + * Copyright 1987, 1998 The Open Group + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of The Open Group shall not be + * used in advertising or otherwise to promote the sale, use or other dealings + * in this Software without prior written authorization from The Open Group. + * + * + * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Digital not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + ******************************************************************/ + +/************************************************************ + * Copyright 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#include "config.h" + +#include "utils.h" +#include "atom.h" + +/* FNV-1a (http://www.isthe.com/chongo/tech/comp/fnv/). */ +static inline uint32_t +hash_buf(const char *string, size_t len) +{ + uint32_t hash = 2166136261u; + for (size_t i = 0; i < (len + 1) / 2; i++) { + hash ^= (uint8_t) string[i]; + hash *= 0x01000193; + hash ^= (uint8_t) string[len - 1 - i]; + hash *= 0x01000193; + } + return hash; +} + +/* + * The atom table is a insert-only unbalanced binary search tree + * mapping strings to atoms. + * + * The tree nodes are kept contiguously in the `table` array. + * + * The atom value is the index of the tree node in the array. + * + * As an optimization, strings are not compared by value directly, + * s1 < s2 + * instead, they are compared by fingerprint (hash) and the value + * is only used to resolve collisions: + * (fingerprint(s1), s1) < (fingerprint(s2), s2) + * Fingerprint are pre-calculated and saved in the tree nodes. + * + * Why is this not just a hash table? Who knows! + */ +struct atom_node { + xkb_atom_t left, right; + uint32_t fingerprint; + char *string; +}; + +struct atom_table { + xkb_atom_t root; + darray(struct atom_node) table; +}; + +struct atom_table * +atom_table_new(void) +{ + struct atom_table *table = calloc(1, sizeof(*table)); + if (!table) + return NULL; + + darray_init(table->table); + /* The original throw-away root is here, at the illegal atom 0. */ + darray_resize0(table->table, 1); + + return table; +} + +void +atom_table_free(struct atom_table *table) +{ + if (!table) + return; + + struct atom_node *node; + darray_foreach(node, table->table) + free(node->string); + darray_free(table->table); + free(table); +} + +const char * +atom_text(struct atom_table *table, xkb_atom_t atom) +{ + assert(atom < darray_size(table->table)); + return darray_item(table->table, atom).string; +} + +xkb_atom_t +atom_intern(struct atom_table *table, const char *string, size_t len, bool add) +{ + uint32_t fingerprint = hash_buf(string, len); + + xkb_atom_t *atomp = &table->root; + while (*atomp != XKB_ATOM_NONE) { + struct atom_node *node = &darray_item(table->table, *atomp); + + if (fingerprint > node->fingerprint) { + atomp = &node->right; + } + else if (fingerprint < node->fingerprint) { + atomp = &node->left; + } + else { + /* Now start testing the strings. */ + const int cmp = strncmp(string, node->string, len); + if (likely(cmp == 0 && node->string[len] == '\0')) { + return *atomp; + } + else if (cmp > 0) { + atomp = &node->right; + } + else { + atomp = &node->left; + } + } + } + + if (!add) + return XKB_ATOM_NONE; + + struct atom_node node; + node.string = strndup(string, len); + assert(node.string != NULL); + node.left = node.right = XKB_ATOM_NONE; + node.fingerprint = fingerprint; + xkb_atom_t atom = darray_size(table->table); + /* Do this before the append, as it may realloc and change the offsets. */ + *atomp = atom; + darray_append(table->table, node); + return atom; +} diff --git a/src/atom.h b/src/atom.h new file mode 100644 index 0000000..49478db --- /dev/null +++ b/src/atom.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2009 Dan Nicholson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef ATOM_H +#define ATOM_H + +typedef uint32_t xkb_atom_t; + +#define XKB_ATOM_NONE 0 + +struct atom_table; + +struct atom_table * +atom_table_new(void); + +void +atom_table_free(struct atom_table *table); + +xkb_atom_t +atom_intern(struct atom_table *table, const char *string, size_t len, bool add); + +const char * +atom_text(struct atom_table *table, xkb_atom_t atom); + +#endif /* ATOM_H */ diff --git a/src/compose/parser.c b/src/compose/parser.c new file mode 100644 index 0000000..0f85a92 --- /dev/null +++ b/src/compose/parser.c @@ -0,0 +1,739 @@ +/* + * Copyright © 2013 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/****************************************************************** + + Copyright 1992 by Oki Technosystems Laboratory, Inc. + Copyright 1992 by Fuji Xerox Co., Ltd. + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Oki Technosystems +Laboratory and Fuji Xerox not be used in advertising or publicity +pertaining to distribution of the software without specific, written +prior permission. +Oki Technosystems Laboratory and Fuji Xerox make no representations +about the suitability of this software for any purpose. It is provided +"as is" without express or implied warranty. + +OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS +LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THIS SOFTWARE. + + Author: Yasuhiro Kawai Oki Technosystems Laboratory + Author: Kazunori Nishihara Fuji Xerox + +******************************************************************/ + +#include "config.h" + +#include <errno.h> + +#include "utils.h" +#include "scanner-utils.h" +#include "table.h" +#include "paths.h" +#include "utf8.h" +#include "parser.h" + +#define MAX_LHS_LEN 10 +#define MAX_INCLUDE_DEPTH 5 + +/* + * Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c. + * See also the XCompose(5) manpage. + * + * FILE ::= { [PRODUCTION] [COMMENT] "\n" | INCLUDE } + * INCLUDE ::= "include" '"' INCLUDE_STRING '"' + * PRODUCTION ::= LHS ":" RHS [ COMMENT ] + * COMMENT ::= "#" {<any character except null or newline>} + * LHS ::= EVENT { EVENT } + * EVENT ::= [MODIFIER_LIST] "<" keysym ">" + * MODIFIER_LIST ::= (["!"] {MODIFIER} ) | "None" + * MODIFIER ::= ["~"] MODIFIER_NAME + * MODIFIER_NAME ::= ("Ctrl"|"Lock"|"Caps"|"Shift"|"Alt"|"Meta") + * RHS ::= ( STRING | keysym | STRING keysym ) + * STRING ::= '"' { CHAR } '"' + * CHAR ::= GRAPHIC_CHAR | ESCAPED_CHAR + * GRAPHIC_CHAR ::= locale (codeset) dependent code + * ESCAPED_CHAR ::= ('\\' | '\"' | OCTAL | HEX ) + * OCTAL ::= '\' OCTAL_CHAR [OCTAL_CHAR [OCTAL_CHAR]] + * OCTAL_CHAR ::= (0|1|2|3|4|5|6|7) + * HEX ::= '\' (x|X) HEX_CHAR [HEX_CHAR]] + * HEX_CHAR ::= (0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|a|b|c|d|e|f) + * + * INCLUDE_STRING is a filesystem path, with the following %-expansions: + * %% - '%'. + * %H - The user's home directory (the $HOME environment variable). + * %L - The name of the locale specific Compose file (e.g., + * "/usr/share/X11/locale/<localename>/Compose"). + * %S - The name of the system directory for Compose files (e.g., + * "/usr/share/X11/locale"). + */ + +enum rules_token { + TOK_END_OF_FILE = 0, + TOK_END_OF_LINE, + TOK_INCLUDE, + TOK_INCLUDE_STRING, + TOK_LHS_KEYSYM, + TOK_COLON, + TOK_BANG, + TOK_TILDE, + TOK_STRING, + TOK_IDENT, + TOK_ERROR +}; + +/* Values returned with some tokens, like yylval. */ +union lvalue { + struct { + /* Still \0-terminated. */ + const char *str; + size_t len; + } string; +}; + +static enum rules_token +lex(struct scanner *s, union lvalue *val) +{ +skip_more_whitespace_and_comments: + /* Skip spaces. */ + while (is_space(peek(s))) + if (next(s) == '\n') + return TOK_END_OF_LINE; + + /* Skip comments. */ + if (chr(s, '#')) { + skip_to_eol(s); + goto skip_more_whitespace_and_comments; + } + + /* See if we're done. */ + if (eof(s)) return TOK_END_OF_FILE; + + /* New token. */ + s->token_line = s->line; + s->token_column = s->column; + s->buf_pos = 0; + + /* LHS Keysym. */ + if (chr(s, '<')) { + while (peek(s) != '>' && !eol(s) && !eof(s)) + buf_append(s, next(s)); + if (!chr(s, '>')) { + scanner_err(s, "unterminated keysym literal"); + return TOK_ERROR; + } + if (!buf_append(s, '\0')) { + scanner_err(s, "keysym literal is too long"); + return TOK_ERROR; + } + val->string.str = s->buf; + val->string.len = s->buf_pos; + return TOK_LHS_KEYSYM; + } + + /* Colon. */ + if (chr(s, ':')) + return TOK_COLON; + if (chr(s, '!')) + return TOK_BANG; + if (chr(s, '~')) + return TOK_TILDE; + + /* String literal. */ + if (chr(s, '\"')) { + while (!eof(s) && !eol(s) && peek(s) != '\"') { + if (chr(s, '\\')) { + uint8_t o; + if (chr(s, '\\')) { + buf_append(s, '\\'); + } + else if (chr(s, '"')) { + buf_append(s, '"'); + } + else if (chr(s, 'x') || chr(s, 'X')) { + if (hex(s, &o)) + buf_append(s, (char) o); + else + scanner_warn(s, "illegal hexadecimal escape sequence in string literal"); + } + else if (oct(s, &o)) { + buf_append(s, (char) o); + } + else { + scanner_warn(s, "unknown escape sequence (%c) in string literal", peek(s)); + /* Ignore. */ + } + } else { + buf_append(s, next(s)); + } + } + if (!chr(s, '\"')) { + scanner_err(s, "unterminated string literal"); + return TOK_ERROR; + } + if (!buf_append(s, '\0')) { + scanner_err(s, "string literal is too long"); + return TOK_ERROR; + } + if (!is_valid_utf8(s->buf, s->buf_pos - 1)) { + scanner_err(s, "string literal is not a valid UTF-8 string"); + return TOK_ERROR; + } + val->string.str = s->buf; + val->string.len = s->buf_pos; + return TOK_STRING; + } + + /* Identifier or include. */ + if (is_alpha(peek(s)) || peek(s) == '_') { + s->buf_pos = 0; + while (is_alnum(peek(s)) || peek(s) == '_') + buf_append(s, next(s)); + if (!buf_append(s, '\0')) { + scanner_err(s, "identifier is too long"); + return TOK_ERROR; + } + + if (streq(s->buf, "include")) + return TOK_INCLUDE; + + val->string.str = s->buf; + val->string.len = s->buf_pos; + return TOK_IDENT; + } + + /* Discard rest of line. */ + skip_to_eol(s); + + scanner_err(s, "unrecognized token"); + return TOK_ERROR; +} + +static enum rules_token +lex_include_string(struct scanner *s, struct xkb_compose_table *table, + union lvalue *val_out) +{ + while (is_space(peek(s))) + if (next(s) == '\n') + return TOK_END_OF_LINE; + + s->token_line = s->line; + s->token_column = s->column; + s->buf_pos = 0; + + if (!chr(s, '\"')) { + scanner_err(s, "include statement must be followed by a path"); + return TOK_ERROR; + } + + while (!eof(s) && !eol(s) && peek(s) != '\"') { + if (chr(s, '%')) { + if (chr(s, '%')) { + buf_append(s, '%'); + } + else if (chr(s, 'H')) { + const char *home = secure_getenv("HOME"); + if (!home) { + scanner_err(s, "%%H was used in an include statement, but the HOME environment variable is not set"); + return TOK_ERROR; + } + if (!buf_appends(s, home)) { + scanner_err(s, "include path after expanding %%H is too long"); + return TOK_ERROR; + } + } + else if (chr(s, 'L')) { + char *path = get_locale_compose_file_path(table->locale); + if (!path) { + scanner_err(s, "failed to expand %%L to the locale Compose file"); + return TOK_ERROR; + } + if (!buf_appends(s, path)) { + free(path); + scanner_err(s, "include path after expanding %%L is too long"); + return TOK_ERROR; + } + free(path); + } + else if (chr(s, 'S')) { + const char *xlocaledir = get_xlocaledir_path(); + if (!buf_appends(s, xlocaledir)) { + scanner_err(s, "include path after expanding %%S is too long"); + return TOK_ERROR; + } + } + else { + scanner_err(s, "unknown %% format (%c) in include statement", peek(s)); + return TOK_ERROR; + } + } else { + buf_append(s, next(s)); + } + } + if (!chr(s, '\"')) { + scanner_err(s, "unterminated include statement"); + return TOK_ERROR; + } + if (!buf_append(s, '\0')) { + scanner_err(s, "include path is too long"); + return TOK_ERROR; + } + val_out->string.str = s->buf; + val_out->string.len = s->buf_pos; + return TOK_INCLUDE_STRING; +} + +struct production { + xkb_keysym_t lhs[MAX_LHS_LEN]; + unsigned int len; + xkb_keysym_t keysym; + char string[256]; + /* At least one of these is true. */ + bool has_keysym; + bool has_string; + + /* The matching is as follows: (active_mods & modmask) == mods. */ + xkb_mod_mask_t modmask; + xkb_mod_mask_t mods; +}; + +static uint32_t +add_node(struct xkb_compose_table *table, xkb_keysym_t keysym) +{ + struct compose_node new = { + .keysym = keysym, + .next = 0, + .is_leaf = true, + }; + darray_append(table->nodes, new); + return darray_size(table->nodes) - 1; +} + +static void +add_production(struct xkb_compose_table *table, struct scanner *s, + const struct production *production) +{ + unsigned lhs_pos; + uint32_t curr; + struct compose_node *node; + + curr = 0; + node = &darray_item(table->nodes, curr); + + /* + * Insert the sequence to the trie, creating new nodes as needed. + * + * TODO: This can be sped up a bit by first trying the path that the + * previous production took, and only then doing the linear search + * through the trie levels. This will work because sequences in the + * Compose files are often clustered by a common prefix; especially + * in the 1st and 2nd keysyms, which is where the largest variation + * (thus, longest search) is. + */ + for (lhs_pos = 0; lhs_pos < production->len; lhs_pos++) { + while (production->lhs[lhs_pos] != node->keysym) { + if (node->next == 0) { + uint32_t next = add_node(table, production->lhs[lhs_pos]); + /* Refetch since add_node could have realloc()ed. */ + node = &darray_item(table->nodes, curr); + node->next = next; + } + + curr = node->next; + node = &darray_item(table->nodes, curr); + } + + if (lhs_pos + 1 == production->len) + break; + + if (node->is_leaf) { + if (node->u.leaf.utf8 != 0 || + node->u.leaf.keysym != XKB_KEY_NoSymbol) { + scanner_warn(s, "a sequence already exists which is a prefix of this sequence; overriding"); + node->u.leaf.utf8 = 0; + node->u.leaf.keysym = XKB_KEY_NoSymbol; + } + + { + uint32_t successor = add_node(table, production->lhs[lhs_pos + 1]); + /* Refetch since add_node could have realloc()ed. */ + node = &darray_item(table->nodes, curr); + node->is_leaf = false; + node->u.successor = successor; + } + } + + curr = node->u.successor; + node = &darray_item(table->nodes, curr); + } + + if (!node->is_leaf) { + scanner_warn(s, "this compose sequence is a prefix of another; skipping line"); + return; + } + + if (node->u.leaf.utf8 != 0 || node->u.leaf.keysym != XKB_KEY_NoSymbol) { + bool same_string = + (node->u.leaf.utf8 == 0 && !production->has_string) || + ( + node->u.leaf.utf8 != 0 && production->has_string && + streq(&darray_item(table->utf8, node->u.leaf.utf8), + production->string) + ); + bool same_keysym = + (node->u.leaf.keysym == XKB_KEY_NoSymbol && !production->has_keysym) || + ( + node->u.leaf.keysym != XKB_KEY_NoSymbol && production->has_keysym && + node->u.leaf.keysym == production->keysym + ); + if (same_string && same_keysym) { + scanner_warn(s, "this compose sequence is a duplicate of another; skipping line"); + return; + } + scanner_warn(s, "this compose sequence already exists; overriding"); + } + + if (production->has_string) { + node->u.leaf.utf8 = darray_size(table->utf8); + darray_append_items(table->utf8, production->string, + strlen(production->string) + 1); + } + if (production->has_keysym) { + node->u.leaf.keysym = production->keysym; + } +} + +/* Should match resolve_modifier(). */ +#define ALL_MODS_MASK ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)) + +static xkb_mod_index_t +resolve_modifier(const char *name) +{ + static const struct { + const char *name; + xkb_mod_index_t mod; + } mods[] = { + { "Shift", 0 }, + { "Ctrl", 2 }, + { "Alt", 3 }, + { "Meta", 3 }, + { "Lock", 1 }, + { "Caps", 1 }, + }; + + for (unsigned i = 0; i < ARRAY_SIZE(mods); i++) + if (streq(name, mods[i].name)) + return mods[i].mod; + + return XKB_MOD_INVALID; +} + +static bool +parse(struct xkb_compose_table *table, struct scanner *s, + unsigned include_depth); + +static bool +do_include(struct xkb_compose_table *table, struct scanner *s, + const char *path, unsigned include_depth) +{ + FILE *file; + bool ok; + char *string; + size_t size; + struct scanner new_s; + + if (include_depth >= MAX_INCLUDE_DEPTH) { + scanner_err(s, "maximum include depth (%d) exceeded; maybe there is an include loop?", + MAX_INCLUDE_DEPTH); + return false; + } + + file = fopen(path, "rb"); + if (!file) { + scanner_err(s, "failed to open included Compose file \"%s\": %s", + path, strerror(errno)); + return false; + } + + ok = map_file(file, &string, &size); + if (!ok) { + scanner_err(s, "failed to read included Compose file \"%s\": %s", + path, strerror(errno)); + goto err_file; + } + + scanner_init(&new_s, table->ctx, string, size, path, s->priv); + + ok = parse(table, &new_s, include_depth + 1); + if (!ok) + goto err_unmap; + +err_unmap: + unmap_file(string, size); +err_file: + fclose(file); + return ok; +} + +static bool +parse(struct xkb_compose_table *table, struct scanner *s, + unsigned include_depth) +{ + enum rules_token tok; + union lvalue val; + xkb_keysym_t keysym; + struct production production; + enum { MAX_ERRORS = 10 }; + int num_errors = 0; + +initial: + production.len = 0; + production.has_keysym = false; + production.has_string = false; + production.mods = 0; + production.modmask = 0; + + /* fallthrough */ + +initial_eol: + switch (tok = lex(s, &val)) { + case TOK_END_OF_LINE: + goto initial_eol; + case TOK_END_OF_FILE: + goto finished; + case TOK_INCLUDE: + goto include; + default: + goto lhs_tok; + } + +include: + switch (tok = lex_include_string(s, table, &val)) { + case TOK_INCLUDE_STRING: + goto include_eol; + default: + goto unexpected; + } + +include_eol: + switch (tok = lex(s, &val)) { + case TOK_END_OF_LINE: + if (!do_include(table, s, val.string.str, include_depth)) + goto fail; + goto initial; + default: + goto unexpected; + } + +lhs: + tok = lex(s, &val); +lhs_tok: + switch (tok) { + case TOK_COLON: + if (production.len <= 0) { + scanner_warn(s, "expected at least one keysym on left-hand side; skipping line"); + goto skip; + } + goto rhs; + case TOK_IDENT: + if (streq(val.string.str, "None")) { + production.mods = 0; + production.modmask = ALL_MODS_MASK; + goto lhs_keysym; + } + goto lhs_mod_list_tok; + case TOK_TILDE: + goto lhs_mod_list_tok; + case TOK_BANG: + production.modmask = ALL_MODS_MASK; + goto lhs_mod_list; + default: + goto lhs_keysym_tok; + } + +lhs_keysym: + tok = lex(s, &val); +lhs_keysym_tok: + switch (tok) { + case TOK_LHS_KEYSYM: + keysym = xkb_keysym_from_name(val.string.str, XKB_KEYSYM_NO_FLAGS); + if (keysym == XKB_KEY_NoSymbol) { + scanner_err(s, "unrecognized keysym \"%s\" on left-hand side", + val.string.str); + goto error; + } + if (production.len + 1 > MAX_LHS_LEN) { + scanner_warn(s, "too many keysyms (%d) on left-hand side; skipping line", + MAX_LHS_LEN + 1); + goto skip; + } + production.lhs[production.len++] = keysym; + production.mods = 0; + production.modmask = 0; + goto lhs; + default: + goto unexpected; + } + +lhs_mod_list: + tok = lex(s, &val); +lhs_mod_list_tok: { + bool tilde = false; + xkb_mod_index_t mod; + + if (tok != TOK_TILDE && tok != TOK_IDENT) + goto lhs_keysym_tok; + + if (tok == TOK_TILDE) { + tilde = true; + tok = lex(s, &val); + } + + if (tok != TOK_IDENT) + goto unexpected; + + mod = resolve_modifier(val.string.str); + if (mod == XKB_MOD_INVALID) { + scanner_err(s, "unrecognized modifier \"%s\"", + val.string.str); + goto error; + } + + production.modmask |= 1 << mod; + if (tilde) + production.mods &= ~(1 << mod); + else + production.mods |= 1 << mod; + + goto lhs_mod_list; + } + +rhs: + switch (tok = lex(s, &val)) { + case TOK_STRING: + if (production.has_string) { + scanner_warn(s, "right-hand side can have at most one string; skipping line"); + goto skip; + } + if (val.string.len <= 0) { + scanner_warn(s, "right-hand side string must not be empty; skipping line"); + goto skip; + } + if (val.string.len >= sizeof(production.string)) { + scanner_warn(s, "right-hand side string is too long; skipping line"); + goto skip; + } + strcpy(production.string, val.string.str); + production.has_string = true; + goto rhs; + case TOK_IDENT: + keysym = xkb_keysym_from_name(val.string.str, XKB_KEYSYM_NO_FLAGS); + if (keysym == XKB_KEY_NoSymbol) { + scanner_err(s, "unrecognized keysym \"%s\" on right-hand side", + val.string.str); + goto error; + } + if (production.has_keysym) { + scanner_warn(s, "right-hand side can have at most one keysym; skipping line"); + goto skip; + } + production.keysym = keysym; + production.has_keysym = true; + /* fallthrough */ + case TOK_END_OF_LINE: + if (!production.has_string && !production.has_keysym) { + scanner_warn(s, "right-hand side must have at least one of string or keysym; skipping line"); + goto skip; + } + add_production(table, s, &production); + goto initial; + default: + goto unexpected; + } + +unexpected: + if (tok != TOK_ERROR) + scanner_err(s, "unexpected token"); +error: + num_errors++; + if (num_errors <= MAX_ERRORS) + goto skip; + + scanner_err(s, "too many errors"); + goto fail; + +fail: + scanner_err(s, "failed to parse file"); + return false; + +skip: + while (tok != TOK_END_OF_LINE && tok != TOK_END_OF_FILE) + tok = lex(s, &val); + goto initial; + +finished: + return true; +} + +bool +parse_string(struct xkb_compose_table *table, const char *string, size_t len, + const char *file_name) +{ + struct scanner s; + scanner_init(&s, table->ctx, string, len, file_name, NULL); + if (!parse(table, &s, 0)) + return false; + /* Maybe the allocator can use the excess space. */ + darray_shrink(table->nodes); + darray_shrink(table->utf8); + return true; +} + +bool +parse_file(struct xkb_compose_table *table, FILE *file, const char *file_name) +{ + bool ok; + char *string; + size_t size; + + ok = map_file(file, &string, &size); + if (!ok) { + log_err(table->ctx, "Couldn't read Compose file %s: %s\n", + file_name, strerror(errno)); + return false; + } + + ok = parse_string(table, string, size, file_name); + unmap_file(string, size); + return ok; +} diff --git a/src/compose/parser.h b/src/compose/parser.h new file mode 100644 index 0000000..3f64a07 --- /dev/null +++ b/src/compose/parser.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2013 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef COMPOSE_PARSER_H +#define COMPOSE_PARSER_H + +bool +parse_string(struct xkb_compose_table *table, + const char *string, size_t len, + const char *file_name); + +bool +parse_file(struct xkb_compose_table *table, + FILE *file, const char *file_name); + +#endif diff --git a/src/compose/paths.c b/src/compose/paths.c new file mode 100644 index 0000000..dab71ac --- /dev/null +++ b/src/compose/paths.c @@ -0,0 +1,215 @@ +/* + * Copyright © 2014 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "utils.h" +#include "paths.h" +#include "utils.h" + +enum resolve_name_direction { + LEFT_TO_RIGHT, + RIGHT_TO_LEFT, +}; + +const char * +get_xlocaledir_path(void) +{ + const char *dir = secure_getenv("XLOCALEDIR"); + if (!dir) + dir = XLOCALEDIR; + return dir; +} + +/* + * Files like compose.dir have the format LEFT: RIGHT. Lookup @name in + * such a file and return its matching value, according to @direction. + * @filename is relative to the xlocaledir. + */ +static char * +resolve_name(const char *filename, enum resolve_name_direction direction, + const char *name) +{ + int ret; + bool ok; + const char *xlocaledir; + char path[512]; + FILE *file; + char *string; + size_t string_size; + const char *end; + const char *s, *left, *right; + char *match; + size_t left_len, right_len, name_len; + + xlocaledir = get_xlocaledir_path(); + + ret = snprintf(path, sizeof(path), "%s/%s", xlocaledir, filename); + if (ret < 0 || (size_t) ret >= sizeof(path)) + return false; + + file = fopen(path, "rb"); + if (!file) + return false; + + ok = map_file(file, &string, &string_size); + fclose(file); + if (!ok) + return false; + + s = string; + end = string + string_size; + name_len = strlen(name); + match = NULL; + + while (s < end) { + /* Skip spaces. */ + while (s < end && is_space(*s)) + s++; + + /* Skip comments. */ + if (s < end && *s == '#') { + while (s < end && *s != '\n') + s++; + continue; + } + + /* Get the left value. */ + left = s; + while (s < end && !is_space(*s) && *s != ':') + s++; + left_len = s - left; + + /* There's an optional colon between left and right. */ + if (s < end && *s == ':') + s++; + + /* Skip spaces. */ + while (s < end && is_space(*s)) + s++; + + /* Get the right value. */ + right = s; + while (s < end && !is_space(*s)) + s++; + right_len = s - right; + + /* Discard rest of line. */ + while (s < end && *s != '\n') + s++; + + if (direction == LEFT_TO_RIGHT) { + if (left_len == name_len && memcmp(left, name, left_len) == 0) { + match = strndup(right, right_len); + break; + } + } + else if (direction == RIGHT_TO_LEFT) { + if (right_len == name_len && memcmp(right, name, right_len) == 0) { + match = strndup(left, left_len); + break; + } + } + } + + unmap_file(string, string_size); + return match; +} + +char * +resolve_locale(const char *locale) +{ + char *alias = resolve_name("locale.alias", LEFT_TO_RIGHT, locale); + return alias ? alias : strdup(locale); +} + +char * +get_xcomposefile_path(void) +{ + return strdup_safe(secure_getenv("XCOMPOSEFILE")); +} + +char * +get_xdg_xcompose_file_path(void) +{ + const char *xdg_config_home; + const char *home; + + xdg_config_home = secure_getenv("XDG_CONFIG_HOME"); + if (!xdg_config_home || xdg_config_home[0] != '/') { + home = secure_getenv("HOME"); + if (!home) + return NULL; + return asprintf_safe("%s/.config/XCompose", home); + } + + return asprintf_safe("%s/XCompose", xdg_config_home); +} + +char * +get_home_xcompose_file_path(void) +{ + const char *home; + + home = secure_getenv("HOME"); + if (!home) + return NULL; + + return asprintf_safe("%s/.XCompose", home); +} + +char * +get_locale_compose_file_path(const char *locale) +{ + char *resolved; + char *path; + + /* + * WARNING: Random workaround ahead. + * + * We currently do not support non-UTF-8 Compose files. The C/POSIX + * locale is specified to be the default fallback locale with an + * ASCII charset. But for some reason the compose.dir points the C + * locale to the iso8859-1/Compose file, which is not ASCII but + * ISO8859-1. Since this is bound to happen a lot, and since our API + * is UTF-8 based, and since 99% of the time a C locale is really just + * a misconfiguration for UTF-8, let's do the most helpful thing. + */ + if (streq(locale, "C")) + locale = "en_US.UTF-8"; + + resolved = resolve_name("compose.dir", RIGHT_TO_LEFT, locale); + if (!resolved) + return NULL; + + if (resolved[0] == '/') { + path = resolved; + } + else { + const char *xlocaledir = get_xlocaledir_path(); + path = asprintf_safe("%s/%s", xlocaledir, resolved); + free(resolved); + } + + return path; +} diff --git a/src/compose/paths.h b/src/compose/paths.h new file mode 100644 index 0000000..bc5150f --- /dev/null +++ b/src/compose/paths.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef COMPOSE_RESOLVE_H +#define COMPOSE_RESOLVE_H + +char * +resolve_locale(const char *locale); + +const char * +get_xlocaledir_path(void); + +char * +get_xcomposefile_path(void); + +char * +get_xdg_xcompose_file_path(void); + +char * +get_home_xcompose_file_path(void); + +char * +get_locale_compose_file_path(const char *locale); + +#endif diff --git a/src/compose/state.c b/src/compose/state.c new file mode 100644 index 0000000..9c64eb4 --- /dev/null +++ b/src/compose/state.c @@ -0,0 +1,198 @@ +/* + * Copyright © 2013 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "table.h" +#include "utils.h" +#include "keysym.h" + +struct xkb_compose_state { + int refcnt; + enum xkb_compose_state_flags flags; + struct xkb_compose_table *table; + + /* + * Offsets into xkb_compose_table::nodes. + * + * They maintain the current and previous position in the trie; see + * xkb_compose_state_feed(). + * + * This is also sufficient for inferring the current status; see + * xkb_compose_state_get_status(). + */ + uint32_t prev_context; + uint32_t context; +}; + +XKB_EXPORT struct xkb_compose_state * +xkb_compose_state_new(struct xkb_compose_table *table, + enum xkb_compose_state_flags flags) +{ + struct xkb_compose_state *state; + + state = calloc(1, sizeof(*state)); + if (!state) + return NULL; + + state->refcnt = 1; + state->table = xkb_compose_table_ref(table); + + state->flags = flags; + state->prev_context = 0; + state->context = 0; + + return state; +} + +XKB_EXPORT struct xkb_compose_state * +xkb_compose_state_ref(struct xkb_compose_state *state) +{ + state->refcnt++; + return state; +} + +XKB_EXPORT void +xkb_compose_state_unref(struct xkb_compose_state *state) +{ + if (!state || --state->refcnt > 0) + return; + + xkb_compose_table_unref(state->table); + free(state); +} + +XKB_EXPORT struct xkb_compose_table * +xkb_compose_state_get_compose_table(struct xkb_compose_state *state) +{ + return state->table; +} + +XKB_EXPORT enum xkb_compose_feed_result +xkb_compose_state_feed(struct xkb_compose_state *state, xkb_keysym_t keysym) +{ + uint32_t context; + const struct compose_node *node; + + /* + * Modifiers do not affect the sequence directly. In particular, + * they do not cancel a sequence; otherwise it'd be impossible to + * have a sequence like <dead_acute><A> (needs Shift in the middle). + * + * The following test is not really accurate - in order to test if + * a key is "modifier key", we really need the keymap, but we don't + * have it here. However, this is (approximately) what libX11 does + * as well. + */ + if (xkb_keysym_is_modifier(keysym)) + return XKB_COMPOSE_FEED_IGNORED; + + node = &darray_item(state->table->nodes, state->context); + + context = (node->is_leaf ? 0 : node->u.successor); + node = &darray_item(state->table->nodes, context); + + while (node->keysym != keysym && node->next != 0) { + context = node->next; + node = &darray_item(state->table->nodes, context); + } + + if (node->keysym != keysym) + context = 0; + + state->prev_context = state->context; + state->context = context; + return XKB_COMPOSE_FEED_ACCEPTED; +} + +XKB_EXPORT void +xkb_compose_state_reset(struct xkb_compose_state *state) +{ + state->prev_context = 0; + state->context = 0; +} + +XKB_EXPORT enum xkb_compose_status +xkb_compose_state_get_status(struct xkb_compose_state *state) +{ + const struct compose_node *prev_node, *node; + + prev_node = &darray_item(state->table->nodes, state->prev_context); + node = &darray_item(state->table->nodes, state->context); + + if (state->context == 0 && !prev_node->is_leaf) + return XKB_COMPOSE_CANCELLED; + + if (state->context == 0) + return XKB_COMPOSE_NOTHING; + + if (!node->is_leaf) + return XKB_COMPOSE_COMPOSING; + + return XKB_COMPOSE_COMPOSED; +} + +XKB_EXPORT int +xkb_compose_state_get_utf8(struct xkb_compose_state *state, + char *buffer, size_t size) +{ + const struct compose_node *node = + &darray_item(state->table->nodes, state->context); + + if (!node->is_leaf) + goto fail; + + /* If there's no string specified, but only a keysym, try to do the + * most helpful thing. */ + if (node->u.leaf.utf8 == 0 && node->u.leaf.keysym != XKB_KEY_NoSymbol) { + char name[64]; + int ret; + + ret = xkb_keysym_to_utf8(node->u.leaf.keysym, name, sizeof(name)); + if (ret < 0 || ret == 0) { + /* ret < 0 is impossible. + * ret == 0 means the keysym has no string representation. */ + goto fail; + } + + return snprintf(buffer, size, "%s", name); + } + + return snprintf(buffer, size, "%s", + &darray_item(state->table->utf8, node->u.leaf.utf8)); + +fail: + if (size > 0) + buffer[0] = '\0'; + return 0; +} + +XKB_EXPORT xkb_keysym_t +xkb_compose_state_get_one_sym(struct xkb_compose_state *state) +{ + const struct compose_node *node = + &darray_item(state->table->nodes, state->context); + if (!node->is_leaf) + return XKB_KEY_NoSymbol; + return node->u.leaf.keysym; +} diff --git a/src/compose/table.c b/src/compose/table.c new file mode 100644 index 0000000..38d4406 --- /dev/null +++ b/src/compose/table.c @@ -0,0 +1,228 @@ +/* + * Copyright © 2013 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "utils.h" +#include "table.h" +#include "parser.h" +#include "paths.h" + +static struct xkb_compose_table * +xkb_compose_table_new(struct xkb_context *ctx, + const char *locale, + enum xkb_compose_format format, + enum xkb_compose_compile_flags flags) +{ + char *resolved_locale; + struct xkb_compose_table *table; + struct compose_node root; + + resolved_locale = resolve_locale(locale); + if (!resolved_locale) + return NULL; + + table = calloc(1, sizeof(*table)); + if (!table) { + free(resolved_locale); + return NULL; + } + + table->refcnt = 1; + table->ctx = xkb_context_ref(ctx); + + table->locale = resolved_locale; + table->format = format; + table->flags = flags; + + darray_init(table->nodes); + darray_init(table->utf8); + + root.keysym = XKB_KEY_NoSymbol; + root.next = 0; + root.is_leaf = true; + root.u.leaf.utf8 = 0; + root.u.leaf.keysym = XKB_KEY_NoSymbol; + darray_append(table->nodes, root); + + darray_append(table->utf8, '\0'); + + return table; +} + +XKB_EXPORT struct xkb_compose_table * +xkb_compose_table_ref(struct xkb_compose_table *table) +{ + table->refcnt++; + return table; +} + +XKB_EXPORT void +xkb_compose_table_unref(struct xkb_compose_table *table) +{ + if (!table || --table->refcnt > 0) + return; + free(table->locale); + darray_free(table->nodes); + darray_free(table->utf8); + xkb_context_unref(table->ctx); + free(table); +} + +XKB_EXPORT struct xkb_compose_table * +xkb_compose_table_new_from_file(struct xkb_context *ctx, + FILE *file, + const char *locale, + enum xkb_compose_format format, + enum xkb_compose_compile_flags flags) +{ + struct xkb_compose_table *table; + bool ok; + + if (flags & ~(XKB_COMPOSE_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + if (format != XKB_COMPOSE_FORMAT_TEXT_V1) { + log_err_func(ctx, "unsupported compose format: %d\n", format); + return NULL; + } + + table = xkb_compose_table_new(ctx, locale, format, flags); + if (!table) + return NULL; + + ok = parse_file(table, file, "(unknown file)"); + if (!ok) { + xkb_compose_table_unref(table); + return NULL; + } + + return table; +} + +XKB_EXPORT struct xkb_compose_table * +xkb_compose_table_new_from_buffer(struct xkb_context *ctx, + const char *buffer, size_t length, + const char *locale, + enum xkb_compose_format format, + enum xkb_compose_compile_flags flags) +{ + struct xkb_compose_table *table; + bool ok; + + if (flags & ~(XKB_COMPOSE_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + if (format != XKB_COMPOSE_FORMAT_TEXT_V1) { + log_err_func(ctx, "unsupported compose format: %d\n", format); + return NULL; + } + + table = xkb_compose_table_new(ctx, locale, format, flags); + if (!table) + return NULL; + + ok = parse_string(table, buffer, length, "(input string)"); + if (!ok) { + xkb_compose_table_unref(table); + return NULL; + } + + return table; +} + +XKB_EXPORT struct xkb_compose_table * +xkb_compose_table_new_from_locale(struct xkb_context *ctx, + const char *locale, + enum xkb_compose_compile_flags flags) +{ + struct xkb_compose_table *table; + char *path; + FILE *file; + bool ok; + + if (flags & ~(XKB_COMPOSE_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + table = xkb_compose_table_new(ctx, locale, XKB_COMPOSE_FORMAT_TEXT_V1, + flags); + if (!table) + return NULL; + + path = get_xcomposefile_path(); + if (path) { + file = fopen(path, "rb"); + if (file) + goto found_path; + } + free(path); + + path = get_xdg_xcompose_file_path(); + if (path) { + file = fopen(path, "rb"); + if (file) + goto found_path; + } + free(path); + + path = get_home_xcompose_file_path(); + if (path) { + file = fopen(path, "rb"); + if (file) + goto found_path; + } + free(path); + + path = get_locale_compose_file_path(table->locale); + if (path) { + file = fopen(path, "rb"); + if (file) + goto found_path; + } + free(path); + + log_err(ctx, "couldn't find a Compose file for locale \"%s\"\n", locale); + xkb_compose_table_unref(table); + return NULL; + +found_path: + ok = parse_file(table, file, path); + fclose(file); + if (!ok) { + free(path); + xkb_compose_table_unref(table); + return NULL; + } + + log_dbg(ctx, "created compose table from locale %s with path %s\n", + table->locale, path); + + free(path); + return table; +} diff --git a/src/compose/table.h b/src/compose/table.h new file mode 100644 index 0000000..05a415f --- /dev/null +++ b/src/compose/table.h @@ -0,0 +1,100 @@ +/* + * Copyright © 2013 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef COMPOSE_COMPOSE_H +#define COMPOSE_COMPOSE_H + +#include "xkbcommon/xkbcommon-compose.h" +#include "utils.h" +#include "context.h" + +/* + * The compose table data structure is a simple trie. An example will + * help. Given these sequences: + * + * <A> <B> : "first" dead_a + * <A> <C> <D> : "second" dead_b + * <E> <F> : "third" dead_c + * + * the trie would look like: + * + * [root] ---> [<A>] -----------------> [<E>] -# + * | | | + * # v v + * [<B>] ---> [<C>] -# [<F>] -# + * | | - + * # v # + * [<D>] -# + * | + * # + * where: + * - [root] is a special empty root node. + * - [<X>] is a node for a sequence keysym <X>. + * - right arrows are `next` pointers. + * - down arrows are `successor` pointers. + * - # is a nil pointer. + * + * The nodes are all kept in a contiguous array. Pointers are represented + * as integer offsets into this array. A nil pointer is represented as 0 + * (which, helpfully, is the offset of the empty root node). + * + * Nodes without a successor are leaf nodes. Since a sequence cannot be a + * prefix of another, these are exactly the nodes which terminate the + * sequences (in a bijective manner). + * + * A leaf contains the result data of its sequence. The result keysym is + * contained in the node struct itself; the result UTF-8 string is a byte + * offset into an array of the form "\0first\0second\0third" (the initial + * \0 is so offset 0 points to an empty string). + */ + +struct compose_node { + xkb_keysym_t keysym; + /* Offset into xkb_compose_table::nodes. */ + unsigned int next:31; + bool is_leaf:1; + + union { + /* Offset into xkb_compose_table::nodes. */ + uint32_t successor; + struct { + /* Offset into xkb_compose_table::utf8. */ + uint32_t utf8; + xkb_keysym_t keysym; + } leaf; + } u; +}; + +struct xkb_compose_table { + int refcnt; + enum xkb_compose_format format; + enum xkb_compose_compile_flags flags; + struct xkb_context *ctx; + + char *locale; + + darray_char utf8; + darray(struct compose_node) nodes; +}; + +#endif diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..4edf0e9 --- /dev/null +++ b/src/config.h @@ -0,0 +1,59 @@ +/* + * Autogenerated by the Meson build system. + * Do not edit, your changes will be lost. + */ + +#pragma once + +#define DEFAULT_XKB_LAYOUT "us" + +#define DEFAULT_XKB_MODEL "pc105" + +#define DEFAULT_XKB_OPTIONS NULL + +#define DEFAULT_XKB_RULES "evdev" + +#define DEFAULT_XKB_VARIANT NULL + +#define DFLT_XKB_CONFIG_EXTRA_PATH "/usr/local/etc/xkb" + +#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb" + +#define EXIT_INVALID_USAGE 2 + +#define HAVE_ASPRINTF 1 + +#define HAVE_MKOSTEMP 1 + +#define HAVE_MMAP 1 + +#define HAVE_POSIX_FALLOCATE 1 + +#define HAVE_STRNDUP 1 + +#define HAVE_XKBCLI_COMPILE_KEYMAP 1 + +#define HAVE_XKBCLI_HOW_TO_TYPE 1 + +#define HAVE_XKBCLI_INTERACTIVE_EVDEV 1 + +#define HAVE_XKBCLI_LIST 1 + +#define HAVE___BUILTIN_EXPECT 1 + +#define LIBXKBCOMMON_TOOL_PATH "/usr/local/libexec/xkbcommon" + +#define LIBXKBCOMMON_VERSION "1.0.0" + +#define WIN32_LEAN_AND_MEAN 1 + +#define XLOCALEDIR "/usr/local/share/X11/locale" + +#define _CRT_NONSTDC_NO_DEPRECATE 1 + +#define _CRT_NONSTDC_NO_WARNINGS 1 + +#define _CRT_SECURE_NO_WARNINGS 1 + +#define _GNU_SOURCE 1 + diff --git a/src/context-priv.c b/src/context-priv.c new file mode 100644 index 0000000..8eaa579 --- /dev/null +++ b/src/context-priv.c @@ -0,0 +1,177 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include "xkbcommon/xkbcommon.h" +#include "utils.h" +#include "context.h" + +unsigned int +xkb_context_num_failed_include_paths(struct xkb_context *ctx) +{ + return darray_size(ctx->failed_includes); +} + +const char * +xkb_context_failed_include_path_get(struct xkb_context *ctx, + unsigned int idx) +{ + if (idx >= xkb_context_num_failed_include_paths(ctx)) + return NULL; + + return darray_item(ctx->failed_includes, idx); +} + +xkb_atom_t +xkb_atom_lookup(struct xkb_context *ctx, const char *string) +{ + return atom_intern(ctx->atom_table, string, strlen(string), false); +} + +xkb_atom_t +xkb_atom_intern(struct xkb_context *ctx, const char *string, size_t len) +{ + return atom_intern(ctx->atom_table, string, len, true); +} + +const char * +xkb_atom_text(struct xkb_context *ctx, xkb_atom_t atom) +{ + return atom_text(ctx->atom_table, atom); +} + +void +xkb_log(struct xkb_context *ctx, enum xkb_log_level level, int verbosity, + const char *fmt, ...) +{ + va_list args; + + if (ctx->log_level < level || ctx->log_verbosity < verbosity) + return; + + va_start(args, fmt); + ctx->log_fn(ctx, level, fmt, args); + va_end(args); +} + +char * +xkb_context_get_buffer(struct xkb_context *ctx, size_t size) +{ + char *rtrn; + + if (size >= sizeof(ctx->text_buffer)) + return NULL; + + if (sizeof(ctx->text_buffer) - ctx->text_next <= size) + ctx->text_next = 0; + + rtrn = &ctx->text_buffer[ctx->text_next]; + ctx->text_next += size; + + return rtrn; +} + +static const char * +xkb_context_get_default_rules(struct xkb_context *ctx) +{ + const char *env = NULL; + + if (ctx->use_environment_names) + env = secure_getenv("XKB_DEFAULT_RULES"); + + return env ? env : DEFAULT_XKB_RULES; +} + +static const char * +xkb_context_get_default_model(struct xkb_context *ctx) +{ + const char *env = NULL; + + if (ctx->use_environment_names) + env = secure_getenv("XKB_DEFAULT_MODEL"); + + return env ? env : DEFAULT_XKB_MODEL; +} + +static const char * +xkb_context_get_default_layout(struct xkb_context *ctx) +{ + const char *env = NULL; + + if (ctx->use_environment_names) + env = secure_getenv("XKB_DEFAULT_LAYOUT"); + + return env ? env : DEFAULT_XKB_LAYOUT; +} + +static const char * +xkb_context_get_default_variant(struct xkb_context *ctx) +{ + const char *env = NULL; + const char *layout = secure_getenv("XKB_DEFAULT_LAYOUT"); + + /* We don't want to inherit the variant if they haven't also set a + * layout, since they're so closely paired. */ + if (layout && ctx->use_environment_names) + env = secure_getenv("XKB_DEFAULT_VARIANT"); + + return env ? env : DEFAULT_XKB_VARIANT; +} + +static const char * +xkb_context_get_default_options(struct xkb_context *ctx) +{ + const char *env = NULL; + + if (ctx->use_environment_names) + env = secure_getenv("XKB_DEFAULT_OPTIONS"); + + return env ? env : DEFAULT_XKB_OPTIONS; +} + +void +xkb_context_sanitize_rule_names(struct xkb_context *ctx, + struct xkb_rule_names *rmlvo) +{ + if (isempty(rmlvo->rules)) + rmlvo->rules = xkb_context_get_default_rules(ctx); + if (isempty(rmlvo->model)) + rmlvo->model = xkb_context_get_default_model(ctx); + /* Layout and variant are tied together, so don't try to use one from + * the caller and one from the environment. */ + if (isempty(rmlvo->layout)) { + rmlvo->layout = xkb_context_get_default_layout(ctx); + rmlvo->variant = xkb_context_get_default_variant(ctx); + } + /* Options can be empty, so respect that if passed in. */ + if (rmlvo->options == NULL) + rmlvo->options = xkb_context_get_default_options(ctx); +} diff --git a/src/context.c b/src/context.c new file mode 100644 index 0000000..4a6ac8e --- /dev/null +++ b/src/context.c @@ -0,0 +1,374 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#ifdef _MSC_VER +# include <direct.h> +# include <io.h> +# ifndef S_ISDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# endif +#else +# include <unistd.h> +#endif + +#include "xkbcommon/xkbcommon.h" +#include "utils.h" +#include "context.h" + +/** + * Append one directory to the context's include path. + */ +XKB_EXPORT int +xkb_context_include_path_append(struct xkb_context *ctx, const char *path) +{ + struct stat stat_buf; + int err = ENOMEM; + char *tmp; + + tmp = strdup(path); + if (!tmp) + goto err; + + err = stat(path, &stat_buf); + if (err != 0) { + err = errno; + goto err; + } + if (!S_ISDIR(stat_buf.st_mode)) { + err = ENOTDIR; + goto err; + } + + if (!check_eaccess(path, R_OK | X_OK)) { + err = EACCES; + goto err; + } + + darray_append(ctx->includes, tmp); + log_dbg(ctx, "Include path added: %s\n", tmp); + + return 1; + +err: + darray_append(ctx->failed_includes, tmp); + log_dbg(ctx, "Include path failed: %s (%s)\n", tmp, strerror(err)); + return 0; +} + +const char * +xkb_context_include_path_get_extra_path(struct xkb_context *ctx) +{ + const char *extra = secure_getenv("XKB_CONFIG_EXTRA_PATH"); + return extra ? extra : DFLT_XKB_CONFIG_EXTRA_PATH; +} + +const char * +xkb_context_include_path_get_system_path(struct xkb_context *ctx) +{ + const char *root = secure_getenv("XKB_CONFIG_ROOT"); + return root ? root : DFLT_XKB_CONFIG_ROOT; +} + +/** + * Append the default include directories to the context. + */ +XKB_EXPORT int +xkb_context_include_path_append_default(struct xkb_context *ctx) +{ + const char *home, *xdg, *root, *extra; + char *user_path; + int ret = 0; + + home = secure_getenv("HOME"); + + xdg = secure_getenv("XDG_CONFIG_HOME"); + if (xdg != NULL) { + user_path = asprintf_safe("%s/xkb", xdg); + if (user_path) { + ret |= xkb_context_include_path_append(ctx, user_path); + free(user_path); + } + } else if (home != NULL) { + /* XDG_CONFIG_HOME fallback is $HOME/.config/ */ + user_path = asprintf_safe("%s/.config/xkb", home); + if (user_path) { + ret |= xkb_context_include_path_append(ctx, user_path); + free(user_path); + } + } + + if (home != NULL) { + user_path = asprintf_safe("%s/.xkb", home); + if (user_path) { + ret |= xkb_context_include_path_append(ctx, user_path); + free(user_path); + } + } + + extra = xkb_context_include_path_get_extra_path(ctx); + ret |= xkb_context_include_path_append(ctx, extra); + root = xkb_context_include_path_get_system_path(ctx); + ret |= xkb_context_include_path_append(ctx, root); + + return ret; +} + +/** + * Remove all entries in the context's include path. + */ +XKB_EXPORT void +xkb_context_include_path_clear(struct xkb_context *ctx) +{ + char **path; + + darray_foreach(path, ctx->includes) + free(*path); + darray_free(ctx->includes); + + darray_foreach(path, ctx->failed_includes) + free(*path); + darray_free(ctx->failed_includes); +} + +/** + * xkb_context_include_path_clear() + xkb_context_include_path_append_default() + */ +XKB_EXPORT int +xkb_context_include_path_reset_defaults(struct xkb_context *ctx) +{ + xkb_context_include_path_clear(ctx); + return xkb_context_include_path_append_default(ctx); +} + +/** + * Returns the number of entries in the context's include path. + */ +XKB_EXPORT unsigned int +xkb_context_num_include_paths(struct xkb_context *ctx) +{ + return darray_size(ctx->includes); +} + +/** + * Returns the given entry in the context's include path, or NULL if an + * invalid index is passed. + */ +XKB_EXPORT const char * +xkb_context_include_path_get(struct xkb_context *ctx, unsigned int idx) +{ + if (idx >= xkb_context_num_include_paths(ctx)) + return NULL; + + return darray_item(ctx->includes, idx); +} + +/** + * Take a new reference on the context. + */ +XKB_EXPORT struct xkb_context * +xkb_context_ref(struct xkb_context *ctx) +{ + ctx->refcnt++; + return ctx; +} + +/** + * Drop an existing reference on the context, and free it if the refcnt is + * now 0. + */ +XKB_EXPORT void +xkb_context_unref(struct xkb_context *ctx) +{ + if (!ctx || --ctx->refcnt > 0) + return; + + xkb_context_include_path_clear(ctx); + atom_table_free(ctx->atom_table); + free(ctx); +} + +static const char * +log_level_to_prefix(enum xkb_log_level level) +{ + switch (level) { + case XKB_LOG_LEVEL_DEBUG: + return "xkbcommon: DEBUG: "; + case XKB_LOG_LEVEL_INFO: + return "xkbcommon: INFO: "; + case XKB_LOG_LEVEL_WARNING: + return "xkbcommon: WARNING: "; + case XKB_LOG_LEVEL_ERROR: + return "xkbcommon: ERROR: "; + case XKB_LOG_LEVEL_CRITICAL: + return "xkbcommon: CRITICAL: "; + default: + return NULL; + } +} + +ATTR_PRINTF(3, 0) static void +default_log_fn(struct xkb_context *ctx, enum xkb_log_level level, + const char *fmt, va_list args) +{ + const char *prefix = log_level_to_prefix(level); + + if (prefix) + fprintf(stderr, "%s", prefix); + vfprintf(stderr, fmt, args); +} + +static enum xkb_log_level +log_level(const char *level) { + char *endptr; + enum xkb_log_level lvl; + + errno = 0; + lvl = strtol(level, &endptr, 10); + if (errno == 0 && (endptr[0] == '\0' || is_space(endptr[0]))) + return lvl; + if (istreq_prefix("crit", level)) + return XKB_LOG_LEVEL_CRITICAL; + if (istreq_prefix("err", level)) + return XKB_LOG_LEVEL_ERROR; + if (istreq_prefix("warn", level)) + return XKB_LOG_LEVEL_WARNING; + if (istreq_prefix("info", level)) + return XKB_LOG_LEVEL_INFO; + if (istreq_prefix("debug", level) || istreq_prefix("dbg", level)) + return XKB_LOG_LEVEL_DEBUG; + + return XKB_LOG_LEVEL_ERROR; +} + +static int +log_verbosity(const char *verbosity) { + char *endptr; + int v; + + errno = 0; + v = strtol(verbosity, &endptr, 10); + if (errno == 0) + return v; + + return 0; +} + +/** + * Create a new context. + */ +XKB_EXPORT struct xkb_context * +xkb_context_new(enum xkb_context_flags flags) +{ + const char *env; + struct xkb_context *ctx = calloc(1, sizeof(*ctx)); + + if (!ctx) + return NULL; + + ctx->refcnt = 1; + ctx->log_fn = default_log_fn; + ctx->log_level = XKB_LOG_LEVEL_ERROR; + ctx->log_verbosity = 0; + + /* Environment overwrites defaults. */ + env = secure_getenv("XKB_LOG_LEVEL"); + if (env) + xkb_context_set_log_level(ctx, log_level(env)); + + env = secure_getenv("XKB_LOG_VERBOSITY"); + if (env) + xkb_context_set_log_verbosity(ctx, log_verbosity(env)); + + if (!(flags & XKB_CONTEXT_NO_DEFAULT_INCLUDES) && + !xkb_context_include_path_append_default(ctx)) { + log_err(ctx, "failed to add default include path %s\n", + DFLT_XKB_CONFIG_ROOT); + xkb_context_unref(ctx); + return NULL; + } + + ctx->use_environment_names = !(flags & XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + + ctx->atom_table = atom_table_new(); + if (!ctx->atom_table) { + xkb_context_unref(ctx); + return NULL; + } + + return ctx; +} + +XKB_EXPORT void +xkb_context_set_log_fn(struct xkb_context *ctx, + void (*log_fn)(struct xkb_context *ctx, + enum xkb_log_level level, + const char *fmt, va_list args)) +{ + ctx->log_fn = (log_fn ? log_fn : default_log_fn); +} + +XKB_EXPORT enum xkb_log_level +xkb_context_get_log_level(struct xkb_context *ctx) +{ + return ctx->log_level; +} + +XKB_EXPORT void +xkb_context_set_log_level(struct xkb_context *ctx, enum xkb_log_level level) +{ + ctx->log_level = level; +} + +XKB_EXPORT int +xkb_context_get_log_verbosity(struct xkb_context *ctx) +{ + return ctx->log_verbosity; +} + +XKB_EXPORT void +xkb_context_set_log_verbosity(struct xkb_context *ctx, int verbosity) +{ + ctx->log_verbosity = verbosity; +} + +XKB_EXPORT void * +xkb_context_get_user_data(struct xkb_context *ctx) +{ + if (ctx) + return ctx->user_data; + return NULL; +} + +XKB_EXPORT void +xkb_context_set_user_data(struct xkb_context *ctx, void *user_data) +{ + ctx->user_data = user_data; +} diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..ead2508 --- /dev/null +++ b/src/context.h @@ -0,0 +1,133 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "atom.h" + +struct xkb_context { + int refcnt; + + ATTR_PRINTF(3, 0) void (*log_fn)(struct xkb_context *ctx, + enum xkb_log_level level, + const char *fmt, va_list args); + enum xkb_log_level log_level; + int log_verbosity; + void *user_data; + + struct xkb_rule_names names_dflt; + + darray(char *) includes; + darray(char *) failed_includes; + + struct atom_table *atom_table; + + /* Buffer for the *Text() functions. */ + char text_buffer[2048]; + size_t text_next; + + unsigned int use_environment_names : 1; +}; + +unsigned int +xkb_context_num_failed_include_paths(struct xkb_context *ctx); + +const char * +xkb_context_failed_include_path_get(struct xkb_context *ctx, + unsigned int idx); + +const char * +xkb_context_include_path_get_extra_path(struct xkb_context *ctx); + +const char * +xkb_context_include_path_get_system_path(struct xkb_context *ctx); + +/* + * Returns XKB_ATOM_NONE if @string was not previously interned, + * otherwise returns the atom. + */ +xkb_atom_t +xkb_atom_lookup(struct xkb_context *ctx, const char *string); + +xkb_atom_t +xkb_atom_intern(struct xkb_context *ctx, const char *string, size_t len); + +#define xkb_atom_intern_literal(ctx, literal) \ + xkb_atom_intern((ctx), (literal), sizeof(literal) - 1) + +/** + * If @string is dynamically allocated, NUL-terminated, free'd immediately + * after being interned, and not used afterwards, use this function + * instead of xkb_atom_intern to avoid some unnecessary allocations. + * The caller should not use or free the passed in string afterwards. + */ +xkb_atom_t +xkb_atom_steal(struct xkb_context *ctx, char *string); + +const char * +xkb_atom_text(struct xkb_context *ctx, xkb_atom_t atom); + +char * +xkb_context_get_buffer(struct xkb_context *ctx, size_t size); + +ATTR_PRINTF(4, 5) void +xkb_log(struct xkb_context *ctx, enum xkb_log_level level, int verbosity, + const char *fmt, ...); + +void +xkb_context_sanitize_rule_names(struct xkb_context *ctx, + struct xkb_rule_names *rmlvo); + +/* + * The format is not part of the argument list in order to avoid the + * "ISO C99 requires rest arguments to be used" warning when only the + * format is supplied without arguments. Not supplying it would still + * result in an error, though. + */ +#define log_dbg(ctx, ...) \ + xkb_log((ctx), XKB_LOG_LEVEL_DEBUG, 0, __VA_ARGS__) +#define log_info(ctx, ...) \ + xkb_log((ctx), XKB_LOG_LEVEL_INFO, 0, __VA_ARGS__) +#define log_warn(ctx, ...) \ + xkb_log((ctx), XKB_LOG_LEVEL_WARNING, 0, __VA_ARGS__) +#define log_err(ctx, ...) \ + xkb_log((ctx), XKB_LOG_LEVEL_ERROR, 0, __VA_ARGS__) +#define log_wsgo(ctx, ...) \ + xkb_log((ctx), XKB_LOG_LEVEL_CRITICAL, 0, __VA_ARGS__) +#define log_vrb(ctx, vrb, ...) \ + xkb_log((ctx), XKB_LOG_LEVEL_WARNING, (vrb), __VA_ARGS__) + +/* + * Variants which are prefixed by the name of the function they're + * called from. + * Here we must have the silly 1 variant. + */ +#define log_err_func(ctx, fmt, ...) \ + log_err(ctx, "%s: " fmt, __func__, __VA_ARGS__) +#define log_err_func1(ctx, fmt) \ + log_err(ctx, "%s: " fmt, __func__) + +#endif diff --git a/src/darray.h b/src/darray.h new file mode 100644 index 0000000..de659cc --- /dev/null +++ b/src/darray.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2011 Joseph Adams <joeyadams3.14159@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef CCAN_DARRAY_H +#define CCAN_DARRAY_H + +/* Originally taken from: https://ccodearchive.net/info/darray.html + * But modified for libxkbcommon. */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <limits.h> + +#define darray(type) struct { type *item; unsigned size; unsigned alloc; } + +#define darray_new() { 0, 0, 0 } + +#define darray_init(arr) do { \ + (arr).item = 0; (arr).size = 0; (arr).alloc = 0; \ +} while (0) + +#define darray_free(arr) do { \ + free((arr).item); \ + darray_init(arr); \ +} while (0) + +#define darray_steal(arr, to, to_size) do { \ + *(to) = (arr).item; \ + if (to_size) \ + *(unsigned int *) (to_size) = (arr).size; \ + darray_init(arr); \ +} while (0) + +/* + * Typedefs for darrays of common types. These are useful + * when you want to pass a pointer to an darray(T) around. + * + * The following will produce an incompatible pointer warning: + * + * void foo(darray(int) *arr); + * darray(int) arr = darray_new(); + * foo(&arr); + * + * The workaround: + * + * void foo(darray_int *arr); + * darray_int arr = darray_new(); + * foo(&arr); + */ + +typedef darray (char) darray_char; +typedef darray (signed char) darray_schar; +typedef darray (unsigned char) darray_uchar; + +typedef darray (short) darray_short; +typedef darray (int) darray_int; +typedef darray (long) darray_long; + +typedef darray (unsigned short) darray_ushort; +typedef darray (unsigned int) darray_uint; +typedef darray (unsigned long) darray_ulong; + +/*** Access ***/ + +#define darray_item(arr, i) ((arr).item[i]) +#define darray_size(arr) ((arr).size) +#define darray_empty(arr) ((arr).size == 0) + +/*** Insertion (single item) ***/ + +#define darray_append(arr, ...) do { \ + darray_resize(arr, (arr).size + 1); \ + (arr).item[(arr).size - 1] = (__VA_ARGS__); \ +} while (0) + +/*** Insertion (multiple items) ***/ + +#define darray_append_items(arr, items, count) do { \ + unsigned __count = (count), __oldSize = (arr).size; \ + darray_resize(arr, __oldSize + __count); \ + memcpy((arr).item + __oldSize, items, __count * sizeof(*(arr).item)); \ +} while (0) + +#define darray_from_items(arr, items, count) do { \ + unsigned __count = (count); \ + darray_resize(arr, __count); \ + if (__count != 0) \ + memcpy((arr).item, items, __count * sizeof(*(arr).item)); \ +} while (0) + +#define darray_copy(arr_to, arr_from) \ + darray_from_items((arr_to), (arr_from).item, (arr_from).size) + +#define darray_concat(arr_to, arr_from) \ + darray_append_items((arr_to), (arr_from).item, (arr_from).size) + +/*** String buffer ***/ + +#define darray_append_string(arr, str) do { \ + const char *__str = (str); \ + darray_append_items(arr, __str, strlen(__str) + 1); \ + (arr).size--; \ +} while (0) + +#define darray_append_lit(arr, stringLiteral) do { \ + darray_append_items(arr, stringLiteral, sizeof(stringLiteral)); \ + (arr).size--; \ +} while (0) + +#define darray_appends_nullterminate(arr, items, count) do { \ + unsigned __count = (count), __oldSize = (arr).size; \ + darray_resize(arr, __oldSize + __count + 1); \ + memcpy((arr).item + __oldSize, items, __count * sizeof(*(arr).item)); \ + (arr).item[--(arr).size] = 0; \ +} while (0) + +#define darray_prepends_nullterminate(arr, items, count) do { \ + unsigned __count = (count), __oldSize = (arr).size; \ + darray_resize(arr, __count + __oldSize + 1); \ + memmove((arr).item + __count, (arr).item, \ + __oldSize * sizeof(*(arr).item)); \ + memcpy((arr).item, items, __count * sizeof(*(arr).item)); \ + (arr).item[--(arr).size] = 0; \ +} while (0) + +/*** Size management ***/ + +#define darray_resize(arr, newSize) \ + darray_growalloc(arr, (arr).size = (newSize)) + +#define darray_resize0(arr, newSize) do { \ + unsigned __oldSize = (arr).size, __newSize = (newSize); \ + (arr).size = __newSize; \ + if (__newSize > __oldSize) { \ + darray_growalloc(arr, __newSize); \ + memset(&(arr).item[__oldSize], 0, \ + (__newSize - __oldSize) * sizeof(*(arr).item)); \ + } \ +} while (0) + +#define darray_realloc(arr, newAlloc) do { \ + (arr).item = realloc((arr).item, \ + ((arr).alloc = (newAlloc)) * sizeof(*(arr).item)); \ +} while (0) + +#define darray_growalloc(arr, need) do { \ + unsigned __need = (need); \ + if (__need > (arr).alloc) \ + darray_realloc(arr, darray_next_alloc((arr).alloc, __need, \ + sizeof(*(arr).item))); \ +} while (0) + +#define darray_shrink(arr) do { \ + if ((arr).size > 0) \ + (arr).item = realloc((arr).item, \ + ((arr).alloc = (arr).size) * sizeof(*(arr).item)); \ +} while (0) + +static inline unsigned +darray_next_alloc(unsigned alloc, unsigned need, unsigned itemSize) +{ + assert(need < UINT_MAX / itemSize / 2); /* Overflow. */ + if (alloc == 0) + alloc = 4; + while (alloc < need) + alloc *= 2; + return alloc; +} + +/*** Traversal ***/ + +#define darray_foreach(i, arr) \ + for ((i) = &(arr).item[0]; (i) < &(arr).item[(arr).size]; (i)++) + +#define darray_foreach_from(i, arr, from) \ + for ((i) = &(arr).item[from]; (i) < &(arr).item[(arr).size]; (i)++) + +/* Iterate on index and value at the same time, like Python's enumerate. */ +#define darray_enumerate(idx, val, arr) \ + for ((idx) = 0, (val) = &(arr).item[0]; \ + (idx) < (arr).size; \ + (idx)++, (val)++) + +#define darray_enumerate_from(idx, val, arr, from) \ + for ((idx) = (from), (val) = &(arr).item[0]; \ + (idx) < (arr).size; \ + (idx)++, (val)++) + +#define darray_foreach_reverse(i, arr) \ + for ((i) = &(arr).item[(arr).size - 1]; (arr).size > 0 && (i) >= &(arr).item[0]; (i)--) + +#endif /* CCAN_DARRAY_H */ diff --git a/src/keymap-priv.c b/src/keymap-priv.c new file mode 100644 index 0000000..8fdaf5b --- /dev/null +++ b/src/keymap-priv.c @@ -0,0 +1,152 @@ +/** + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include "keymap.h" + +static void +update_builtin_keymap_fields(struct xkb_keymap *keymap) +{ + /* Predefined (AKA real, core, X11) modifiers. The order is important! */ + static const char *const builtin_mods[] = { + [0] = "Shift", + [1] = "Lock", + [2] = "Control", + [3] = "Mod1", + [4] = "Mod2", + [5] = "Mod3", + [6] = "Mod4", + [7] = "Mod5" + }; + + for (unsigned i = 0; i < ARRAY_SIZE(builtin_mods); i++) { + keymap->mods.mods[i].name = xkb_atom_intern(keymap->ctx, + builtin_mods[i], + strlen(builtin_mods[i])); + keymap->mods.mods[i].type = MOD_REAL; + } + keymap->mods.num_mods = ARRAY_SIZE(builtin_mods); +} + +struct xkb_keymap * +xkb_keymap_new(struct xkb_context *ctx, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap *keymap; + + keymap = calloc(1, sizeof(*keymap)); + if (!keymap) + return NULL; + + keymap->refcnt = 1; + keymap->ctx = xkb_context_ref(ctx); + + keymap->format = format; + keymap->flags = flags; + + update_builtin_keymap_fields(keymap); + + return keymap; +} + +struct xkb_key * +XkbKeyByName(struct xkb_keymap *keymap, xkb_atom_t name, bool use_aliases) +{ + struct xkb_key *key; + + xkb_keys_foreach(key, keymap) + if (key->name == name) + return key; + + if (use_aliases) { + xkb_atom_t new_name = XkbResolveKeyAlias(keymap, name); + if (new_name != XKB_ATOM_NONE) + return XkbKeyByName(keymap, new_name, false); + } + + return NULL; +} + +xkb_atom_t +XkbResolveKeyAlias(const struct xkb_keymap *keymap, xkb_atom_t name) +{ + for (unsigned i = 0; i < keymap->num_key_aliases; i++) + if (keymap->key_aliases[i].alias == name) + return keymap->key_aliases[i].real; + + return XKB_ATOM_NONE; +} + +void +XkbEscapeMapName(char *name) +{ + /* + * All latin-1 alphanumerics, plus parens, slash, minus, underscore and + * wildcards. + */ + static const unsigned char legal[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0xff, 0x83, + 0xfe, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff + }; + + if (!name) + return; + + while (*name) { + unsigned char c = *name; + if (!(legal[c / 8] & (1 << (c % 8)))) + *name = '_'; + name++; + } +} + +xkb_mod_index_t +XkbModNameToIndex(const struct xkb_mod_set *mods, xkb_atom_t name, + enum mod_type type) +{ + xkb_mod_index_t i; + const struct xkb_mod *mod; + + xkb_mods_enumerate(i, mod, mods) + if ((mod->type & type) && name == mod->name) + return i; + + return XKB_MOD_INVALID; +} + +bool +XkbLevelsSameSyms(const struct xkb_level *a, const struct xkb_level *b) +{ + if (a->num_syms != b->num_syms) + return false; + if (a->num_syms <= 1) + return a->u.sym == b->u.sym; + return memcmp(a->u.syms, b->u.syms, sizeof(*a->u.syms) * a->num_syms) == 0; +} diff --git a/src/keymap.c b/src/keymap.c new file mode 100644 index 0000000..d2baf94 --- /dev/null +++ b/src/keymap.c @@ -0,0 +1,581 @@ +/** + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +/************************************************************ + * Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * ********************************************************/ + +#include "config.h" + +#include "keymap.h" +#include "text.h" + +XKB_EXPORT struct xkb_keymap * +xkb_keymap_ref(struct xkb_keymap *keymap) +{ + keymap->refcnt++; + return keymap; +} + +XKB_EXPORT void +xkb_keymap_unref(struct xkb_keymap *keymap) +{ + if (!keymap || --keymap->refcnt > 0) + return; + + if (keymap->keys) { + struct xkb_key *key; + xkb_keys_foreach(key, keymap) { + if (key->groups) { + for (unsigned i = 0; i < key->num_groups; i++) { + if (key->groups[i].levels) { + for (unsigned j = 0; j < XkbKeyNumLevels(key, i); j++) + if (key->groups[i].levels[j].num_syms > 1) + free(key->groups[i].levels[j].u.syms); + free(key->groups[i].levels); + } + } + free(key->groups); + } + } + free(keymap->keys); + } + if (keymap->types) { + for (unsigned i = 0; i < keymap->num_types; i++) { + free(keymap->types[i].entries); + free(keymap->types[i].level_names); + } + free(keymap->types); + } + free(keymap->sym_interprets); + free(keymap->key_aliases); + free(keymap->group_names); + free(keymap->keycodes_section_name); + free(keymap->symbols_section_name); + free(keymap->types_section_name); + free(keymap->compat_section_name); + xkb_context_unref(keymap->ctx); + free(keymap); +} + +static const struct xkb_keymap_format_ops * +get_keymap_format_ops(enum xkb_keymap_format format) +{ + static const struct xkb_keymap_format_ops *keymap_format_ops[] = { + [XKB_KEYMAP_FORMAT_TEXT_V1] = &text_v1_keymap_format_ops, + }; + + if ((int) format < 0 || (int) format >= (int) ARRAY_SIZE(keymap_format_ops)) + return NULL; + + return keymap_format_ops[(int) format]; +} + +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_names(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo_in, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap *keymap; + struct xkb_rule_names rmlvo; + const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1; + const struct xkb_keymap_format_ops *ops; + + ops = get_keymap_format_ops(format); + if (!ops || !ops->keymap_new_from_names) { + log_err_func(ctx, "unsupported keymap format: %d\n", format); + return NULL; + } + + if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + keymap = xkb_keymap_new(ctx, format, flags); + if (!keymap) + return NULL; + + if (rmlvo_in) + rmlvo = *rmlvo_in; + else + memset(&rmlvo, 0, sizeof(rmlvo)); + xkb_context_sanitize_rule_names(ctx, &rmlvo); + + if (!ops->keymap_new_from_names(keymap, &rmlvo)) { + xkb_keymap_unref(keymap); + return NULL; + } + + return keymap; +} + +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_string(struct xkb_context *ctx, + const char *string, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + return xkb_keymap_new_from_buffer(ctx, string, strlen(string), + format, flags); +} + +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_buffer(struct xkb_context *ctx, + const char *buffer, size_t length, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap *keymap; + const struct xkb_keymap_format_ops *ops; + + ops = get_keymap_format_ops(format); + if (!ops || !ops->keymap_new_from_string) { + log_err_func(ctx, "unsupported keymap format: %d\n", format); + return NULL; + } + + if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + if (!buffer) { + log_err_func1(ctx, "no buffer specified\n"); + return NULL; + } + + keymap = xkb_keymap_new(ctx, format, flags); + if (!keymap) + return NULL; + + if (!ops->keymap_new_from_string(keymap, buffer, length)) { + xkb_keymap_unref(keymap); + return NULL; + } + + return keymap; +} + +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_file(struct xkb_context *ctx, + FILE *file, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap *keymap; + const struct xkb_keymap_format_ops *ops; + + ops = get_keymap_format_ops(format); + if (!ops || !ops->keymap_new_from_file) { + log_err_func(ctx, "unsupported keymap format: %d\n", format); + return NULL; + } + + if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + if (!file) { + log_err_func1(ctx, "no file specified\n"); + return NULL; + } + + keymap = xkb_keymap_new(ctx, format, flags); + if (!keymap) + return NULL; + + if (!ops->keymap_new_from_file(keymap, file)) { + xkb_keymap_unref(keymap); + return NULL; + } + + return keymap; +} + +XKB_EXPORT char * +xkb_keymap_get_as_string(struct xkb_keymap *keymap, + enum xkb_keymap_format format) +{ + const struct xkb_keymap_format_ops *ops; + + if (format == XKB_KEYMAP_USE_ORIGINAL_FORMAT) + format = keymap->format; + + ops = get_keymap_format_ops(format); + if (!ops || !ops->keymap_get_as_string) { + log_err_func(keymap->ctx, "unsupported keymap format: %d\n", format); + return NULL; + } + + return ops->keymap_get_as_string(keymap); +} + +/** + * Returns the total number of modifiers active in the keymap. + */ +XKB_EXPORT xkb_mod_index_t +xkb_keymap_num_mods(struct xkb_keymap *keymap) +{ + return keymap->mods.num_mods; +} + +/** + * Return the name for a given modifier. + */ +XKB_EXPORT const char * +xkb_keymap_mod_get_name(struct xkb_keymap *keymap, xkb_mod_index_t idx) +{ + if (idx >= keymap->mods.num_mods) + return NULL; + + return xkb_atom_text(keymap->ctx, keymap->mods.mods[idx].name); +} + +/** + * Returns the index for a named modifier. + */ +XKB_EXPORT xkb_mod_index_t +xkb_keymap_mod_get_index(struct xkb_keymap *keymap, const char *name) +{ + xkb_atom_t atom; + + atom = xkb_atom_lookup(keymap->ctx, name); + if (atom == XKB_ATOM_NONE) + return XKB_MOD_INVALID; + + return XkbModNameToIndex(&keymap->mods, atom, MOD_BOTH); +} + +/** + * Return the total number of active groups in the keymap. + */ +XKB_EXPORT xkb_layout_index_t +xkb_keymap_num_layouts(struct xkb_keymap *keymap) +{ + return keymap->num_groups; +} + +/** + * Returns the name for a given group. + */ +XKB_EXPORT const char * +xkb_keymap_layout_get_name(struct xkb_keymap *keymap, xkb_layout_index_t idx) +{ + if (idx >= keymap->num_group_names) + return NULL; + + return xkb_atom_text(keymap->ctx, keymap->group_names[idx]); +} + +/** + * Returns the index for a named layout. + */ +XKB_EXPORT xkb_layout_index_t +xkb_keymap_layout_get_index(struct xkb_keymap *keymap, const char *name) +{ + xkb_atom_t atom = xkb_atom_lookup(keymap->ctx, name); + xkb_layout_index_t i; + + if (atom == XKB_ATOM_NONE) + return XKB_LAYOUT_INVALID; + + for (i = 0; i < keymap->num_group_names; i++) + if (keymap->group_names[i] == atom) + return i; + + return XKB_LAYOUT_INVALID; +} + +/** + * Returns the number of layouts active for a particular key. + */ +XKB_EXPORT xkb_layout_index_t +xkb_keymap_num_layouts_for_key(struct xkb_keymap *keymap, xkb_keycode_t kc) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + + if (!key) + return 0; + + return key->num_groups; +} + +/** + * Returns the number of levels active for a particular key and layout. + */ +XKB_EXPORT xkb_level_index_t +xkb_keymap_num_levels_for_key(struct xkb_keymap *keymap, xkb_keycode_t kc, + xkb_layout_index_t layout) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + + if (!key) + return 0; + + layout = XkbWrapGroupIntoRange(layout, key->num_groups, + key->out_of_range_group_action, + key->out_of_range_group_number); + if (layout == XKB_LAYOUT_INVALID) + return 0; + + return XkbKeyNumLevels(key, layout); +} + +/** + * Return the total number of LEDs in the keymap. + */ +XKB_EXPORT xkb_led_index_t +xkb_keymap_num_leds(struct xkb_keymap *keymap) +{ + return keymap->num_leds; +} + +/** + * Returns the name for a given LED. + */ +XKB_EXPORT const char * +xkb_keymap_led_get_name(struct xkb_keymap *keymap, xkb_led_index_t idx) +{ + if (idx >= keymap->num_leds) + return NULL; + + return xkb_atom_text(keymap->ctx, keymap->leds[idx].name); +} + +/** + * Returns the index for a named LED. + */ +XKB_EXPORT xkb_led_index_t +xkb_keymap_led_get_index(struct xkb_keymap *keymap, const char *name) +{ + xkb_atom_t atom = xkb_atom_lookup(keymap->ctx, name); + xkb_led_index_t i; + const struct xkb_led *led; + + if (atom == XKB_ATOM_NONE) + return XKB_LED_INVALID; + + xkb_leds_enumerate(i, led, keymap) + if (led->name == atom) + return i; + + return XKB_LED_INVALID; +} + +XKB_EXPORT size_t +xkb_keymap_key_get_mods_for_level(struct xkb_keymap *keymap, + xkb_keycode_t kc, + xkb_layout_index_t layout, + xkb_level_index_t level, + xkb_mod_mask_t *masks_out, + size_t masks_size) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + if (!key) + return 0; + + layout = XkbWrapGroupIntoRange(layout, key->num_groups, + key->out_of_range_group_action, + key->out_of_range_group_number); + if (layout == XKB_LAYOUT_INVALID) + return 0; + + if (level >= XkbKeyNumLevels(key, layout)) + return 0; + + const struct xkb_key_type *type = key->groups[layout].type; + + size_t count = 0; + + /* + * If the active set of modifiers doesn't match any explicit entry of + * the key type, the resulting level is 0 (i.e. Level 1). + * So, if we are asked to find the modifiers for level==0, we can offer + * an ~infinite supply, which is not very workable. + * What we do instead, is special case the empty set of modifiers for + * this purpose. If the empty set isn't explicit mapped to a level, we + * take it to map to Level 1. + * This is almost always what we want. If applicable, given it priority + * over other ways to generate the level. + */ + if (level == 0) { + bool empty_mapped = false; + for (unsigned i = 0; i < type->num_entries && count < masks_size; i++) + if (entry_is_active(&type->entries[i]) && + type->entries[i].mods.mask == 0) { + empty_mapped = true; + break; + } + if (!empty_mapped && count < masks_size) { + masks_out[count++] = 0; + } + } + + /* Now search explicit mappings. */ + for (unsigned i = 0; i < type->num_entries && count < masks_size; i++) { + if (entry_is_active(&type->entries[i]) && + type->entries[i].level == level) { + masks_out[count++] = type->entries[i].mods.mask; + } + } + + return count; +} + +/** + * As below, but takes an explicit layout/level rather than state. + */ +XKB_EXPORT int +xkb_keymap_key_get_syms_by_level(struct xkb_keymap *keymap, + xkb_keycode_t kc, + xkb_layout_index_t layout, + xkb_level_index_t level, + const xkb_keysym_t **syms_out) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + int num_syms; + + if (!key) + goto err; + + layout = XkbWrapGroupIntoRange(layout, key->num_groups, + key->out_of_range_group_action, + key->out_of_range_group_number); + if (layout == XKB_LAYOUT_INVALID) + goto err; + + if (level >= XkbKeyNumLevels(key, layout)) + goto err; + + num_syms = key->groups[layout].levels[level].num_syms; + if (num_syms == 0) + goto err; + + if (num_syms == 1) + *syms_out = &key->groups[layout].levels[level].u.sym; + else + *syms_out = key->groups[layout].levels[level].u.syms; + + return num_syms; + +err: + *syms_out = NULL; + return 0; +} + +XKB_EXPORT xkb_keycode_t +xkb_keymap_min_keycode(struct xkb_keymap *keymap) +{ + return keymap->min_key_code; +} + +XKB_EXPORT xkb_keycode_t +xkb_keymap_max_keycode(struct xkb_keymap *keymap) +{ + return keymap->max_key_code; +} + +XKB_EXPORT void +xkb_keymap_key_for_each(struct xkb_keymap *keymap, xkb_keymap_key_iter_t iter, + void *data) +{ + struct xkb_key *key; + + xkb_keys_foreach(key, keymap) + iter(keymap, key->keycode, data); +} + +XKB_EXPORT const char * +xkb_keymap_key_get_name(struct xkb_keymap *keymap, xkb_keycode_t kc) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + + if (!key) + return NULL; + + return xkb_atom_text(keymap->ctx, key->name); +} + +XKB_EXPORT xkb_keycode_t +xkb_keymap_key_by_name(struct xkb_keymap *keymap, const char *name) +{ + struct xkb_key *key; + xkb_atom_t atom; + + atom = xkb_atom_lookup(keymap->ctx, name); + if (atom) { + xkb_atom_t ratom = XkbResolveKeyAlias(keymap, atom); + if (ratom) + atom = ratom; + } + if (!atom) + return XKB_KEYCODE_INVALID; + + xkb_keys_foreach(key, keymap) { + if (key->name == atom) + return key->keycode; + } + + return XKB_KEYCODE_INVALID; +} + +/** + * Simple boolean specifying whether or not the key should repeat. + */ +XKB_EXPORT int +xkb_keymap_key_repeats(struct xkb_keymap *keymap, xkb_keycode_t kc) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + + if (!key) + return 0; + + return key->repeats; +} diff --git a/src/keymap.h b/src/keymap.h new file mode 100644 index 0000000..7c5341d --- /dev/null +++ b/src/keymap.h @@ -0,0 +1,493 @@ +/* + * Copyright 1985, 1987, 1990, 1998 The Open Group + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or their + * institutions shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization from the authors. + */ + +/************************************************************ + * Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2009 Dan Nicholson + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Dan Nicholson <dbn.lists@gmail.com> + */ + +#ifndef KEYMAP_H +#define KEYMAP_H + + /* Don't use compat names in internal code. */ +#define _XKBCOMMON_COMPAT_H +#include "xkbcommon/xkbcommon.h" + +#include "utils.h" +#include "context.h" + +/* This limit is artificially enforced, we do not depend on it any where. + * The reason it's still here is that the rules file format does not + * support multiple groups very well, and the rules shipped with + * xkeyboard-config (see rules/evdev) depend on this limit extensively. + * So just lifting this limit would cause problems for people who will use + * more than 4 layouts. + * TODO: Fix the group index syntax in the rules format, preferably in a + * backwards compatible way. + * See e.g. https://bugs.freedesktop.org/show_bug.cgi?id=14372 + * Note: A limit on the number of groups we *do* depend on is imposed by + * the size of the xkb_layout_mask_t type (32). This is more than enough + * though. + */ +#define XKB_MAX_GROUPS 4 + +/* Don't allow more modifiers than we can hold in xkb_mod_mask_t. */ +#define XKB_MAX_MODS ((xkb_mod_index_t) (sizeof(xkb_mod_mask_t) * 8)) + +/* Don't allow more leds than we can hold in xkb_led_mask_t. */ +#define XKB_MAX_LEDS ((xkb_led_index_t) (sizeof(xkb_led_mask_t) * 8)) + +/* These should all go away. */ +enum mod_type { + MOD_REAL = (1 << 0), + MOD_VIRT = (1 << 1), + MOD_BOTH = (MOD_REAL | MOD_VIRT), +}; +#define MOD_REAL_MASK_ALL ((xkb_mod_mask_t) 0x000000ff) + +enum xkb_action_type { + ACTION_TYPE_NONE = 0, + ACTION_TYPE_MOD_SET, + ACTION_TYPE_MOD_LATCH, + ACTION_TYPE_MOD_LOCK, + ACTION_TYPE_GROUP_SET, + ACTION_TYPE_GROUP_LATCH, + ACTION_TYPE_GROUP_LOCK, + ACTION_TYPE_PTR_MOVE, + ACTION_TYPE_PTR_BUTTON, + ACTION_TYPE_PTR_LOCK, + ACTION_TYPE_PTR_DEFAULT, + ACTION_TYPE_TERMINATE, + ACTION_TYPE_SWITCH_VT, + ACTION_TYPE_CTRL_SET, + ACTION_TYPE_CTRL_LOCK, + ACTION_TYPE_PRIVATE, + _ACTION_TYPE_NUM_ENTRIES +}; + +enum xkb_action_flags { + ACTION_LOCK_CLEAR = (1 << 0), + ACTION_LATCH_TO_LOCK = (1 << 1), + ACTION_LOCK_NO_LOCK = (1 << 2), + ACTION_LOCK_NO_UNLOCK = (1 << 3), + ACTION_MODS_LOOKUP_MODMAP = (1 << 4), + ACTION_ABSOLUTE_SWITCH = (1 << 5), + ACTION_ABSOLUTE_X = (1 << 6), + ACTION_ABSOLUTE_Y = (1 << 7), + ACTION_ACCEL = (1 << 8), + ACTION_SAME_SCREEN = (1 << 9), +}; + +enum xkb_action_controls { + CONTROL_REPEAT = (1 << 0), + CONTROL_SLOW = (1 << 1), + CONTROL_DEBOUNCE = (1 << 2), + CONTROL_STICKY = (1 << 3), + CONTROL_MOUSEKEYS = (1 << 4), + CONTROL_MOUSEKEYS_ACCEL = (1 << 5), + CONTROL_AX = (1 << 6), + CONTROL_AX_TIMEOUT = (1 << 7), + CONTROL_AX_FEEDBACK = (1 << 8), + CONTROL_BELL = (1 << 9), + CONTROL_IGNORE_GROUP_LOCK = (1 << 10), + CONTROL_ALL = \ + (CONTROL_REPEAT | CONTROL_SLOW | CONTROL_DEBOUNCE | CONTROL_STICKY | \ + CONTROL_MOUSEKEYS | CONTROL_MOUSEKEYS_ACCEL | CONTROL_AX | \ + CONTROL_AX_TIMEOUT | CONTROL_AX_FEEDBACK | CONTROL_BELL | \ + CONTROL_IGNORE_GROUP_LOCK) +}; + +enum xkb_match_operation { + MATCH_NONE, + MATCH_ANY_OR_NONE, + MATCH_ANY, + MATCH_ALL, + MATCH_EXACTLY, +}; + +struct xkb_mods { + xkb_mod_mask_t mods; /* original real+virtual mods in definition */ + xkb_mod_mask_t mask; /* computed effective mask */ +}; + +struct xkb_mod_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + struct xkb_mods mods; +}; + +struct xkb_group_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + int32_t group; +}; + +struct xkb_controls_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + enum xkb_action_controls ctrls; +}; + +struct xkb_pointer_default_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + int8_t value; +}; + +struct xkb_switch_screen_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + int8_t screen; +}; + +struct xkb_pointer_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + int16_t x; + int16_t y; +}; + +struct xkb_pointer_button_action { + enum xkb_action_type type; + enum xkb_action_flags flags; + uint8_t count; + uint8_t button; +}; + +struct xkb_private_action { + enum xkb_action_type type; + uint8_t data[7]; +}; + +union xkb_action { + enum xkb_action_type type; + struct xkb_mod_action mods; + struct xkb_group_action group; + struct xkb_controls_action ctrls; + struct xkb_pointer_default_action dflt; + struct xkb_switch_screen_action screen; + struct xkb_pointer_action ptr; + struct xkb_pointer_button_action btn; + struct xkb_private_action priv; +}; + +struct xkb_key_type_entry { + xkb_level_index_t level; + struct xkb_mods mods; + struct xkb_mods preserve; +}; + +struct xkb_key_type { + xkb_atom_t name; + struct xkb_mods mods; + xkb_level_index_t num_levels; + unsigned int num_level_names; + xkb_atom_t *level_names; + unsigned int num_entries; + struct xkb_key_type_entry *entries; +}; + +struct xkb_sym_interpret { + xkb_keysym_t sym; + enum xkb_match_operation match; + xkb_mod_mask_t mods; + xkb_mod_index_t virtual_mod; + union xkb_action action; + bool level_one_only; + bool repeat; +}; + +struct xkb_led { + xkb_atom_t name; + enum xkb_state_component which_groups; + xkb_layout_mask_t groups; + enum xkb_state_component which_mods; + struct xkb_mods mods; + enum xkb_action_controls ctrls; +}; + +struct xkb_key_alias { + xkb_atom_t real; + xkb_atom_t alias; +}; + +struct xkb_controls { + unsigned char groups_wrap; + struct xkb_mods internal; + struct xkb_mods ignore_lock; + unsigned short repeat_delay; + unsigned short repeat_interval; + unsigned short slow_keys_delay; + unsigned short debounce_delay; + unsigned short ax_options; + unsigned short ax_timeout; + unsigned short axt_opts_mask; + unsigned short axt_opts_values; + unsigned int axt_ctrls_mask; + unsigned int axt_ctrls_values; +}; + +/* Such an awkward name. Oh well. */ +enum xkb_range_exceed_type { + RANGE_WRAP = 0, + RANGE_SATURATE, + RANGE_REDIRECT, +}; + +enum xkb_explicit_components { + EXPLICIT_INTERP = (1 << 0), + EXPLICIT_VMODMAP = (1 << 1), + EXPLICIT_REPEAT = (1 << 2), +}; + +struct xkb_level { + union xkb_action action; + unsigned int num_syms; + union { + xkb_keysym_t sym; /* num_syms == 1 */ + xkb_keysym_t *syms; /* num_syms > 1 */ + } u; +}; + +struct xkb_group { + bool explicit_type; + /* Points to a type in keymap->types. */ + const struct xkb_key_type *type; + /* Use XkbKeyNumLevels for the number of levels. */ + struct xkb_level *levels; +}; + +struct xkb_key { + xkb_keycode_t keycode; + xkb_atom_t name; + + enum xkb_explicit_components explicit; + + xkb_mod_mask_t modmap; + xkb_mod_mask_t vmodmap; + + bool repeats; + + enum xkb_range_exceed_type out_of_range_group_action; + xkb_layout_index_t out_of_range_group_number; + + xkb_layout_index_t num_groups; + struct xkb_group *groups; +}; + +struct xkb_mod { + xkb_atom_t name; + enum mod_type type; + xkb_mod_mask_t mapping; /* vmod -> real mod mapping */ +}; + +struct xkb_mod_set { + struct xkb_mod mods[XKB_MAX_MODS]; + unsigned int num_mods; +}; + +/* Common keyboard description structure */ +struct xkb_keymap { + struct xkb_context *ctx; + + int refcnt; + enum xkb_keymap_compile_flags flags; + enum xkb_keymap_format format; + + enum xkb_action_controls enabled_ctrls; + + xkb_keycode_t min_key_code; + xkb_keycode_t max_key_code; + struct xkb_key *keys; + + /* aliases in no particular order */ + unsigned int num_key_aliases; + struct xkb_key_alias *key_aliases; + + struct xkb_key_type *types; + unsigned int num_types; + + unsigned int num_sym_interprets; + struct xkb_sym_interpret *sym_interprets; + + struct xkb_mod_set mods; + + /* Number of groups in the key with the most groups. */ + xkb_layout_index_t num_groups; + /* Not all groups must have names. */ + xkb_layout_index_t num_group_names; + xkb_atom_t *group_names; + + struct xkb_led leds[XKB_MAX_LEDS]; + unsigned int num_leds; + + char *keycodes_section_name; + char *symbols_section_name; + char *types_section_name; + char *compat_section_name; +}; + +#define xkb_keys_foreach(iter, keymap) \ + for ((iter) = (keymap)->keys + (keymap)->min_key_code; \ + (iter) <= (keymap)->keys + (keymap)->max_key_code; \ + (iter)++) + +#define xkb_mods_foreach(iter, mods_) \ + for ((iter) = (mods_)->mods; \ + (iter) < (mods_)->mods + (mods_)->num_mods; \ + (iter)++) + +#define xkb_mods_enumerate(idx, iter, mods_) \ + for ((idx) = 0, (iter) = (mods_)->mods; \ + (idx) < (mods_)->num_mods; \ + (idx)++, (iter)++) + +#define xkb_leds_foreach(iter, keymap) \ + for ((iter) = (keymap)->leds; \ + (iter) < (keymap)->leds + (keymap)->num_leds; \ + (iter)++) + +#define xkb_leds_enumerate(idx, iter, keymap) \ + for ((idx) = 0, (iter) = (keymap)->leds; \ + (idx) < (keymap)->num_leds; \ + (idx)++, (iter)++) + +static inline const struct xkb_key * +XkbKey(struct xkb_keymap *keymap, xkb_keycode_t kc) +{ + if (kc < keymap->min_key_code || kc > keymap->max_key_code) + return NULL; + return &keymap->keys[kc]; +} + +static inline xkb_level_index_t +XkbKeyNumLevels(const struct xkb_key *key, xkb_layout_index_t layout) +{ + return key->groups[layout].type->num_levels; +} + +/* + * If the virtual modifiers are not bound to anything, the entry + * is not active and should be skipped. xserver does this with + * cached entry->active field. + */ +static inline bool +entry_is_active(const struct xkb_key_type_entry *entry) +{ + return entry->mods.mods == 0 || entry->mods.mask != 0; +} + +struct xkb_keymap * +xkb_keymap_new(struct xkb_context *ctx, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags); + +struct xkb_key * +XkbKeyByName(struct xkb_keymap *keymap, xkb_atom_t name, bool use_aliases); + +xkb_atom_t +XkbResolveKeyAlias(const struct xkb_keymap *keymap, xkb_atom_t name); + +void +XkbEscapeMapName(char *name); + +xkb_mod_index_t +XkbModNameToIndex(const struct xkb_mod_set *mods, xkb_atom_t name, + enum mod_type type); + +bool +XkbLevelsSameSyms(const struct xkb_level *a, const struct xkb_level *b); + +xkb_layout_index_t +XkbWrapGroupIntoRange(int32_t group, + xkb_layout_index_t num_groups, + enum xkb_range_exceed_type out_of_range_group_action, + xkb_layout_index_t out_of_range_group_number); + +xkb_mod_mask_t +mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods); + +struct xkb_keymap_format_ops { + bool (*keymap_new_from_names)(struct xkb_keymap *keymap, + const struct xkb_rule_names *names); + bool (*keymap_new_from_string)(struct xkb_keymap *keymap, + const char *string, size_t length); + bool (*keymap_new_from_file)(struct xkb_keymap *keymap, FILE *file); + char *(*keymap_get_as_string)(struct xkb_keymap *keymap); +}; + +extern const struct xkb_keymap_format_ops text_v1_keymap_format_ops; + +#endif diff --git a/src/keysym-utf.c b/src/keysym-utf.c new file mode 100644 index 0000000..a49944a --- /dev/null +++ b/src/keysym-utf.c @@ -0,0 +1,968 @@ +/* The table and comments below along with the function xkb_keysym_to_ucs4 + * are under the public domain and are derived as described below. + */ +/* This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * keysym2ucs() maps a keysym onto a Unicode value using a binary search, + * therefore keysymtab[] must remain SORTED by keysym value. + * + * The keysym -> UTF-8 conversion will hopefully one day be provided + * by Xlib via XmbLookupString() and should ideally not have to be + * done in X applications. But we are not there yet. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * NOTE: The comments in the table below contain the actual character + * encoded in UTF-8, so for viewing and editing best use an editor in + * UTF-8 mode. + * + * Author: Markus G. Kuhn <http://www.cl.cam.ac.uk/~mgk25/>, + * University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + * + */ + +#include "config.h" + +#include "xkbcommon/xkbcommon.h" +#include "utils.h" +#include "utf8.h" + +/* We don't use the uint32_t types here, to save some space. */ +struct codepair { + uint16_t keysym; + uint16_t ucs; +}; + +static const struct codepair keysymtab[] = { + { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ + { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ + { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ + { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ + { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ + { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ + { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ + { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ + { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ + { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ + { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ + { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ + { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ + { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ + { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ + { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ + { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ + { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ + { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ + { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ + { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ + { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ + { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ + { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ + { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ + { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ + { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ + { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ + { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ + { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ + { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ + { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ + { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ + { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ + { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ + { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ + { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ + { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ + { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ + { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ + { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ + { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ + { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ + { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ + { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ + { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ + { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ + { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ + { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ + { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ + { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ + { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ + { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ + { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ + { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ + { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ + { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ + { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ + { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ + { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ + { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ + { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ + { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ + { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ + { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ + { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ + { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ + { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ + { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ + { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ + { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ + { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ + { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ + { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ + { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ + { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ + { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ + { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ + { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ + { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ + { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ + { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ + { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ + { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ + { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ + { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ + { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ + { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ + { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ + { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ + { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ + { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ + { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ + { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ + { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ + { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ + { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ + { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ + { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ + { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ + { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ + { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ + { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ + { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ + { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ + { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ + { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ + { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ + { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ + { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ + { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ + { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ + { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ + { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ + { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ + { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ + { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ + { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ + { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ + { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ + { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ + { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ + { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ + { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ + { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ + { 0x06ad, 0x0491 }, /* Ukrainian_ghe_with_upturn ґ CYRILLIC SMALL LETTER GHE WITH UPTURN */ + { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ + { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ + { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ + { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ + { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ + { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ + { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ + { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ + { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ + { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ + { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ + { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ + { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ + { 0x06bd, 0x0490 }, /* Ukrainian_GHE_WITH_UPTURN Ґ CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ + { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ + { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ + { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ + { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ + { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ + { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ + { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ + { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ + { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ + { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ + { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ + { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ + { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ + { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ + { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ + { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ + { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ + { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ + { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ + { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ + { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ + { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ + { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ + { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ + { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ + { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ + { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ + { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ + { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ + { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ + { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ + { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ + { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ + { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ + { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ + { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ + { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ + { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ + { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ + { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ + { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ + { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ + { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ + { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ + { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ + { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ + { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ + { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ + { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ + { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ + { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ + { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ + { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ + { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ + { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ + { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ + { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ + { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ + { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ + { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ + { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ + { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ + { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ + { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ + { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ + { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ + { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ + { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ + { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ + { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ + { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ + { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ + { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ + { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ + { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ + { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ + { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ + { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ + { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ + { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ + { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ + { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ + { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ + { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ + { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ + { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ + { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ + { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ + { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ + { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ + { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ + { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ + { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ + { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ + { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ + { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ + { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ + { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ + { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ + { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ + { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ + { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ + { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ + { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ + { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ + { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ + { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ + { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ + { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ + { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ + { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ + { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ + { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ + { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ + { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ + { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ + { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ + { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ + { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ + { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ + { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ + { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ + { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ + { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ + /* 0x08b1 topleftsummation ? ??? */ + /* 0x08b2 botleftsummation ? ??? */ + /* 0x08b3 topvertsummationconnector ? ??? */ + /* 0x08b4 botvertsummationconnector ? ??? */ + /* 0x08b5 toprightsummation ? ??? */ + /* 0x08b6 botrightsummation ? ??? */ + /* 0x08b7 rightmiddlesummation ? ??? */ + { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ + { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ + { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ + { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ + { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ + { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ + { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ + { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ + { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ + { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ + { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ + { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ + { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x08dd, 0x222a }, /* union ∪ UNION */ + { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ + { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ + { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ + { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ +/* 0x09df blank ? ??? */ + { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ + { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ + { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e8, 0x2424 }, /* nl  SYMBOL FOR NEWLINE */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ + { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ + { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ + { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ + { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x0aa1, 0x2003 }, /* emspace EM SPACE */ + { 0x0aa2, 0x2002 }, /* enspace EN SPACE */ + { 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ + { 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ + { 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ + { 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ + { 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ + { 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ + { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ + { 0x0aaa, 0x2013 }, /* endash – EN DASH */ + { 0x0aac, 0x2423 }, /* signifblank ␣ OPEN BOX */ + { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ + { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ + { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ + { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ + { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ + { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ + { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ + { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ + { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ + { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ + { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ + { 0x0abc, 0x27e8 }, /* leftanglebracket ⟨ MATHEMATICAL LEFT ANGLE BRACKET */ + { 0x0abd, 0x002e }, /* decimalpoint . FULL STOP */ + { 0x0abe, 0x27e9 }, /* rightanglebracket ⟩ MATHEMATICAL RIGHT ANGLE BRACKET */ + /* 0x0abf marker ? ??? */ + { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ + { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ + { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ + { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ + { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ + /* 0x0acb trademarkincircle ? ??? */ + { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ + { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ + { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ + { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ + { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ + { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ + { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ + { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ + { 0x0ad5, 0x2030 }, /* permille ‰ PER MILLE SIGN */ + { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ + { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ + { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ + /* 0x0ada hexagram ? ??? */ + { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ + { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ + { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ + { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ + { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ + { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ + { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ + { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ + { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ + { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ + { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ + { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ + { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ + { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ + { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0af1, 0x2020 }, /* dagger † DAGGER */ + { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ + { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ + { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ + { 0x0afc, 0x2038 }, /* caret ‸ CARET */ + { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ + { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ + /* 0x0aff cursor ? ??? */ + { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ + { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ + { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x0bc2, 0x22a4 }, /* downtack ⊤ DOWN TACK */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ + { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ + { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ + { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ + { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD (Unicode 3.0) */ + { 0x0bce, 0x22a5 }, /* uptack ⊥ UP TACK */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ + { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ + { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ + { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ + { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ + { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ + { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ + { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ + { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ + { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ + { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ + { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ + { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ + { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ + { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ + { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ + { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ + { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ + { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ + { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ + { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ + { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ + { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ + { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ + { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ + { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ + { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ + { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ + { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ + { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ + { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ + { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ + { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ + { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ + { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ + { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ + { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ + { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ + { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ + { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ + { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ + { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ + { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ + { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ + { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ + { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ + { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ + { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ + { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ + { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ + { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ + { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ + { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ + { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ + { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ + { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ + { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ + { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ + { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ + { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ + { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ + { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ + { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ + { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ + { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ + { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ + { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ + { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ + { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ + { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ + { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ + { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ + { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ + { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ + { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ + { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ + { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ + { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ + { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ + { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ + { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ + { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ + { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ + { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ + { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ + { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ + { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ + { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ + { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ + { 0x0dde, 0x0e3e }, /* Thai_maihanakat_maitho ??? */ + { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ + { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ + { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ + { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ + { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ + { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ + { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ + { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ + { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ + { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ + { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ + { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ + { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ + { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ + { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ + { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ + { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ + { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ + { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ + { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ + { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ + { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ + { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ + { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ + { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ + { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ + { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ + { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ + { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ + { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ + { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ + { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ + { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ + { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ + { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ + { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ + { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ + { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ + { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ + { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ + { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ + { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ + { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ + { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ + { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ + { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ + { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ + { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ + { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ + { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ + { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ + { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ + { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ + { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ + { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ + { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ + { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ + { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ + { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ + { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ + { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ + { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ + { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ + { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ + { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ + { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ + { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ + { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ + { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ + { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ + { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ + { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ + { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ + { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ + { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ + { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ + { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ + { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ + { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ + { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ + { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ + { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ + { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ + { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ + { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ + { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ + { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ + { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ + { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ + { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ + { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ + { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ + { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ + { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ + { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ + { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ + { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ + { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ + { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ + { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ + { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ + { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ + { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ + { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ + { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ + { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ + { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ +/* 0x0ef3 Hangul_KkogjiDalrinIeung ? ??? */ + { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ + { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ + { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ + { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ + { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ + { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ + { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ + { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ + { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ + { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x20a0, 0x20a0 }, /* EcuSign ₠ EURO-CURRENCY SIGN */ + { 0x20a1, 0x20a1 }, /* ColonSign ₡ COLON SIGN */ + { 0x20a2, 0x20a2 }, /* CruzeiroSign ₢ CRUZEIRO SIGN */ + { 0x20a3, 0x20a3 }, /* FFrancSign ₣ FRENCH FRANC SIGN */ + { 0x20a4, 0x20a4 }, /* LiraSign ₤ LIRA SIGN */ + { 0x20a5, 0x20a5 }, /* MillSign ₥ MILL SIGN */ + { 0x20a6, 0x20a6 }, /* NairaSign ₦ NAIRA SIGN */ + { 0x20a7, 0x20a7 }, /* PesetaSign ₧ PESETA SIGN */ + { 0x20a8, 0x20a8 }, /* RupeeSign ₨ RUPEE SIGN */ + { 0x20a9, 0x20a9 }, /* WonSign ₩ WON SIGN */ + { 0x20aa, 0x20aa }, /* NewSheqelSign ₪ NEW SHEQEL SIGN */ + { 0x20ab, 0x20ab }, /* DongSign ₫ DONG SIGN */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ +}; + +/* binary search with range check */ +static uint32_t +bin_search(const struct codepair *table, size_t length, xkb_keysym_t keysym) +{ + size_t first = 0; + size_t last = length; + + if (keysym < table[0].keysym || keysym > table[length].keysym) + return 0; + + /* binary search in table */ + while (last >= first) { + size_t mid = (first + last) / 2; + if (table[mid].keysym < keysym) + first = mid + 1; + else if (table[mid].keysym > keysym) + last = mid - 1; + else /* found it */ + return table[mid].ucs; + } + + /* no matching Unicode value found in table */ + return 0; +} + +XKB_EXPORT uint32_t +xkb_keysym_to_utf32(xkb_keysym_t keysym) +{ + /* first check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + return keysym; + + /* patch encoding botch */ + if (keysym == XKB_KEY_KP_Space) + return XKB_KEY_space & 0x7f; + + /* special keysyms */ + if ((keysym >= XKB_KEY_BackSpace && keysym <= XKB_KEY_Clear) || + (keysym >= XKB_KEY_KP_Multiply && keysym <= XKB_KEY_KP_9) || + keysym == XKB_KEY_Return || keysym == XKB_KEY_Escape || + keysym == XKB_KEY_Delete || keysym == XKB_KEY_KP_Tab || + keysym == XKB_KEY_KP_Enter || keysym == XKB_KEY_KP_Equal) + return keysym & 0x7f; + + /* also check for directly encoded Unicode codepoints */ + /* + * In theory, this is supposed to start from 0x100100, such that the ASCII + * range, which is already covered by 0x00-0xff, can't be encoded in two + * ways. However, changing this after a couple of decades probably won't + * go well, so it stays as it is. + */ + if (0x01000000 <= keysym && keysym <= 0x0110ffff) + return keysym - 0x01000000; + + /* search main table */ + return bin_search(keysymtab, ARRAY_SIZE(keysymtab) - 1, keysym); +} + +XKB_EXPORT xkb_keysym_t +xkb_utf32_to_keysym(uint32_t ucs) +{ + /* first check for Latin-1 characters (1:1 mapping) */ + if ((ucs >= 0x0020 && ucs <= 0x007e) || + (ucs >= 0x00a0 && ucs <= 0x00ff)) + return ucs; + + /* special keysyms */ + if ((ucs >= (XKB_KEY_BackSpace & 0x7f) && ucs <= (XKB_KEY_Clear & 0x7f)) || + ucs == (XKB_KEY_Return & 0x7f) || ucs == (XKB_KEY_Escape & 0x7f)) + return ucs | 0xff00; + if (ucs == (XKB_KEY_Delete & 0x7f)) + return XKB_KEY_Delete; + + /* Unicode non-symbols and code points outside Unicode planes */ + if ((ucs >= 0xfdd0 && ucs <= 0xfdef) || + ucs > 0x10ffff || (ucs & 0xfffe) == 0xfffe) + return XKB_KEY_NoSymbol; + + /* search main table */ + for (size_t i = 0; i < ARRAY_SIZE(keysymtab); i++) + if (keysymtab[i].ucs == ucs) + return keysymtab[i].keysym; + + /* Use direct encoding if everything else fails */ + return ucs | 0x01000000; +} + +/* + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rob Bradford <rob@linux.intel.com> + */ + +XKB_EXPORT int +xkb_keysym_to_utf8(xkb_keysym_t keysym, char *buffer, size_t size) +{ + uint32_t codepoint; + + if (size < 7) + return -1; + + codepoint = xkb_keysym_to_utf32(keysym); + + if (codepoint == 0) + return 0; + + return utf32_to_utf8(codepoint, buffer); +} diff --git a/src/keysym.c b/src/keysym.c new file mode 100644 index 0000000..7b492e2 --- /dev/null +++ b/src/keysym.c @@ -0,0 +1,768 @@ +/* + * Copyright 1985, 1987, 1990, 1998 The Open Group + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or their + * institutions shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization from the authors. + */ + +/* + * Copyright © 2009 Dan Nicholson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <stdlib.h> +#include "xkbcommon/xkbcommon.h" +#include "utils.h" +#include "keysym.h" +#include "ks_tables.h" + +static inline const char * +get_name(const struct name_keysym *entry) +{ + return keysym_names + entry->offset; +} + +static int +compare_by_keysym(const void *a, const void *b) +{ + const xkb_keysym_t *key = a; + const struct name_keysym *entry = b; + if (*key < entry->keysym) + return -1; + if (*key > entry->keysym) + return 1; + return 0; +} + +static int +compare_by_name(const void *a, const void *b) +{ + const char *key = a; + const struct name_keysym *entry = b; + return istrcmp(key, get_name(entry)); +} + +XKB_EXPORT int +xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) +{ + const struct name_keysym *entry; + + if ((ks & ((unsigned long) ~0x1fffffff)) != 0) { + snprintf(buffer, size, "Invalid"); + return -1; + } + + entry = bsearch(&ks, keysym_to_name, + ARRAY_SIZE(keysym_to_name), + sizeof(*keysym_to_name), + compare_by_keysym); + if (entry) + return snprintf(buffer, size, "%s", get_name(entry)); + + /* Unnamed Unicode codepoint. */ + if (ks >= 0x01000100 && ks <= 0x0110ffff) { + const int width = (ks & 0xff0000UL) ? 8 : 4; + return snprintf(buffer, size, "U%0*lX", width, ks & 0xffffffUL); + } + + /* Unnamed, non-Unicode, symbol (shouldn't generally happen). */ + return snprintf(buffer, size, "0x%08x", ks); +} + +/* + * Find the correct keysym if one case-insensitive match is given. + * + * The name_to_keysym table is sorted by istrcmp(). So bsearch() may return + * _any_ of all possible case-insensitive duplicates. This function searches the + * returned entry @entry, all previous and all next entries that match by + * case-insensitive comparison and returns the exact match to @name. If @icase + * is true, then this returns the best case-insensitive match instead of a + * correct match. + * The "best" case-insensitive match is the lower-case keysym which we find with + * the help of xkb_keysym_is_lower(). + * The only keysyms that only differ by letter-case are keysyms that are + * available as lower-case and upper-case variant (like KEY_a and KEY_A). So + * returning the first lower-case match is enough in this case. + */ +static const struct name_keysym * +find_sym(const struct name_keysym *entry, const char *name, bool icase) +{ + const struct name_keysym *iter, *last; + size_t len = ARRAY_SIZE(name_to_keysym); + + if (!entry) + return NULL; + + if (!icase && strcmp(get_name(entry), name) == 0) + return entry; + if (icase && xkb_keysym_is_lower(entry->keysym)) + return entry; + + for (iter = entry - 1; iter >= name_to_keysym; --iter) { + if (!icase && strcmp(get_name(iter), name) == 0) + return iter; + if (istrcmp(get_name(iter), get_name(entry)) != 0) + break; + if (icase && xkb_keysym_is_lower(iter->keysym)) + return iter; + } + + last = name_to_keysym + len; + for (iter = entry + 1; iter < last; ++iter) { + if (!icase && strcmp(get_name(iter), name) == 0) + return iter; + if (istrcmp(get_name(iter), get_name(entry)) != 0) + break; + if (icase && xkb_keysym_is_lower(iter->keysym)) + return iter; + } + + if (icase) + return entry; + return NULL; +} + +XKB_EXPORT xkb_keysym_t +xkb_keysym_from_name(const char *s, enum xkb_keysym_flags flags) +{ + const struct name_keysym *entry; + char *tmp; + xkb_keysym_t val; + bool icase = (flags & XKB_KEYSYM_CASE_INSENSITIVE); + + if (flags & ~XKB_KEYSYM_CASE_INSENSITIVE) + return XKB_KEY_NoSymbol; + + entry = bsearch(s, name_to_keysym, + ARRAY_SIZE(name_to_keysym), + sizeof(*name_to_keysym), + compare_by_name); + entry = find_sym(entry, s, icase); + if (entry) + return entry->keysym; + + if (*s == 'U' || (icase && *s == 'u')) { + val = strtoul(&s[1], &tmp, 16); + if (tmp && *tmp != '\0') + return XKB_KEY_NoSymbol; + + if (val < 0x20 || (val > 0x7e && val < 0xa0)) + return XKB_KEY_NoSymbol; + if (val < 0x100) + return val; + if (val > 0x10ffff) + return XKB_KEY_NoSymbol; + return val | 0x01000000; + } + else if (s[0] == '0' && (s[1] == 'x' || (icase && s[1] == 'X'))) { + val = strtoul(&s[2], &tmp, 16); + if (tmp && *tmp != '\0') + return XKB_KEY_NoSymbol; + + return val; + } + + /* Stupid inconsistency between the headers and XKeysymDB: the former has + * no separating underscore, while some XF86* syms in the latter did. + * As a last ditch effort, try without. */ + if (strncmp(s, "XF86_", 5) == 0 || + (icase && istrncmp(s, "XF86_", 5) == 0)) { + xkb_keysym_t ret; + tmp = strdup(s); + if (!tmp) + return XKB_KEY_NoSymbol; + memmove(&tmp[4], &tmp[5], strlen(s) - 5 + 1); + ret = xkb_keysym_from_name(tmp, flags); + free(tmp); + return ret; + } + + return XKB_KEY_NoSymbol; +} + +bool +xkb_keysym_is_keypad(xkb_keysym_t keysym) +{ + return keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_Equal; +} + + +bool +xkb_keysym_is_modifier(xkb_keysym_t keysym) +{ + return + (keysym >= XKB_KEY_Shift_L && keysym <= XKB_KEY_Hyper_R) || + /* libX11 only goes upto XKB_KEY_ISO_Level5_Lock. */ + (keysym >= XKB_KEY_ISO_Lock && keysym <= XKB_KEY_ISO_Last_Group_Lock) || + keysym == XKB_KEY_Mode_switch || + keysym == XKB_KEY_Num_Lock; +} + +static void +XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper); + +bool +xkb_keysym_is_lower(xkb_keysym_t ks) +{ + xkb_keysym_t lower, upper; + + XConvertCase(ks, &lower, &upper); + + if (lower == upper) + return false; + + return (ks == lower ? true : false); +} + +bool +xkb_keysym_is_upper(xkb_keysym_t ks) +{ + xkb_keysym_t lower, upper; + + XConvertCase(ks, &lower, &upper); + + if (lower == upper) + return false; + + return (ks == upper ? true : false); +} + +XKB_EXPORT xkb_keysym_t +xkb_keysym_to_lower(xkb_keysym_t ks) +{ + xkb_keysym_t lower, upper; + + XConvertCase(ks, &lower, &upper); + + return lower; +} + +XKB_EXPORT xkb_keysym_t +xkb_keysym_to_upper(xkb_keysym_t ks) +{ + xkb_keysym_t lower, upper; + + XConvertCase(ks, &lower, &upper); + + return upper; +} + +/* + * The following is copied verbatim from libX11:src/KeyBind.c, commit + * d45b3fc19fbe95c41afc4e51d768df6d42332010, with the following changes: + * - unsigned -> uint32_t + * - unsigend short -> uint16_t + * - s/XK_/XKB_KEY_ + * + * XXX: If newlocale() and iswlower_l()/iswupper_l() interface ever + * become portable, we should use that in conjunction with + * xkb_keysym_to_utf32(), instead of all this stuff. We should + * be sure to give the same results as libX11, though, and be + * locale independent; this information is used by xkbcomp to + * find the automatic type to assign to key groups. + */ + +static void +UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper) +{ + /* Case conversion for UCS, as in Unicode Data version 4.0.0 */ + /* NB: Only converts simple one-to-one mappings. */ + + /* Tables are used where they take less space than */ + /* the code to work out the mappings. Zero values mean */ + /* undefined code points. */ + + static uint16_t const IPAExt_upper_mapping[] = { /* part only */ + 0x0181, 0x0186, 0x0255, 0x0189, 0x018A, + 0x0258, 0x018F, 0x025A, 0x0190, 0x025C, 0x025D, 0x025E, 0x025F, + 0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267, + 0x0197, 0x0196, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x019C, + 0x0270, 0x0271, 0x019D, 0x0273, 0x0274, 0x019F, 0x0276, 0x0277, + 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, + 0x01A6, 0x0281, 0x0282, 0x01A9, 0x0284, 0x0285, 0x0286, 0x0287, + 0x01AE, 0x0289, 0x01B1, 0x01B2, 0x028C, 0x028D, 0x028E, 0x028F, + 0x0290, 0x0291, 0x01B7 + }; + + static uint16_t const LatinExtB_upper_mapping[] = { /* first part only */ + 0x0180, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187, + 0x0187, 0x0189, 0x018A, 0x018B, 0x018B, 0x018D, 0x018E, 0x018F, + 0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x01F6, 0x0196, 0x0197, + 0x0198, 0x0198, 0x019A, 0x019B, 0x019C, 0x019D, 0x0220, 0x019F, + 0x01A0, 0x01A0, 0x01A2, 0x01A2, 0x01A4, 0x01A4, 0x01A6, 0x01A7, + 0x01A7, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AC, 0x01AE, 0x01AF, + 0x01AF, 0x01B1, 0x01B2, 0x01B3, 0x01B3, 0x01B5, 0x01B5, 0x01B7, + 0x01B8, 0x01B8, 0x01BA, 0x01BB, 0x01BC, 0x01BC, 0x01BE, 0x01F7, + 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C4, 0x01C4, 0x01C7, + 0x01C7, 0x01C7, 0x01CA, 0x01CA, 0x01CA + }; + + static uint16_t const LatinExtB_lower_mapping[] = { /* first part only */ + 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, + 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259, + 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, + 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275, + 0x01A1, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x0280, 0x01A8, + 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01B0, + 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, + 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF, + 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, + 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC + }; + + static uint16_t const Greek_upper_mapping[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0374, 0x0375, 0x0000, 0x0000, + 0x0000, 0x0000, 0x037A, 0x0000, 0x0000, 0x0000, 0x037E, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0384, 0x0385, 0x0386, 0x0387, + 0x0388, 0x0389, 0x038A, 0x0000, 0x038C, 0x0000, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x0386, 0x0388, 0x0389, 0x038A, + 0x03B0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x03A3, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x038C, 0x038E, 0x038F, 0x0000, + 0x0392, 0x0398, 0x03D2, 0x03D3, 0x03D4, 0x03A6, 0x03A0, 0x03D7, + 0x03D8, 0x03D8, 0x03DA, 0x03DA, 0x03DC, 0x03DC, 0x03DE, 0x03DE, + 0x03E0, 0x03E0, 0x03E2, 0x03E2, 0x03E4, 0x03E4, 0x03E6, 0x03E6, + 0x03E8, 0x03E8, 0x03EA, 0x03EA, 0x03EC, 0x03EC, 0x03EE, 0x03EE, + 0x039A, 0x03A1, 0x03F9, 0x03F3, 0x03F4, 0x0395, 0x03F6, 0x03F7, + 0x03F7, 0x03F9, 0x03FA, 0x03FA, 0x0000, 0x0000, 0x0000, 0x0000 + }; + + static uint16_t const Greek_lower_mapping[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0374, 0x0375, 0x0000, 0x0000, + 0x0000, 0x0000, 0x037A, 0x0000, 0x0000, 0x0000, 0x037E, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0384, 0x0385, 0x03AC, 0x0387, + 0x03AD, 0x03AE, 0x03AF, 0x0000, 0x03CC, 0x0000, 0x03CD, 0x03CE, + 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x0000, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000, + 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, + 0x03D9, 0x03D9, 0x03DB, 0x03DB, 0x03DD, 0x03DD, 0x03DF, 0x03DF, + 0x03E1, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, + 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF, + 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03B8, 0x03F5, 0x03F6, 0x03F8, + 0x03F8, 0x03F2, 0x03FB, 0x03FB, 0x0000, 0x0000, 0x0000, 0x0000 + }; + + static uint16_t const GreekExt_lower_mapping[] = { + 0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07, + 0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07, + 0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x0000, 0x0000, + 0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x0000, 0x0000, + 0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27, + 0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27, + 0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37, + 0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37, + 0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x0000, 0x0000, + 0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x0000, 0x0000, + 0x1F50, 0x1F51, 0x1F52, 0x1F53, 0x1F54, 0x1F55, 0x1F56, 0x1F57, + 0x0000, 0x1F51, 0x0000, 0x1F53, 0x0000, 0x1F55, 0x0000, 0x1F57, + 0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67, + 0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67, + 0x1F70, 0x1F71, 0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1F76, 0x1F77, + 0x1F78, 0x1F79, 0x1F7A, 0x1F7B, 0x1F7C, 0x1F7D, 0x0000, 0x0000, + 0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87, + 0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87, + 0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97, + 0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97, + 0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7, + 0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7, + 0x1FB0, 0x1FB1, 0x1FB2, 0x1FB3, 0x1FB4, 0x0000, 0x1FB6, 0x1FB7, + 0x1FB0, 0x1FB1, 0x1F70, 0x1F71, 0x1FB3, 0x1FBD, 0x1FBE, 0x1FBF, + 0x1FC0, 0x1FC1, 0x1FC2, 0x1FC3, 0x1FC4, 0x0000, 0x1FC6, 0x1FC7, + 0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1FC3, 0x1FCD, 0x1FCE, 0x1FCF, + 0x1FD0, 0x1FD1, 0x1FD2, 0x1FD3, 0x0000, 0x0000, 0x1FD6, 0x1FD7, + 0x1FD0, 0x1FD1, 0x1F76, 0x1F77, 0x0000, 0x1FDD, 0x1FDE, 0x1FDF, + 0x1FE0, 0x1FE1, 0x1FE2, 0x1FE3, 0x1FE4, 0x1FE5, 0x1FE6, 0x1FE7, + 0x1FE0, 0x1FE1, 0x1F7A, 0x1F7B, 0x1FE5, 0x1FED, 0x1FEE, 0x1FEF, + 0x0000, 0x0000, 0x1FF2, 0x1FF3, 0x1FF4, 0x0000, 0x1FF6, 0x1FF7, + 0x1F78, 0x1F79, 0x1F7C, 0x1F7D, 0x1FF3, 0x1FFD, 0x1FFE, 0x0000 + }; + + static uint16_t const GreekExt_upper_mapping[] = { + 0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F, + 0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F, + 0x1F18, 0x1F19, 0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x0000, 0x0000, + 0x1F18, 0x1F19, 0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x0000, 0x0000, + 0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D, 0x1F2E, 0x1F2F, + 0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D, 0x1F2E, 0x1F2F, + 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D, 0x1F3E, 0x1F3F, + 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D, 0x1F3E, 0x1F3F, + 0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x0000, 0x0000, + 0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x0000, 0x0000, + 0x1F50, 0x1F59, 0x1F52, 0x1F5B, 0x1F54, 0x1F5D, 0x1F56, 0x1F5F, + 0x0000, 0x1F59, 0x0000, 0x1F5B, 0x0000, 0x1F5D, 0x0000, 0x1F5F, + 0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F, + 0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F, + 0x1FBA, 0x1FBB, 0x1FC8, 0x1FC9, 0x1FCA, 0x1FCB, 0x1FDA, 0x1FDB, + 0x1FF8, 0x1FF9, 0x1FEA, 0x1FEB, 0x1FFA, 0x1FFB, 0x0000, 0x0000, + 0x1F88, 0x1F89, 0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D, 0x1F8E, 0x1F8F, + 0x1F88, 0x1F89, 0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D, 0x1F8E, 0x1F8F, + 0x1F98, 0x1F99, 0x1F9A, 0x1F9B, 0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F, + 0x1F98, 0x1F99, 0x1F9A, 0x1F9B, 0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F, + 0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD, 0x1FAE, 0x1FAF, + 0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD, 0x1FAE, 0x1FAF, + 0x1FB8, 0x1FB9, 0x1FB2, 0x1FBC, 0x1FB4, 0x0000, 0x1FB6, 0x1FB7, + 0x1FB8, 0x1FB9, 0x1FBA, 0x1FBB, 0x1FBC, 0x1FBD, 0x0399, 0x1FBF, + 0x1FC0, 0x1FC1, 0x1FC2, 0x1FCC, 0x1FC4, 0x0000, 0x1FC6, 0x1FC7, + 0x1FC8, 0x1FC9, 0x1FCA, 0x1FCB, 0x1FCC, 0x1FCD, 0x1FCE, 0x1FCF, + 0x1FD8, 0x1FD9, 0x1FD2, 0x1FD3, 0x0000, 0x0000, 0x1FD6, 0x1FD7, + 0x1FD8, 0x1FD9, 0x1FDA, 0x1FDB, 0x0000, 0x1FDD, 0x1FDE, 0x1FDF, + 0x1FE8, 0x1FE9, 0x1FE2, 0x1FE3, 0x1FE4, 0x1FEC, 0x1FE6, 0x1FE7, + 0x1FE8, 0x1FE9, 0x1FEA, 0x1FEB, 0x1FEC, 0x1FED, 0x1FEE, 0x1FEF, + 0x0000, 0x0000, 0x1FF2, 0x1FFC, 0x1FF4, 0x0000, 0x1FF6, 0x1FF7, + 0x1FF8, 0x1FF9, 0x1FFA, 0x1FFB, 0x1FFC, 0x1FFD, 0x1FFE, 0x0000 + }; + + *lower = code; + *upper = code; + + /* Basic Latin and Latin-1 Supplement, U+0000 to U+00FF */ + if (code <= 0x00ff) { + if (code >= 0x0041 && code <= 0x005a) /* A-Z */ + *lower += 0x20; + else if (code >= 0x0061 && code <= 0x007a) /* a-z */ + *upper -= 0x20; + else if ( (code >= 0x00c0 && code <= 0x00d6) || + (code >= 0x00d8 && code <= 0x00de) ) + *lower += 0x20; + else if ( (code >= 0x00e0 && code <= 0x00f6) || + (code >= 0x00f8 && code <= 0x00fe) ) + *upper -= 0x20; + else if (code == 0x00ff) /* y with diaeresis */ + *upper = 0x0178; + else if (code == 0x00b5) /* micro sign */ + *upper = 0x039c; + else if (code == 0x00df) /* ssharp */ + *upper = 0x1e9e; + return; + } + + /* Latin Extended-A, U+0100 to U+017F */ + if (code >= 0x0100 && code <= 0x017f) { + if ( (code >= 0x0100 && code <= 0x012f) || + (code >= 0x0132 && code <= 0x0137) || + (code >= 0x014a && code <= 0x0177) ) { + *upper = code & ~1; + *lower = code | 1; + } + else if ( (code >= 0x0139 && code <= 0x0148) || + (code >= 0x0179 && code <= 0x017e) ) { + if (code & 1) + *lower += 1; + else + *upper -= 1; + } + else if (code == 0x0130) + *lower = 0x0069; + else if (code == 0x0131) + *upper = 0x0049; + else if (code == 0x0178) + *lower = 0x00ff; + else if (code == 0x017f) + *upper = 0x0053; + return; + } + + /* Latin Extended-B, U+0180 to U+024F */ + if (code >= 0x0180 && code <= 0x024f) { + if (code >= 0x01cd && code <= 0x01dc) { + if (code & 1) + *lower += 1; + else + *upper -= 1; + } + else if ( (code >= 0x01de && code <= 0x01ef) || + (code >= 0x01f4 && code <= 0x01f5) || + (code >= 0x01f8 && code <= 0x021f) || + (code >= 0x0222 && code <= 0x0233) ) { + *lower |= 1; + *upper &= ~1; + } + else if (code >= 0x0180 && code <= 0x01cc) { + *lower = LatinExtB_lower_mapping[code - 0x0180]; + *upper = LatinExtB_upper_mapping[code - 0x0180]; + } + else if (code == 0x01dd) + *upper = 0x018e; + else if (code == 0x01f1 || code == 0x01f2) { + *lower = 0x01f3; + *upper = 0x01f1; + } + else if (code == 0x01f3) + *upper = 0x01f1; + else if (code == 0x01f6) + *lower = 0x0195; + else if (code == 0x01f7) + *lower = 0x01bf; + else if (code == 0x0220) + *lower = 0x019e; + return; + } + + /* IPA Extensions, U+0250 to U+02AF */ + if (code >= 0x0253 && code <= 0x0292) { + *upper = IPAExt_upper_mapping[code - 0x0253]; + } + + /* Combining Diacritical Marks, U+0300 to U+036F */ + if (code == 0x0345) { + *upper = 0x0399; + } + + /* Greek and Coptic, U+0370 to U+03FF */ + if (code >= 0x0370 && code <= 0x03ff) { + *lower = Greek_lower_mapping[code - 0x0370]; + *upper = Greek_upper_mapping[code - 0x0370]; + if (*upper == 0) + *upper = code; + if (*lower == 0) + *lower = code; + } + + /* Cyrillic and Cyrillic Supplementary, U+0400 to U+052F */ + if ( (code >= 0x0400 && code <= 0x04ff) || + (code >= 0x0500 && code <= 0x052f) ) { + if (code >= 0x0400 && code <= 0x040f) + *lower += 0x50; + else if (code >= 0x0410 && code <= 0x042f) + *lower += 0x20; + else if (code >= 0x0430 && code <= 0x044f) + *upper -= 0x20; + else if (code >= 0x0450 && code <= 0x045f) + *upper -= 0x50; + else if ( (code >= 0x0460 && code <= 0x0481) || + (code >= 0x048a && code <= 0x04bf) || + (code >= 0x04d0 && code <= 0x04f5) || + (code >= 0x04f8 && code <= 0x04f9) || + (code >= 0x0500 && code <= 0x050f) ) { + *upper &= ~1; + *lower |= 1; + } + else if (code >= 0x04c1 && code <= 0x04ce) { + if (code & 1) + *lower += 1; + else + *upper -= 1; + } + } + + /* Armenian, U+0530 to U+058F */ + if (code >= 0x0530 && code <= 0x058f) { + if (code >= 0x0531 && code <= 0x0556) + *lower += 0x30; + else if (code >=0x0561 && code <= 0x0586) + *upper -= 0x30; + } + + /* Latin Extended Additional, U+1E00 to U+1EFF */ + if (code >= 0x1e00 && code <= 0x1eff) { + if ( (code >= 0x1e00 && code <= 0x1e95) || + (code >= 0x1ea0 && code <= 0x1ef9) ) { + *upper &= ~1; + *lower |= 1; + } + else if (code == 0x1e9b) + *upper = 0x1e60; + else if (code == 0x1e9e) + *lower = 0x00df; /* ssharp */ + } + + /* Greek Extended, U+1F00 to U+1FFF */ + if (code >= 0x1f00 && code <= 0x1fff) { + *lower = GreekExt_lower_mapping[code - 0x1f00]; + *upper = GreekExt_upper_mapping[code - 0x1f00]; + if (*upper == 0) + *upper = code; + if (*lower == 0) + *lower = code; + } + + /* Letterlike Symbols, U+2100 to U+214F */ + if (code >= 0x2100 && code <= 0x214f) { + switch (code) { + case 0x2126: *lower = 0x03c9; break; + case 0x212a: *lower = 0x006b; break; + case 0x212b: *lower = 0x00e5; break; + } + } + /* Number Forms, U+2150 to U+218F */ + else if (code >= 0x2160 && code <= 0x216f) + *lower += 0x10; + else if (code >= 0x2170 && code <= 0x217f) + *upper -= 0x10; + /* Enclosed Alphanumerics, U+2460 to U+24FF */ + else if (code >= 0x24b6 && code <= 0x24cf) + *lower += 0x1a; + else if (code >= 0x24d0 && code <= 0x24e9) + *upper -= 0x1a; + /* Halfwidth and Fullwidth Forms, U+FF00 to U+FFEF */ + else if (code >= 0xff21 && code <= 0xff3a) + *lower += 0x20; + else if (code >= 0xff41 && code <= 0xff5a) + *upper -= 0x20; + /* Deseret, U+10400 to U+104FF */ + else if (code >= 0x10400 && code <= 0x10427) + *lower += 0x28; + else if (code >= 0x10428 && code <= 0x1044f) + *upper -= 0x28; +} + +static void +XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper) +{ + /* Latin 1 keysym */ + if (sym < 0x100) { + UCSConvertCase(sym, lower, upper); + return; + } + + /* Unicode keysym */ + if ((sym & 0xff000000) == 0x01000000) { + UCSConvertCase((sym & 0x00ffffff), lower, upper); + *upper |= 0x01000000; + *lower |= 0x01000000; + return; + } + + /* Legacy keysym */ + + *lower = sym; + *upper = sym; + + switch(sym >> 8) { + case 1: /* Latin 2 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym == XKB_KEY_Aogonek) + *lower = XKB_KEY_aogonek; + else if (sym >= XKB_KEY_Lstroke && sym <= XKB_KEY_Sacute) + *lower += (XKB_KEY_lstroke - XKB_KEY_Lstroke); + else if (sym >= XKB_KEY_Scaron && sym <= XKB_KEY_Zacute) + *lower += (XKB_KEY_scaron - XKB_KEY_Scaron); + else if (sym >= XKB_KEY_Zcaron && sym <= XKB_KEY_Zabovedot) + *lower += (XKB_KEY_zcaron - XKB_KEY_Zcaron); + else if (sym == XKB_KEY_aogonek) + *upper = XKB_KEY_Aogonek; + else if (sym >= XKB_KEY_lstroke && sym <= XKB_KEY_sacute) + *upper -= (XKB_KEY_lstroke - XKB_KEY_Lstroke); + else if (sym >= XKB_KEY_scaron && sym <= XKB_KEY_zacute) + *upper -= (XKB_KEY_scaron - XKB_KEY_Scaron); + else if (sym >= XKB_KEY_zcaron && sym <= XKB_KEY_zabovedot) + *upper -= (XKB_KEY_zcaron - XKB_KEY_Zcaron); + else if (sym >= XKB_KEY_Racute && sym <= XKB_KEY_Tcedilla) + *lower += (XKB_KEY_racute - XKB_KEY_Racute); + else if (sym >= XKB_KEY_racute && sym <= XKB_KEY_tcedilla) + *upper -= (XKB_KEY_racute - XKB_KEY_Racute); + break; + case 2: /* Latin 3 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Hstroke && sym <= XKB_KEY_Hcircumflex) + *lower += (XKB_KEY_hstroke - XKB_KEY_Hstroke); + else if (sym >= XKB_KEY_Gbreve && sym <= XKB_KEY_Jcircumflex) + *lower += (XKB_KEY_gbreve - XKB_KEY_Gbreve); + else if (sym >= XKB_KEY_hstroke && sym <= XKB_KEY_hcircumflex) + *upper -= (XKB_KEY_hstroke - XKB_KEY_Hstroke); + else if (sym >= XKB_KEY_gbreve && sym <= XKB_KEY_jcircumflex) + *upper -= (XKB_KEY_gbreve - XKB_KEY_Gbreve); + else if (sym >= XKB_KEY_Cabovedot && sym <= XKB_KEY_Scircumflex) + *lower += (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); + else if (sym >= XKB_KEY_cabovedot && sym <= XKB_KEY_scircumflex) + *upper -= (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); + break; + case 3: /* Latin 4 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Rcedilla && sym <= XKB_KEY_Tslash) + *lower += (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); + else if (sym >= XKB_KEY_rcedilla && sym <= XKB_KEY_tslash) + *upper -= (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); + else if (sym == XKB_KEY_ENG) + *lower = XKB_KEY_eng; + else if (sym == XKB_KEY_eng) + *upper = XKB_KEY_ENG; + else if (sym >= XKB_KEY_Amacron && sym <= XKB_KEY_Umacron) + *lower += (XKB_KEY_amacron - XKB_KEY_Amacron); + else if (sym >= XKB_KEY_amacron && sym <= XKB_KEY_umacron) + *upper -= (XKB_KEY_amacron - XKB_KEY_Amacron); + break; + case 6: /* Cyrillic */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE) + *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); + else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze) + *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); + else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN) + *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); + else if (sym >= XKB_KEY_Cyrillic_yu && sym <= XKB_KEY_Cyrillic_hardsign) + *upper += (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); + break; + case 7: /* Greek */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Greek_ALPHAaccent && sym <= XKB_KEY_Greek_OMEGAaccent) + *lower += (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); + else if (sym >= XKB_KEY_Greek_alphaaccent && sym <= XKB_KEY_Greek_omegaaccent && + sym != XKB_KEY_Greek_iotaaccentdieresis && + sym != XKB_KEY_Greek_upsilonaccentdieresis) + *upper -= (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); + else if (sym >= XKB_KEY_Greek_ALPHA && sym <= XKB_KEY_Greek_OMEGA) + *lower += (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); + else if (sym >= XKB_KEY_Greek_alpha && sym <= XKB_KEY_Greek_omega && + sym != XKB_KEY_Greek_finalsmallsigma) + *upper -= (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); + break; + case 0x13: /* Latin 9 */ + if (sym == XKB_KEY_OE) + *lower = XKB_KEY_oe; + else if (sym == XKB_KEY_oe) + *upper = XKB_KEY_OE; + else if (sym == XKB_KEY_Ydiaeresis) + *lower = XKB_KEY_ydiaeresis; + break; + } +} diff --git a/src/keysym.h b/src/keysym.h new file mode 100644 index 0000000..2633963 --- /dev/null +++ b/src/keysym.h @@ -0,0 +1,65 @@ +/* + * Copyright 1985, 1987, 1990, 1998 The Open Group + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or their + * institutions shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization from the authors. + */ + +/* + * Copyright © 2009 Dan Nicholson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef KEYSYM_H +#define KEYSYM_H + +bool +xkb_keysym_is_lower(xkb_keysym_t keysym); + +bool +xkb_keysym_is_upper(xkb_keysym_t keysym); + +bool +xkb_keysym_is_keypad(xkb_keysym_t keysym); + +bool +xkb_keysym_is_modifier(xkb_keysym_t keysym); + +#endif diff --git a/src/ks_tables.h b/src/ks_tables.h new file mode 100644 index 0000000..12dc4c0 --- /dev/null +++ b/src/ks_tables.h @@ -0,0 +1,7129 @@ + +/** + * This file comes from libxkbcommon and was generated by makekeys.py + * You can always fetch the latest version from: + * https://raw.github.com/xkbcommon/libxkbcommon/master/src/ks_tables.h + */ + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" +#endif +static const char *keysym_names = + "0\0" + "1\0" + "2\0" + "3\0" + "3270_AltCursor\0" + "3270_Attn\0" + "3270_BackTab\0" + "3270_ChangeScreen\0" + "3270_Copy\0" + "3270_CursorBlink\0" + "3270_CursorSelect\0" + "3270_DeleteWord\0" + "3270_Duplicate\0" + "3270_Enter\0" + "3270_EraseEOF\0" + "3270_EraseInput\0" + "3270_ExSelect\0" + "3270_FieldMark\0" + "3270_Ident\0" + "3270_Jump\0" + "3270_KeyClick\0" + "3270_Left2\0" + "3270_PA1\0" + "3270_PA2\0" + "3270_PA3\0" + "3270_Play\0" + "3270_PrintScreen\0" + "3270_Quit\0" + "3270_Record\0" + "3270_Reset\0" + "3270_Right2\0" + "3270_Rule\0" + "3270_Setup\0" + "3270_Test\0" + "4\0" + "5\0" + "6\0" + "7\0" + "8\0" + "9\0" + "A\0" + "a\0" + "Aacute\0" + "aacute\0" + "Abelowdot\0" + "abelowdot\0" + "abovedot\0" + "Abreve\0" + "abreve\0" + "Abreveacute\0" + "abreveacute\0" + "Abrevebelowdot\0" + "abrevebelowdot\0" + "Abrevegrave\0" + "abrevegrave\0" + "Abrevehook\0" + "abrevehook\0" + "Abrevetilde\0" + "abrevetilde\0" + "AccessX_Enable\0" + "AccessX_Feedback_Enable\0" + "Acircumflex\0" + "acircumflex\0" + "Acircumflexacute\0" + "acircumflexacute\0" + "Acircumflexbelowdot\0" + "acircumflexbelowdot\0" + "Acircumflexgrave\0" + "acircumflexgrave\0" + "Acircumflexhook\0" + "acircumflexhook\0" + "Acircumflextilde\0" + "acircumflextilde\0" + "acute\0" + "Adiaeresis\0" + "adiaeresis\0" + "AE\0" + "ae\0" + "Agrave\0" + "agrave\0" + "Ahook\0" + "ahook\0" + "Alt_L\0" + "Alt_R\0" + "Amacron\0" + "amacron\0" + "ampersand\0" + "Aogonek\0" + "aogonek\0" + "apostrophe\0" + "approxeq\0" + "approximate\0" + "Arabic_0\0" + "Arabic_1\0" + "Arabic_2\0" + "Arabic_3\0" + "Arabic_4\0" + "Arabic_5\0" + "Arabic_6\0" + "Arabic_7\0" + "Arabic_8\0" + "Arabic_9\0" + "Arabic_ain\0" + "Arabic_alef\0" + "Arabic_alefmaksura\0" + "Arabic_beh\0" + "Arabic_comma\0" + "Arabic_dad\0" + "Arabic_dal\0" + "Arabic_damma\0" + "Arabic_dammatan\0" + "Arabic_ddal\0" + "Arabic_farsi_yeh\0" + "Arabic_fatha\0" + "Arabic_fathatan\0" + "Arabic_feh\0" + "Arabic_fullstop\0" + "Arabic_gaf\0" + "Arabic_ghain\0" + "Arabic_ha\0" + "Arabic_hah\0" + "Arabic_hamza\0" + "Arabic_hamza_above\0" + "Arabic_hamza_below\0" + "Arabic_hamzaonalef\0" + "Arabic_hamzaonwaw\0" + "Arabic_hamzaonyeh\0" + "Arabic_hamzaunderalef\0" + "Arabic_heh\0" + "Arabic_heh_doachashmee\0" + "Arabic_heh_goal\0" + "Arabic_jeem\0" + "Arabic_jeh\0" + "Arabic_kaf\0" + "Arabic_kasra\0" + "Arabic_kasratan\0" + "Arabic_keheh\0" + "Arabic_khah\0" + "Arabic_lam\0" + "Arabic_madda_above\0" + "Arabic_maddaonalef\0" + "Arabic_meem\0" + "Arabic_noon\0" + "Arabic_noon_ghunna\0" + "Arabic_peh\0" + "Arabic_percent\0" + "Arabic_qaf\0" + "Arabic_question_mark\0" + "Arabic_ra\0" + "Arabic_rreh\0" + "Arabic_sad\0" + "Arabic_seen\0" + "Arabic_semicolon\0" + "Arabic_shadda\0" + "Arabic_sheen\0" + "Arabic_sukun\0" + "Arabic_superscript_alef\0" + "Arabic_switch\0" + "Arabic_tah\0" + "Arabic_tatweel\0" + "Arabic_tcheh\0" + "Arabic_teh\0" + "Arabic_tehmarbuta\0" + "Arabic_thal\0" + "Arabic_theh\0" + "Arabic_tteh\0" + "Arabic_veh\0" + "Arabic_waw\0" + "Arabic_yeh\0" + "Arabic_yeh_baree\0" + "Arabic_zah\0" + "Arabic_zain\0" + "Aring\0" + "aring\0" + "Armenian_accent\0" + "Armenian_amanak\0" + "Armenian_apostrophe\0" + "Armenian_AT\0" + "Armenian_at\0" + "Armenian_AYB\0" + "Armenian_ayb\0" + "Armenian_BEN\0" + "Armenian_ben\0" + "Armenian_but\0" + "Armenian_CHA\0" + "Armenian_cha\0" + "Armenian_DA\0" + "Armenian_da\0" + "Armenian_DZA\0" + "Armenian_dza\0" + "Armenian_E\0" + "Armenian_e\0" + "Armenian_exclam\0" + "Armenian_FE\0" + "Armenian_fe\0" + "Armenian_full_stop\0" + "Armenian_GHAT\0" + "Armenian_ghat\0" + "Armenian_GIM\0" + "Armenian_gim\0" + "Armenian_HI\0" + "Armenian_hi\0" + "Armenian_HO\0" + "Armenian_ho\0" + "Armenian_hyphen\0" + "Armenian_INI\0" + "Armenian_ini\0" + "Armenian_JE\0" + "Armenian_je\0" + "Armenian_KE\0" + "Armenian_ke\0" + "Armenian_KEN\0" + "Armenian_ken\0" + "Armenian_KHE\0" + "Armenian_khe\0" + "Armenian_ligature_ew\0" + "Armenian_LYUN\0" + "Armenian_lyun\0" + "Armenian_MEN\0" + "Armenian_men\0" + "Armenian_NU\0" + "Armenian_nu\0" + "Armenian_O\0" + "Armenian_o\0" + "Armenian_paruyk\0" + "Armenian_PE\0" + "Armenian_pe\0" + "Armenian_PYUR\0" + "Armenian_pyur\0" + "Armenian_question\0" + "Armenian_RA\0" + "Armenian_ra\0" + "Armenian_RE\0" + "Armenian_re\0" + "Armenian_SE\0" + "Armenian_se\0" + "Armenian_separation_mark\0" + "Armenian_SHA\0" + "Armenian_sha\0" + "Armenian_shesht\0" + "Armenian_TCHE\0" + "Armenian_tche\0" + "Armenian_TO\0" + "Armenian_to\0" + "Armenian_TSA\0" + "Armenian_tsa\0" + "Armenian_TSO\0" + "Armenian_tso\0" + "Armenian_TYUN\0" + "Armenian_tyun\0" + "Armenian_verjaket\0" + "Armenian_VEV\0" + "Armenian_vev\0" + "Armenian_VO\0" + "Armenian_vo\0" + "Armenian_VYUN\0" + "Armenian_vyun\0" + "Armenian_YECH\0" + "Armenian_yech\0" + "Armenian_yentamna\0" + "Armenian_ZA\0" + "Armenian_za\0" + "Armenian_ZHE\0" + "Armenian_zhe\0" + "asciicircum\0" + "asciitilde\0" + "asterisk\0" + "at\0" + "Atilde\0" + "atilde\0" + "AudibleBell_Enable\0" + "B\0" + "b\0" + "Babovedot\0" + "babovedot\0" + "backslash\0" + "BackSpace\0" + "BackTab\0" + "ballotcross\0" + "bar\0" + "because\0" + "Begin\0" + "blank\0" + "block\0" + "botintegral\0" + "botleftparens\0" + "botleftsqbracket\0" + "botleftsummation\0" + "botrightparens\0" + "botrightsqbracket\0" + "botrightsummation\0" + "bott\0" + "botvertsummationconnector\0" + "BounceKeys_Enable\0" + "braceleft\0" + "braceright\0" + "bracketleft\0" + "bracketright\0" + "braille_blank\0" + "braille_dot_1\0" + "braille_dot_10\0" + "braille_dot_2\0" + "braille_dot_3\0" + "braille_dot_4\0" + "braille_dot_5\0" + "braille_dot_6\0" + "braille_dot_7\0" + "braille_dot_8\0" + "braille_dot_9\0" + "braille_dots_1\0" + "braille_dots_12\0" + "braille_dots_123\0" + "braille_dots_1234\0" + "braille_dots_12345\0" + "braille_dots_123456\0" + "braille_dots_1234567\0" + "braille_dots_12345678\0" + "braille_dots_1234568\0" + "braille_dots_123457\0" + "braille_dots_1234578\0" + "braille_dots_123458\0" + "braille_dots_12346\0" + "braille_dots_123467\0" + "braille_dots_1234678\0" + "braille_dots_123468\0" + "braille_dots_12347\0" + "braille_dots_123478\0" + "braille_dots_12348\0" + "braille_dots_1235\0" + "braille_dots_12356\0" + "braille_dots_123567\0" + "braille_dots_1235678\0" + "braille_dots_123568\0" + "braille_dots_12357\0" + "braille_dots_123578\0" + "braille_dots_12358\0" + "braille_dots_1236\0" + "braille_dots_12367\0" + "braille_dots_123678\0" + "braille_dots_12368\0" + "braille_dots_1237\0" + "braille_dots_12378\0" + "braille_dots_1238\0" + "braille_dots_124\0" + "braille_dots_1245\0" + "braille_dots_12456\0" + "braille_dots_124567\0" + "braille_dots_1245678\0" + "braille_dots_124568\0" + "braille_dots_12457\0" + "braille_dots_124578\0" + "braille_dots_12458\0" + "braille_dots_1246\0" + "braille_dots_12467\0" + "braille_dots_124678\0" + "braille_dots_12468\0" + "braille_dots_1247\0" + "braille_dots_12478\0" + "braille_dots_1248\0" + "braille_dots_125\0" + "braille_dots_1256\0" + "braille_dots_12567\0" + "braille_dots_125678\0" + "braille_dots_12568\0" + "braille_dots_1257\0" + "braille_dots_12578\0" + "braille_dots_1258\0" + "braille_dots_126\0" + "braille_dots_1267\0" + "braille_dots_12678\0" + "braille_dots_1268\0" + "braille_dots_127\0" + "braille_dots_1278\0" + "braille_dots_128\0" + "braille_dots_13\0" + "braille_dots_134\0" + "braille_dots_1345\0" + "braille_dots_13456\0" + "braille_dots_134567\0" + "braille_dots_1345678\0" + "braille_dots_134568\0" + "braille_dots_13457\0" + "braille_dots_134578\0" + "braille_dots_13458\0" + "braille_dots_1346\0" + "braille_dots_13467\0" + "braille_dots_134678\0" + "braille_dots_13468\0" + "braille_dots_1347\0" + "braille_dots_13478\0" + "braille_dots_1348\0" + "braille_dots_135\0" + "braille_dots_1356\0" + "braille_dots_13567\0" + "braille_dots_135678\0" + "braille_dots_13568\0" + "braille_dots_1357\0" + "braille_dots_13578\0" + "braille_dots_1358\0" + "braille_dots_136\0" + "braille_dots_1367\0" + "braille_dots_13678\0" + "braille_dots_1368\0" + "braille_dots_137\0" + "braille_dots_1378\0" + "braille_dots_138\0" + "braille_dots_14\0" + "braille_dots_145\0" + "braille_dots_1456\0" + "braille_dots_14567\0" + "braille_dots_145678\0" + "braille_dots_14568\0" + "braille_dots_1457\0" + "braille_dots_14578\0" + "braille_dots_1458\0" + "braille_dots_146\0" + "braille_dots_1467\0" + "braille_dots_14678\0" + "braille_dots_1468\0" + "braille_dots_147\0" + "braille_dots_1478\0" + "braille_dots_148\0" + "braille_dots_15\0" + "braille_dots_156\0" + "braille_dots_1567\0" + "braille_dots_15678\0" + "braille_dots_1568\0" + "braille_dots_157\0" + "braille_dots_1578\0" + "braille_dots_158\0" + "braille_dots_16\0" + "braille_dots_167\0" + "braille_dots_1678\0" + "braille_dots_168\0" + "braille_dots_17\0" + "braille_dots_178\0" + "braille_dots_18\0" + "braille_dots_2\0" + "braille_dots_23\0" + "braille_dots_234\0" + "braille_dots_2345\0" + "braille_dots_23456\0" + "braille_dots_234567\0" + "braille_dots_2345678\0" + "braille_dots_234568\0" + "braille_dots_23457\0" + "braille_dots_234578\0" + "braille_dots_23458\0" + "braille_dots_2346\0" + "braille_dots_23467\0" + "braille_dots_234678\0" + "braille_dots_23468\0" + "braille_dots_2347\0" + "braille_dots_23478\0" + "braille_dots_2348\0" + "braille_dots_235\0" + "braille_dots_2356\0" + "braille_dots_23567\0" + "braille_dots_235678\0" + "braille_dots_23568\0" + "braille_dots_2357\0" + "braille_dots_23578\0" + "braille_dots_2358\0" + "braille_dots_236\0" + "braille_dots_2367\0" + "braille_dots_23678\0" + "braille_dots_2368\0" + "braille_dots_237\0" + "braille_dots_2378\0" + "braille_dots_238\0" + "braille_dots_24\0" + "braille_dots_245\0" + "braille_dots_2456\0" + "braille_dots_24567\0" + "braille_dots_245678\0" + "braille_dots_24568\0" + "braille_dots_2457\0" + "braille_dots_24578\0" + "braille_dots_2458\0" + "braille_dots_246\0" + "braille_dots_2467\0" + "braille_dots_24678\0" + "braille_dots_2468\0" + "braille_dots_247\0" + "braille_dots_2478\0" + "braille_dots_248\0" + "braille_dots_25\0" + "braille_dots_256\0" + "braille_dots_2567\0" + "braille_dots_25678\0" + "braille_dots_2568\0" + "braille_dots_257\0" + "braille_dots_2578\0" + "braille_dots_258\0" + "braille_dots_26\0" + "braille_dots_267\0" + "braille_dots_2678\0" + "braille_dots_268\0" + "braille_dots_27\0" + "braille_dots_278\0" + "braille_dots_28\0" + "braille_dots_3\0" + "braille_dots_34\0" + "braille_dots_345\0" + "braille_dots_3456\0" + "braille_dots_34567\0" + "braille_dots_345678\0" + "braille_dots_34568\0" + "braille_dots_3457\0" + "braille_dots_34578\0" + "braille_dots_3458\0" + "braille_dots_346\0" + "braille_dots_3467\0" + "braille_dots_34678\0" + "braille_dots_3468\0" + "braille_dots_347\0" + "braille_dots_3478\0" + "braille_dots_348\0" + "braille_dots_35\0" + "braille_dots_356\0" + "braille_dots_3567\0" + "braille_dots_35678\0" + "braille_dots_3568\0" + "braille_dots_357\0" + "braille_dots_3578\0" + "braille_dots_358\0" + "braille_dots_36\0" + "braille_dots_367\0" + "braille_dots_3678\0" + "braille_dots_368\0" + "braille_dots_37\0" + "braille_dots_378\0" + "braille_dots_38\0" + "braille_dots_4\0" + "braille_dots_45\0" + "braille_dots_456\0" + "braille_dots_4567\0" + "braille_dots_45678\0" + "braille_dots_4568\0" + "braille_dots_457\0" + "braille_dots_4578\0" + "braille_dots_458\0" + "braille_dots_46\0" + "braille_dots_467\0" + "braille_dots_4678\0" + "braille_dots_468\0" + "braille_dots_47\0" + "braille_dots_478\0" + "braille_dots_48\0" + "braille_dots_5\0" + "braille_dots_56\0" + "braille_dots_567\0" + "braille_dots_5678\0" + "braille_dots_568\0" + "braille_dots_57\0" + "braille_dots_578\0" + "braille_dots_58\0" + "braille_dots_6\0" + "braille_dots_67\0" + "braille_dots_678\0" + "braille_dots_68\0" + "braille_dots_7\0" + "braille_dots_78\0" + "braille_dots_8\0" + "Break\0" + "breve\0" + "brokenbar\0" + "Byelorussian_shortu\0" + "Byelorussian_SHORTU\0" + "C\0" + "c\0" + "c_h\0" + "C_h\0" + "C_H\0" + "Cabovedot\0" + "cabovedot\0" + "Cacute\0" + "cacute\0" + "Cancel\0" + "Caps_Lock\0" + "careof\0" + "caret\0" + "caron\0" + "Ccaron\0" + "ccaron\0" + "Ccedilla\0" + "ccedilla\0" + "Ccircumflex\0" + "ccircumflex\0" + "cedilla\0" + "cent\0" + "ch\0" + "Ch\0" + "CH\0" + "checkerboard\0" + "checkmark\0" + "circle\0" + "Clear\0" + "ClearLine\0" + "club\0" + "Codeinput\0" + "colon\0" + "ColonSign\0" + "comma\0" + "containsas\0" + "Control_L\0" + "Control_R\0" + "copyright\0" + "cr\0" + "crossinglines\0" + "CruzeiroSign\0" + "cuberoot\0" + "currency\0" + "cursor\0" + "Cyrillic_a\0" + "Cyrillic_A\0" + "Cyrillic_be\0" + "Cyrillic_BE\0" + "Cyrillic_che\0" + "Cyrillic_CHE\0" + "Cyrillic_CHE_descender\0" + "Cyrillic_che_descender\0" + "Cyrillic_CHE_vertstroke\0" + "Cyrillic_che_vertstroke\0" + "Cyrillic_de\0" + "Cyrillic_DE\0" + "Cyrillic_dzhe\0" + "Cyrillic_DZHE\0" + "Cyrillic_e\0" + "Cyrillic_E\0" + "Cyrillic_ef\0" + "Cyrillic_EF\0" + "Cyrillic_el\0" + "Cyrillic_EL\0" + "Cyrillic_em\0" + "Cyrillic_EM\0" + "Cyrillic_en\0" + "Cyrillic_EN\0" + "Cyrillic_EN_descender\0" + "Cyrillic_en_descender\0" + "Cyrillic_er\0" + "Cyrillic_ER\0" + "Cyrillic_es\0" + "Cyrillic_ES\0" + "Cyrillic_ghe\0" + "Cyrillic_GHE\0" + "Cyrillic_GHE_bar\0" + "Cyrillic_ghe_bar\0" + "Cyrillic_ha\0" + "Cyrillic_HA\0" + "Cyrillic_HA_descender\0" + "Cyrillic_ha_descender\0" + "Cyrillic_hardsign\0" + "Cyrillic_HARDSIGN\0" + "Cyrillic_i\0" + "Cyrillic_I\0" + "Cyrillic_I_macron\0" + "Cyrillic_i_macron\0" + "Cyrillic_ie\0" + "Cyrillic_IE\0" + "Cyrillic_io\0" + "Cyrillic_IO\0" + "Cyrillic_je\0" + "Cyrillic_JE\0" + "Cyrillic_ka\0" + "Cyrillic_KA\0" + "Cyrillic_KA_descender\0" + "Cyrillic_ka_descender\0" + "Cyrillic_KA_vertstroke\0" + "Cyrillic_ka_vertstroke\0" + "Cyrillic_lje\0" + "Cyrillic_LJE\0" + "Cyrillic_nje\0" + "Cyrillic_NJE\0" + "Cyrillic_o\0" + "Cyrillic_O\0" + "Cyrillic_O_bar\0" + "Cyrillic_o_bar\0" + "Cyrillic_pe\0" + "Cyrillic_PE\0" + "Cyrillic_SCHWA\0" + "Cyrillic_schwa\0" + "Cyrillic_sha\0" + "Cyrillic_SHA\0" + "Cyrillic_shcha\0" + "Cyrillic_SHCHA\0" + "Cyrillic_SHHA\0" + "Cyrillic_shha\0" + "Cyrillic_shorti\0" + "Cyrillic_SHORTI\0" + "Cyrillic_softsign\0" + "Cyrillic_SOFTSIGN\0" + "Cyrillic_te\0" + "Cyrillic_TE\0" + "Cyrillic_tse\0" + "Cyrillic_TSE\0" + "Cyrillic_u\0" + "Cyrillic_U\0" + "Cyrillic_U_macron\0" + "Cyrillic_u_macron\0" + "Cyrillic_U_straight\0" + "Cyrillic_u_straight\0" + "Cyrillic_U_straight_bar\0" + "Cyrillic_u_straight_bar\0" + "Cyrillic_ve\0" + "Cyrillic_VE\0" + "Cyrillic_ya\0" + "Cyrillic_YA\0" + "Cyrillic_yeru\0" + "Cyrillic_YERU\0" + "Cyrillic_yu\0" + "Cyrillic_YU\0" + "Cyrillic_ze\0" + "Cyrillic_ZE\0" + "Cyrillic_zhe\0" + "Cyrillic_ZHE\0" + "Cyrillic_ZHE_descender\0" + "Cyrillic_zhe_descender\0" + "D\0" + "d\0" + "Dabovedot\0" + "dabovedot\0" + "Dacute_accent\0" + "dagger\0" + "Dcaron\0" + "dcaron\0" + "Dcedilla_accent\0" + "Dcircumflex_accent\0" + "Ddiaeresis\0" + "dead_a\0" + "dead_A\0" + "dead_abovecomma\0" + "dead_abovedot\0" + "dead_abovereversedcomma\0" + "dead_abovering\0" + "dead_aboveverticalline\0" + "dead_acute\0" + "dead_belowbreve\0" + "dead_belowcircumflex\0" + "dead_belowcomma\0" + "dead_belowdiaeresis\0" + "dead_belowdot\0" + "dead_belowmacron\0" + "dead_belowring\0" + "dead_belowtilde\0" + "dead_belowverticalline\0" + "dead_breve\0" + "dead_capital_schwa\0" + "dead_caron\0" + "dead_cedilla\0" + "dead_circumflex\0" + "dead_currency\0" + "dead_dasia\0" + "dead_diaeresis\0" + "dead_doubleacute\0" + "dead_doublegrave\0" + "dead_e\0" + "dead_E\0" + "dead_grave\0" + "dead_greek\0" + "dead_hook\0" + "dead_horn\0" + "dead_i\0" + "dead_I\0" + "dead_invertedbreve\0" + "dead_iota\0" + "dead_longsolidusoverlay\0" + "dead_lowline\0" + "dead_macron\0" + "dead_o\0" + "dead_O\0" + "dead_ogonek\0" + "dead_perispomeni\0" + "dead_psili\0" + "dead_semivoiced_sound\0" + "dead_small_schwa\0" + "dead_stroke\0" + "dead_tilde\0" + "dead_u\0" + "dead_U\0" + "dead_voiced_sound\0" + "decimalpoint\0" + "degree\0" + "Delete\0" + "DeleteChar\0" + "DeleteLine\0" + "Dgrave_accent\0" + "diaeresis\0" + "diamond\0" + "digitspace\0" + "dintegral\0" + "division\0" + "dollar\0" + "DongSign\0" + "doubbaselinedot\0" + "doubleacute\0" + "doubledagger\0" + "doublelowquotemark\0" + "Down\0" + "downarrow\0" + "downcaret\0" + "downshoe\0" + "downstile\0" + "downtack\0" + "DRemove\0" + "Dring_accent\0" + "Dstroke\0" + "dstroke\0" + "Dtilde\0" + "E\0" + "e\0" + "Eabovedot\0" + "eabovedot\0" + "Eacute\0" + "eacute\0" + "Ebelowdot\0" + "ebelowdot\0" + "Ecaron\0" + "ecaron\0" + "Ecircumflex\0" + "ecircumflex\0" + "Ecircumflexacute\0" + "ecircumflexacute\0" + "Ecircumflexbelowdot\0" + "ecircumflexbelowdot\0" + "Ecircumflexgrave\0" + "ecircumflexgrave\0" + "Ecircumflexhook\0" + "ecircumflexhook\0" + "Ecircumflextilde\0" + "ecircumflextilde\0" + "EcuSign\0" + "Ediaeresis\0" + "ediaeresis\0" + "Egrave\0" + "egrave\0" + "Ehook\0" + "ehook\0" + "eightsubscript\0" + "eightsuperior\0" + "Eisu_Shift\0" + "Eisu_toggle\0" + "elementof\0" + "ellipsis\0" + "em3space\0" + "em4space\0" + "Emacron\0" + "emacron\0" + "emdash\0" + "emfilledcircle\0" + "emfilledrect\0" + "emopencircle\0" + "emopenrectangle\0" + "emptyset\0" + "emspace\0" + "End\0" + "endash\0" + "enfilledcircbullet\0" + "enfilledsqbullet\0" + "ENG\0" + "eng\0" + "enopencircbullet\0" + "enopensquarebullet\0" + "enspace\0" + "Eogonek\0" + "eogonek\0" + "equal\0" + "Escape\0" + "ETH\0" + "Eth\0" + "eth\0" + "Etilde\0" + "etilde\0" + "EuroSign\0" + "exclam\0" + "exclamdown\0" + "Execute\0" + "Ext16bit_L\0" + "Ext16bit_R\0" + "EZH\0" + "ezh\0" + "F\0" + "f\0" + "F1\0" + "F10\0" + "F11\0" + "F12\0" + "F13\0" + "F14\0" + "F15\0" + "F16\0" + "F17\0" + "F18\0" + "F19\0" + "F2\0" + "F20\0" + "F21\0" + "F22\0" + "F23\0" + "F24\0" + "F25\0" + "F26\0" + "F27\0" + "F28\0" + "F29\0" + "F3\0" + "F30\0" + "F31\0" + "F32\0" + "F33\0" + "F34\0" + "F35\0" + "F4\0" + "F5\0" + "F6\0" + "F7\0" + "F8\0" + "F9\0" + "Fabovedot\0" + "fabovedot\0" + "Farsi_0\0" + "Farsi_1\0" + "Farsi_2\0" + "Farsi_3\0" + "Farsi_4\0" + "Farsi_5\0" + "Farsi_6\0" + "Farsi_7\0" + "Farsi_8\0" + "Farsi_9\0" + "Farsi_yeh\0" + "femalesymbol\0" + "ff\0" + "FFrancSign\0" + "figdash\0" + "filledlefttribullet\0" + "filledrectbullet\0" + "filledrighttribullet\0" + "filledtribulletdown\0" + "filledtribulletup\0" + "Find\0" + "First_Virtual_Screen\0" + "fiveeighths\0" + "fivesixths\0" + "fivesubscript\0" + "fivesuperior\0" + "fourfifths\0" + "foursubscript\0" + "foursuperior\0" + "fourthroot\0" + "function\0" + "G\0" + "g\0" + "Gabovedot\0" + "gabovedot\0" + "Gbreve\0" + "gbreve\0" + "Gcaron\0" + "gcaron\0" + "Gcedilla\0" + "gcedilla\0" + "Gcircumflex\0" + "gcircumflex\0" + "Georgian_an\0" + "Georgian_ban\0" + "Georgian_can\0" + "Georgian_char\0" + "Georgian_chin\0" + "Georgian_cil\0" + "Georgian_don\0" + "Georgian_en\0" + "Georgian_fi\0" + "Georgian_gan\0" + "Georgian_ghan\0" + "Georgian_hae\0" + "Georgian_har\0" + "Georgian_he\0" + "Georgian_hie\0" + "Georgian_hoe\0" + "Georgian_in\0" + "Georgian_jhan\0" + "Georgian_jil\0" + "Georgian_kan\0" + "Georgian_khar\0" + "Georgian_las\0" + "Georgian_man\0" + "Georgian_nar\0" + "Georgian_on\0" + "Georgian_par\0" + "Georgian_phar\0" + "Georgian_qar\0" + "Georgian_rae\0" + "Georgian_san\0" + "Georgian_shin\0" + "Georgian_tan\0" + "Georgian_tar\0" + "Georgian_un\0" + "Georgian_vin\0" + "Georgian_we\0" + "Georgian_xan\0" + "Georgian_zen\0" + "Georgian_zhar\0" + "grave\0" + "greater\0" + "greaterthanequal\0" + "Greek_accentdieresis\0" + "Greek_ALPHA\0" + "Greek_alpha\0" + "Greek_ALPHAaccent\0" + "Greek_alphaaccent\0" + "Greek_BETA\0" + "Greek_beta\0" + "Greek_CHI\0" + "Greek_chi\0" + "Greek_DELTA\0" + "Greek_delta\0" + "Greek_EPSILON\0" + "Greek_epsilon\0" + "Greek_EPSILONaccent\0" + "Greek_epsilonaccent\0" + "Greek_ETA\0" + "Greek_eta\0" + "Greek_ETAaccent\0" + "Greek_etaaccent\0" + "Greek_finalsmallsigma\0" + "Greek_GAMMA\0" + "Greek_gamma\0" + "Greek_horizbar\0" + "Greek_IOTA\0" + "Greek_iota\0" + "Greek_IOTAaccent\0" + "Greek_iotaaccent\0" + "Greek_iotaaccentdieresis\0" + "Greek_IOTAdiaeresis\0" + "Greek_IOTAdieresis\0" + "Greek_iotadieresis\0" + "Greek_KAPPA\0" + "Greek_kappa\0" + "Greek_LAMBDA\0" + "Greek_lambda\0" + "Greek_LAMDA\0" + "Greek_lamda\0" + "Greek_MU\0" + "Greek_mu\0" + "Greek_NU\0" + "Greek_nu\0" + "Greek_OMEGA\0" + "Greek_omega\0" + "Greek_OMEGAaccent\0" + "Greek_omegaaccent\0" + "Greek_OMICRON\0" + "Greek_omicron\0" + "Greek_OMICRONaccent\0" + "Greek_omicronaccent\0" + "Greek_PHI\0" + "Greek_phi\0" + "Greek_PI\0" + "Greek_pi\0" + "Greek_PSI\0" + "Greek_psi\0" + "Greek_RHO\0" + "Greek_rho\0" + "Greek_SIGMA\0" + "Greek_sigma\0" + "Greek_switch\0" + "Greek_TAU\0" + "Greek_tau\0" + "Greek_THETA\0" + "Greek_theta\0" + "Greek_UPSILON\0" + "Greek_upsilon\0" + "Greek_UPSILONaccent\0" + "Greek_upsilonaccent\0" + "Greek_upsilonaccentdieresis\0" + "Greek_UPSILONdieresis\0" + "Greek_upsilondieresis\0" + "Greek_XI\0" + "Greek_xi\0" + "Greek_ZETA\0" + "Greek_zeta\0" + "guilder\0" + "guillemotleft\0" + "guillemotright\0" + "H\0" + "h\0" + "hairspace\0" + "Hangul\0" + "Hangul_A\0" + "Hangul_AE\0" + "Hangul_AraeA\0" + "Hangul_AraeAE\0" + "Hangul_Banja\0" + "Hangul_Cieuc\0" + "Hangul_Codeinput\0" + "Hangul_Dikeud\0" + "Hangul_E\0" + "Hangul_End\0" + "Hangul_EO\0" + "Hangul_EU\0" + "Hangul_Hanja\0" + "Hangul_Hieuh\0" + "Hangul_I\0" + "Hangul_Ieung\0" + "Hangul_J_Cieuc\0" + "Hangul_J_Dikeud\0" + "Hangul_J_Hieuh\0" + "Hangul_J_Ieung\0" + "Hangul_J_Jieuj\0" + "Hangul_J_Khieuq\0" + "Hangul_J_Kiyeog\0" + "Hangul_J_KiyeogSios\0" + "Hangul_J_KkogjiDalrinIeung\0" + "Hangul_J_Mieum\0" + "Hangul_J_Nieun\0" + "Hangul_J_NieunHieuh\0" + "Hangul_J_NieunJieuj\0" + "Hangul_J_PanSios\0" + "Hangul_J_Phieuf\0" + "Hangul_J_Pieub\0" + "Hangul_J_PieubSios\0" + "Hangul_J_Rieul\0" + "Hangul_J_RieulHieuh\0" + "Hangul_J_RieulKiyeog\0" + "Hangul_J_RieulMieum\0" + "Hangul_J_RieulPhieuf\0" + "Hangul_J_RieulPieub\0" + "Hangul_J_RieulSios\0" + "Hangul_J_RieulTieut\0" + "Hangul_J_Sios\0" + "Hangul_J_SsangKiyeog\0" + "Hangul_J_SsangSios\0" + "Hangul_J_Tieut\0" + "Hangul_J_YeorinHieuh\0" + "Hangul_Jamo\0" + "Hangul_Jeonja\0" + "Hangul_Jieuj\0" + "Hangul_Khieuq\0" + "Hangul_Kiyeog\0" + "Hangul_KiyeogSios\0" + "Hangul_KkogjiDalrinIeung\0" + "Hangul_Mieum\0" + "Hangul_MultipleCandidate\0" + "Hangul_Nieun\0" + "Hangul_NieunHieuh\0" + "Hangul_NieunJieuj\0" + "Hangul_O\0" + "Hangul_OE\0" + "Hangul_PanSios\0" + "Hangul_Phieuf\0" + "Hangul_Pieub\0" + "Hangul_PieubSios\0" + "Hangul_PostHanja\0" + "Hangul_PreHanja\0" + "Hangul_PreviousCandidate\0" + "Hangul_Rieul\0" + "Hangul_RieulHieuh\0" + "Hangul_RieulKiyeog\0" + "Hangul_RieulMieum\0" + "Hangul_RieulPhieuf\0" + "Hangul_RieulPieub\0" + "Hangul_RieulSios\0" + "Hangul_RieulTieut\0" + "Hangul_RieulYeorinHieuh\0" + "Hangul_Romaja\0" + "Hangul_SingleCandidate\0" + "Hangul_Sios\0" + "Hangul_Special\0" + "Hangul_SsangDikeud\0" + "Hangul_SsangJieuj\0" + "Hangul_SsangKiyeog\0" + "Hangul_SsangPieub\0" + "Hangul_SsangSios\0" + "Hangul_Start\0" + "Hangul_SunkyeongeumMieum\0" + "Hangul_SunkyeongeumPhieuf\0" + "Hangul_SunkyeongeumPieub\0" + "Hangul_switch\0" + "Hangul_Tieut\0" + "Hangul_U\0" + "Hangul_WA\0" + "Hangul_WAE\0" + "Hangul_WE\0" + "Hangul_WEO\0" + "Hangul_WI\0" + "Hangul_YA\0" + "Hangul_YAE\0" + "Hangul_YE\0" + "Hangul_YEO\0" + "Hangul_YeorinHieuh\0" + "Hangul_YI\0" + "Hangul_YO\0" + "Hangul_YU\0" + "Hankaku\0" + "Hcircumflex\0" + "hcircumflex\0" + "heart\0" + "hebrew_aleph\0" + "hebrew_ayin\0" + "hebrew_bet\0" + "hebrew_beth\0" + "hebrew_chet\0" + "hebrew_dalet\0" + "hebrew_daleth\0" + "hebrew_doublelowline\0" + "hebrew_finalkaph\0" + "hebrew_finalmem\0" + "hebrew_finalnun\0" + "hebrew_finalpe\0" + "hebrew_finalzade\0" + "hebrew_finalzadi\0" + "hebrew_gimel\0" + "hebrew_gimmel\0" + "hebrew_he\0" + "hebrew_het\0" + "hebrew_kaph\0" + "hebrew_kuf\0" + "hebrew_lamed\0" + "hebrew_mem\0" + "hebrew_nun\0" + "hebrew_pe\0" + "hebrew_qoph\0" + "hebrew_resh\0" + "hebrew_samech\0" + "hebrew_samekh\0" + "hebrew_shin\0" + "Hebrew_switch\0" + "hebrew_taf\0" + "hebrew_taw\0" + "hebrew_tet\0" + "hebrew_teth\0" + "hebrew_waw\0" + "hebrew_yod\0" + "hebrew_zade\0" + "hebrew_zadi\0" + "hebrew_zain\0" + "hebrew_zayin\0" + "Help\0" + "Henkan\0" + "Henkan_Mode\0" + "hexagram\0" + "Hiragana\0" + "Hiragana_Katakana\0" + "Home\0" + "horizconnector\0" + "horizlinescan1\0" + "horizlinescan3\0" + "horizlinescan5\0" + "horizlinescan7\0" + "horizlinescan9\0" + "hpBackTab\0" + "hpblock\0" + "hpClearLine\0" + "hpDeleteChar\0" + "hpDeleteLine\0" + "hpguilder\0" + "hpInsertChar\0" + "hpInsertLine\0" + "hpIO\0" + "hpKP_BackTab\0" + "hplira\0" + "hplongminus\0" + "hpModelock1\0" + "hpModelock2\0" + "hpmute_acute\0" + "hpmute_asciicircum\0" + "hpmute_asciitilde\0" + "hpmute_diaeresis\0" + "hpmute_grave\0" + "hpReset\0" + "hpSystem\0" + "hpUser\0" + "hpYdiaeresis\0" + "Hstroke\0" + "hstroke\0" + "ht\0" + "Hyper_L\0" + "Hyper_R\0" + "hyphen\0" + "I\0" + "i\0" + "Iabovedot\0" + "Iacute\0" + "iacute\0" + "Ibelowdot\0" + "ibelowdot\0" + "Ibreve\0" + "ibreve\0" + "Icircumflex\0" + "icircumflex\0" + "identical\0" + "Idiaeresis\0" + "idiaeresis\0" + "idotless\0" + "ifonlyif\0" + "Igrave\0" + "igrave\0" + "Ihook\0" + "ihook\0" + "Imacron\0" + "imacron\0" + "implies\0" + "includedin\0" + "includes\0" + "infinity\0" + "Insert\0" + "InsertChar\0" + "InsertLine\0" + "integral\0" + "intersection\0" + "IO\0" + "Iogonek\0" + "iogonek\0" + "ISO_Center_Object\0" + "ISO_Continuous_Underline\0" + "ISO_Discontinuous_Underline\0" + "ISO_Emphasize\0" + "ISO_Enter\0" + "ISO_Fast_Cursor_Down\0" + "ISO_Fast_Cursor_Left\0" + "ISO_Fast_Cursor_Right\0" + "ISO_Fast_Cursor_Up\0" + "ISO_First_Group\0" + "ISO_First_Group_Lock\0" + "ISO_Group_Latch\0" + "ISO_Group_Lock\0" + "ISO_Group_Shift\0" + "ISO_Last_Group\0" + "ISO_Last_Group_Lock\0" + "ISO_Left_Tab\0" + "ISO_Level2_Latch\0" + "ISO_Level3_Latch\0" + "ISO_Level3_Lock\0" + "ISO_Level3_Shift\0" + "ISO_Level5_Latch\0" + "ISO_Level5_Lock\0" + "ISO_Level5_Shift\0" + "ISO_Lock\0" + "ISO_Move_Line_Down\0" + "ISO_Move_Line_Up\0" + "ISO_Next_Group\0" + "ISO_Next_Group_Lock\0" + "ISO_Partial_Line_Down\0" + "ISO_Partial_Line_Up\0" + "ISO_Partial_Space_Left\0" + "ISO_Partial_Space_Right\0" + "ISO_Prev_Group\0" + "ISO_Prev_Group_Lock\0" + "ISO_Release_Both_Margins\0" + "ISO_Release_Margin_Left\0" + "ISO_Release_Margin_Right\0" + "ISO_Set_Margin_Left\0" + "ISO_Set_Margin_Right\0" + "Itilde\0" + "itilde\0" + "J\0" + "j\0" + "Jcircumflex\0" + "jcircumflex\0" + "jot\0" + "K\0" + "k\0" + "kana_a\0" + "kana_A\0" + "kana_CHI\0" + "kana_closingbracket\0" + "kana_comma\0" + "kana_conjunctive\0" + "kana_e\0" + "kana_E\0" + "kana_FU\0" + "kana_fullstop\0" + "kana_HA\0" + "kana_HE\0" + "kana_HI\0" + "kana_HO\0" + "kana_HU\0" + "kana_i\0" + "kana_I\0" + "kana_KA\0" + "kana_KE\0" + "kana_KI\0" + "kana_KO\0" + "kana_KU\0" + "Kana_Lock\0" + "kana_MA\0" + "kana_ME\0" + "kana_MI\0" + "kana_middledot\0" + "kana_MO\0" + "kana_MU\0" + "kana_N\0" + "kana_NA\0" + "kana_NE\0" + "kana_NI\0" + "kana_NO\0" + "kana_NU\0" + "kana_o\0" + "kana_O\0" + "kana_openingbracket\0" + "kana_RA\0" + "kana_RE\0" + "kana_RI\0" + "kana_RO\0" + "kana_RU\0" + "kana_SA\0" + "kana_SE\0" + "kana_SHI\0" + "Kana_Shift\0" + "kana_SO\0" + "kana_SU\0" + "kana_switch\0" + "kana_TA\0" + "kana_TE\0" + "kana_TI\0" + "kana_TO\0" + "kana_tsu\0" + "kana_TSU\0" + "kana_tu\0" + "kana_TU\0" + "kana_u\0" + "kana_U\0" + "kana_WA\0" + "kana_WO\0" + "kana_ya\0" + "kana_YA\0" + "kana_yo\0" + "kana_YO\0" + "kana_yu\0" + "kana_YU\0" + "Kanji\0" + "Kanji_Bangou\0" + "kappa\0" + "Katakana\0" + "Kcedilla\0" + "kcedilla\0" + "Korean_Won\0" + "KP_0\0" + "KP_1\0" + "KP_2\0" + "KP_3\0" + "KP_4\0" + "KP_5\0" + "KP_6\0" + "KP_7\0" + "KP_8\0" + "KP_9\0" + "KP_Add\0" + "KP_BackTab\0" + "KP_Begin\0" + "KP_Decimal\0" + "KP_Delete\0" + "KP_Divide\0" + "KP_Down\0" + "KP_End\0" + "KP_Enter\0" + "KP_Equal\0" + "KP_F1\0" + "KP_F2\0" + "KP_F3\0" + "KP_F4\0" + "KP_Home\0" + "KP_Insert\0" + "KP_Left\0" + "KP_Multiply\0" + "KP_Next\0" + "KP_Page_Down\0" + "KP_Page_Up\0" + "KP_Prior\0" + "KP_Right\0" + "KP_Separator\0" + "KP_Space\0" + "KP_Subtract\0" + "KP_Tab\0" + "KP_Up\0" + "kra\0" + "L\0" + "l\0" + "L1\0" + "L10\0" + "L2\0" + "L3\0" + "L4\0" + "L5\0" + "L6\0" + "L7\0" + "L8\0" + "L9\0" + "Lacute\0" + "lacute\0" + "Last_Virtual_Screen\0" + "latincross\0" + "Lbelowdot\0" + "lbelowdot\0" + "Lcaron\0" + "lcaron\0" + "Lcedilla\0" + "lcedilla\0" + "Left\0" + "leftanglebracket\0" + "leftarrow\0" + "leftcaret\0" + "leftdoublequotemark\0" + "leftmiddlecurlybrace\0" + "leftopentriangle\0" + "leftpointer\0" + "leftradical\0" + "leftshoe\0" + "leftsinglequotemark\0" + "leftt\0" + "lefttack\0" + "less\0" + "lessthanequal\0" + "lf\0" + "Linefeed\0" + "lira\0" + "LiraSign\0" + "logicaland\0" + "logicalor\0" + "longminus\0" + "lowleftcorner\0" + "lowrightcorner\0" + "Lstroke\0" + "lstroke\0" + "M\0" + "m\0" + "Mabovedot\0" + "mabovedot\0" + "Macedonia_dse\0" + "Macedonia_DSE\0" + "Macedonia_gje\0" + "Macedonia_GJE\0" + "Macedonia_kje\0" + "Macedonia_KJE\0" + "macron\0" + "Mae_Koho\0" + "malesymbol\0" + "maltesecross\0" + "marker\0" + "masculine\0" + "Massyo\0" + "Menu\0" + "Meta_L\0" + "Meta_R\0" + "MillSign\0" + "minus\0" + "minutes\0" + "Mode_switch\0" + "MouseKeys_Accel_Enable\0" + "MouseKeys_Enable\0" + "mu\0" + "Muhenkan\0" + "Multi_key\0" + "MultipleCandidate\0" + "multiply\0" + "musicalflat\0" + "musicalsharp\0" + "mute_acute\0" + "mute_asciicircum\0" + "mute_asciitilde\0" + "mute_diaeresis\0" + "mute_grave\0" + "N\0" + "n\0" + "nabla\0" + "Nacute\0" + "nacute\0" + "NairaSign\0" + "Ncaron\0" + "ncaron\0" + "Ncedilla\0" + "ncedilla\0" + "NewSheqelSign\0" + "Next\0" + "Next_Virtual_Screen\0" + "ninesubscript\0" + "ninesuperior\0" + "nl\0" + "nobreakspace\0" + "NoSymbol\0" + "notapproxeq\0" + "notelementof\0" + "notequal\0" + "notidentical\0" + "notsign\0" + "Ntilde\0" + "ntilde\0" + "Num_Lock\0" + "numbersign\0" + "numerosign\0" + "O\0" + "o\0" + "Oacute\0" + "oacute\0" + "Obarred\0" + "obarred\0" + "Obelowdot\0" + "obelowdot\0" + "Ocaron\0" + "ocaron\0" + "Ocircumflex\0" + "ocircumflex\0" + "Ocircumflexacute\0" + "ocircumflexacute\0" + "Ocircumflexbelowdot\0" + "ocircumflexbelowdot\0" + "Ocircumflexgrave\0" + "ocircumflexgrave\0" + "Ocircumflexhook\0" + "ocircumflexhook\0" + "Ocircumflextilde\0" + "ocircumflextilde\0" + "Odiaeresis\0" + "odiaeresis\0" + "Odoubleacute\0" + "odoubleacute\0" + "OE\0" + "oe\0" + "ogonek\0" + "Ograve\0" + "ograve\0" + "Ohook\0" + "ohook\0" + "Ohorn\0" + "ohorn\0" + "Ohornacute\0" + "ohornacute\0" + "Ohornbelowdot\0" + "ohornbelowdot\0" + "Ohorngrave\0" + "ohorngrave\0" + "Ohornhook\0" + "ohornhook\0" + "Ohorntilde\0" + "ohorntilde\0" + "Omacron\0" + "omacron\0" + "oneeighth\0" + "onefifth\0" + "onehalf\0" + "onequarter\0" + "onesixth\0" + "onesubscript\0" + "onesuperior\0" + "onethird\0" + "Ooblique\0" + "ooblique\0" + "openrectbullet\0" + "openstar\0" + "opentribulletdown\0" + "opentribulletup\0" + "ordfeminine\0" + "osfActivate\0" + "osfAddMode\0" + "osfBackSpace\0" + "osfBackTab\0" + "osfBeginData\0" + "osfBeginLine\0" + "osfCancel\0" + "osfClear\0" + "osfCopy\0" + "osfCut\0" + "osfDelete\0" + "osfDeselectAll\0" + "osfDown\0" + "osfEndData\0" + "osfEndLine\0" + "osfEscape\0" + "osfExtend\0" + "osfHelp\0" + "osfInsert\0" + "osfLeft\0" + "osfMenu\0" + "osfMenuBar\0" + "osfNextField\0" + "osfNextMenu\0" + "osfPageDown\0" + "osfPageLeft\0" + "osfPageRight\0" + "osfPageUp\0" + "osfPaste\0" + "osfPrevField\0" + "osfPrevMenu\0" + "osfPrimaryPaste\0" + "osfQuickPaste\0" + "osfReselect\0" + "osfRestore\0" + "osfRight\0" + "osfSelect\0" + "osfSelectAll\0" + "osfUndo\0" + "osfUp\0" + "Oslash\0" + "oslash\0" + "Otilde\0" + "otilde\0" + "overbar\0" + "Overlay1_Enable\0" + "Overlay2_Enable\0" + "overline\0" + "P\0" + "p\0" + "Pabovedot\0" + "pabovedot\0" + "Page_Down\0" + "Page_Up\0" + "paragraph\0" + "parenleft\0" + "parenright\0" + "partdifferential\0" + "partialderivative\0" + "Pause\0" + "percent\0" + "period\0" + "periodcentered\0" + "permille\0" + "PesetaSign\0" + "phonographcopyright\0" + "plus\0" + "plusminus\0" + "Pointer_Accelerate\0" + "Pointer_Button1\0" + "Pointer_Button2\0" + "Pointer_Button3\0" + "Pointer_Button4\0" + "Pointer_Button5\0" + "Pointer_Button_Dflt\0" + "Pointer_DblClick1\0" + "Pointer_DblClick2\0" + "Pointer_DblClick3\0" + "Pointer_DblClick4\0" + "Pointer_DblClick5\0" + "Pointer_DblClick_Dflt\0" + "Pointer_DfltBtnNext\0" + "Pointer_DfltBtnPrev\0" + "Pointer_Down\0" + "Pointer_DownLeft\0" + "Pointer_DownRight\0" + "Pointer_Drag1\0" + "Pointer_Drag2\0" + "Pointer_Drag3\0" + "Pointer_Drag4\0" + "Pointer_Drag5\0" + "Pointer_Drag_Dflt\0" + "Pointer_EnableKeys\0" + "Pointer_Left\0" + "Pointer_Right\0" + "Pointer_Up\0" + "Pointer_UpLeft\0" + "Pointer_UpRight\0" + "prescription\0" + "Prev_Virtual_Screen\0" + "PreviousCandidate\0" + "Print\0" + "Prior\0" + "prolongedsound\0" + "punctspace\0" + "Q\0" + "q\0" + "quad\0" + "question\0" + "questiondown\0" + "quotedbl\0" + "quoteleft\0" + "quoteright\0" + "R\0" + "r\0" + "R1\0" + "R10\0" + "R11\0" + "R12\0" + "R13\0" + "R14\0" + "R15\0" + "R2\0" + "R3\0" + "R4\0" + "R5\0" + "R6\0" + "R7\0" + "R8\0" + "R9\0" + "Racute\0" + "racute\0" + "radical\0" + "Rcaron\0" + "rcaron\0" + "Rcedilla\0" + "rcedilla\0" + "Redo\0" + "registered\0" + "RepeatKeys_Enable\0" + "Reset\0" + "Return\0" + "Right\0" + "rightanglebracket\0" + "rightarrow\0" + "rightcaret\0" + "rightdoublequotemark\0" + "rightmiddlecurlybrace\0" + "rightmiddlesummation\0" + "rightopentriangle\0" + "rightpointer\0" + "rightshoe\0" + "rightsinglequotemark\0" + "rightt\0" + "righttack\0" + "Romaji\0" + "RupeeSign\0" + "S\0" + "s\0" + "Sabovedot\0" + "sabovedot\0" + "Sacute\0" + "sacute\0" + "Scaron\0" + "scaron\0" + "Scedilla\0" + "scedilla\0" + "SCHWA\0" + "schwa\0" + "Scircumflex\0" + "scircumflex\0" + "script_switch\0" + "Scroll_Lock\0" + "seconds\0" + "section\0" + "Select\0" + "semicolon\0" + "semivoicedsound\0" + "Serbian_dje\0" + "Serbian_DJE\0" + "Serbian_dze\0" + "Serbian_DZE\0" + "Serbian_je\0" + "Serbian_JE\0" + "Serbian_lje\0" + "Serbian_LJE\0" + "Serbian_nje\0" + "Serbian_NJE\0" + "Serbian_tshe\0" + "Serbian_TSHE\0" + "seveneighths\0" + "sevensubscript\0" + "sevensuperior\0" + "Shift_L\0" + "Shift_Lock\0" + "Shift_R\0" + "signaturemark\0" + "signifblank\0" + "similarequal\0" + "SingleCandidate\0" + "singlelowquotemark\0" + "Sinh_a\0" + "Sinh_aa\0" + "Sinh_aa2\0" + "Sinh_ae\0" + "Sinh_ae2\0" + "Sinh_aee\0" + "Sinh_aee2\0" + "Sinh_ai\0" + "Sinh_ai2\0" + "Sinh_al\0" + "Sinh_au\0" + "Sinh_au2\0" + "Sinh_ba\0" + "Sinh_bha\0" + "Sinh_ca\0" + "Sinh_cha\0" + "Sinh_dda\0" + "Sinh_ddha\0" + "Sinh_dha\0" + "Sinh_dhha\0" + "Sinh_e\0" + "Sinh_e2\0" + "Sinh_ee\0" + "Sinh_ee2\0" + "Sinh_fa\0" + "Sinh_ga\0" + "Sinh_gha\0" + "Sinh_h2\0" + "Sinh_ha\0" + "Sinh_i\0" + "Sinh_i2\0" + "Sinh_ii\0" + "Sinh_ii2\0" + "Sinh_ja\0" + "Sinh_jha\0" + "Sinh_jnya\0" + "Sinh_ka\0" + "Sinh_kha\0" + "Sinh_kunddaliya\0" + "Sinh_la\0" + "Sinh_lla\0" + "Sinh_lu\0" + "Sinh_lu2\0" + "Sinh_luu\0" + "Sinh_luu2\0" + "Sinh_ma\0" + "Sinh_mba\0" + "Sinh_na\0" + "Sinh_ndda\0" + "Sinh_ndha\0" + "Sinh_ng\0" + "Sinh_ng2\0" + "Sinh_nga\0" + "Sinh_nja\0" + "Sinh_nna\0" + "Sinh_nya\0" + "Sinh_o\0" + "Sinh_o2\0" + "Sinh_oo\0" + "Sinh_oo2\0" + "Sinh_pa\0" + "Sinh_pha\0" + "Sinh_ra\0" + "Sinh_ri\0" + "Sinh_rii\0" + "Sinh_ru2\0" + "Sinh_ruu2\0" + "Sinh_sa\0" + "Sinh_sha\0" + "Sinh_ssha\0" + "Sinh_tha\0" + "Sinh_thha\0" + "Sinh_tta\0" + "Sinh_ttha\0" + "Sinh_u\0" + "Sinh_u2\0" + "Sinh_uu\0" + "Sinh_uu2\0" + "Sinh_va\0" + "Sinh_ya\0" + "sixsubscript\0" + "sixsuperior\0" + "slash\0" + "SlowKeys_Enable\0" + "soliddiamond\0" + "space\0" + "squareroot\0" + "ssharp\0" + "sterling\0" + "StickyKeys_Enable\0" + "stricteq\0" + "SunAgain\0" + "SunAltGraph\0" + "SunAudioLowerVolume\0" + "SunAudioMute\0" + "SunAudioRaiseVolume\0" + "SunCompose\0" + "SunCopy\0" + "SunCut\0" + "SunF36\0" + "SunF37\0" + "SunFA_Acute\0" + "SunFA_Cedilla\0" + "SunFA_Circum\0" + "SunFA_Diaeresis\0" + "SunFA_Grave\0" + "SunFA_Tilde\0" + "SunFind\0" + "SunFront\0" + "SunOpen\0" + "SunPageDown\0" + "SunPageUp\0" + "SunPaste\0" + "SunPowerSwitch\0" + "SunPowerSwitchShift\0" + "SunPrint_Screen\0" + "SunProps\0" + "SunStop\0" + "SunSys_Req\0" + "SunUndo\0" + "SunVideoDegauss\0" + "SunVideoLowerBrightness\0" + "SunVideoRaiseBrightness\0" + "Super_L\0" + "Super_R\0" + "Sys_Req\0" + "System\0" + "T\0" + "t\0" + "Tab\0" + "Tabovedot\0" + "tabovedot\0" + "Tcaron\0" + "tcaron\0" + "Tcedilla\0" + "tcedilla\0" + "telephone\0" + "telephonerecorder\0" + "Terminate_Server\0" + "Thai_baht\0" + "Thai_bobaimai\0" + "Thai_chochan\0" + "Thai_chochang\0" + "Thai_choching\0" + "Thai_chochoe\0" + "Thai_dochada\0" + "Thai_dodek\0" + "Thai_fofa\0" + "Thai_fofan\0" + "Thai_hohip\0" + "Thai_honokhuk\0" + "Thai_khokhai\0" + "Thai_khokhon\0" + "Thai_khokhuat\0" + "Thai_khokhwai\0" + "Thai_khorakhang\0" + "Thai_kokai\0" + "Thai_lakkhangyao\0" + "Thai_lekchet\0" + "Thai_lekha\0" + "Thai_lekhok\0" + "Thai_lekkao\0" + "Thai_leknung\0" + "Thai_lekpaet\0" + "Thai_leksam\0" + "Thai_leksi\0" + "Thai_leksong\0" + "Thai_leksun\0" + "Thai_lochula\0" + "Thai_loling\0" + "Thai_lu\0" + "Thai_maichattawa\0" + "Thai_maiek\0" + "Thai_maihanakat\0" + "Thai_maihanakat_maitho\0" + "Thai_maitaikhu\0" + "Thai_maitho\0" + "Thai_maitri\0" + "Thai_maiyamok\0" + "Thai_moma\0" + "Thai_ngongu\0" + "Thai_nikhahit\0" + "Thai_nonen\0" + "Thai_nonu\0" + "Thai_oang\0" + "Thai_paiyannoi\0" + "Thai_phinthu\0" + "Thai_phophan\0" + "Thai_phophung\0" + "Thai_phosamphao\0" + "Thai_popla\0" + "Thai_rorua\0" + "Thai_ru\0" + "Thai_saraa\0" + "Thai_saraaa\0" + "Thai_saraae\0" + "Thai_saraaimaimalai\0" + "Thai_saraaimaimuan\0" + "Thai_saraam\0" + "Thai_sarae\0" + "Thai_sarai\0" + "Thai_saraii\0" + "Thai_sarao\0" + "Thai_sarau\0" + "Thai_saraue\0" + "Thai_sarauee\0" + "Thai_sarauu\0" + "Thai_sorusi\0" + "Thai_sosala\0" + "Thai_soso\0" + "Thai_sosua\0" + "Thai_thanthakhat\0" + "Thai_thonangmontho\0" + "Thai_thophuthao\0" + "Thai_thothahan\0" + "Thai_thothan\0" + "Thai_thothong\0" + "Thai_thothung\0" + "Thai_topatak\0" + "Thai_totao\0" + "Thai_wowaen\0" + "Thai_yoyak\0" + "Thai_yoying\0" + "therefore\0" + "thinspace\0" + "THORN\0" + "Thorn\0" + "thorn\0" + "threeeighths\0" + "threefifths\0" + "threequarters\0" + "threesubscript\0" + "threesuperior\0" + "tintegral\0" + "topintegral\0" + "topleftparens\0" + "topleftradical\0" + "topleftsqbracket\0" + "topleftsummation\0" + "toprightparens\0" + "toprightsqbracket\0" + "toprightsummation\0" + "topt\0" + "topvertsummationconnector\0" + "Touroku\0" + "trademark\0" + "trademarkincircle\0" + "Tslash\0" + "tslash\0" + "twofifths\0" + "twosubscript\0" + "twosuperior\0" + "twothirds\0" + "U\0" + "u\0" + "Uacute\0" + "uacute\0" + "Ubelowdot\0" + "ubelowdot\0" + "Ubreve\0" + "ubreve\0" + "Ucircumflex\0" + "ucircumflex\0" + "Udiaeresis\0" + "udiaeresis\0" + "Udoubleacute\0" + "udoubleacute\0" + "Ugrave\0" + "ugrave\0" + "Uhook\0" + "uhook\0" + "Uhorn\0" + "uhorn\0" + "Uhornacute\0" + "uhornacute\0" + "Uhornbelowdot\0" + "uhornbelowdot\0" + "Uhorngrave\0" + "uhorngrave\0" + "Uhornhook\0" + "uhornhook\0" + "Uhorntilde\0" + "uhorntilde\0" + "Ukrainian_ghe_with_upturn\0" + "Ukrainian_GHE_WITH_UPTURN\0" + "Ukrainian_i\0" + "Ukrainian_I\0" + "Ukrainian_ie\0" + "Ukrainian_IE\0" + "Ukrainian_yi\0" + "Ukrainian_YI\0" + "Ukranian_i\0" + "Ukranian_I\0" + "Ukranian_je\0" + "Ukranian_JE\0" + "Ukranian_yi\0" + "Ukranian_YI\0" + "Umacron\0" + "umacron\0" + "underbar\0" + "underscore\0" + "Undo\0" + "union\0" + "Uogonek\0" + "uogonek\0" + "Up\0" + "uparrow\0" + "upcaret\0" + "upleftcorner\0" + "uprightcorner\0" + "upshoe\0" + "upstile\0" + "uptack\0" + "Uring\0" + "uring\0" + "User\0" + "Utilde\0" + "utilde\0" + "V\0" + "v\0" + "variation\0" + "vertbar\0" + "vertconnector\0" + "voicedsound\0" + "VoidSymbol\0" + "vt\0" + "W\0" + "w\0" + "Wacute\0" + "wacute\0" + "Wcircumflex\0" + "wcircumflex\0" + "Wdiaeresis\0" + "wdiaeresis\0" + "Wgrave\0" + "wgrave\0" + "WonSign\0" + "X\0" + "x\0" + "Xabovedot\0" + "xabovedot\0" + "XF86AddFavorite\0" + "XF86ApplicationLeft\0" + "XF86ApplicationRight\0" + "XF86AudioCycleTrack\0" + "XF86AudioForward\0" + "XF86AudioLowerVolume\0" + "XF86AudioMedia\0" + "XF86AudioMicMute\0" + "XF86AudioMute\0" + "XF86AudioNext\0" + "XF86AudioPause\0" + "XF86AudioPlay\0" + "XF86AudioPreset\0" + "XF86AudioPrev\0" + "XF86AudioRaiseVolume\0" + "XF86AudioRandomPlay\0" + "XF86AudioRecord\0" + "XF86AudioRepeat\0" + "XF86AudioRewind\0" + "XF86AudioStop\0" + "XF86Away\0" + "XF86Back\0" + "XF86BackForward\0" + "XF86Battery\0" + "XF86Blue\0" + "XF86Bluetooth\0" + "XF86Book\0" + "XF86BrightnessAdjust\0" + "XF86Calculater\0" + "XF86Calculator\0" + "XF86Calendar\0" + "XF86CD\0" + "XF86Clear\0" + "XF86ClearGrab\0" + "XF86Close\0" + "XF86Community\0" + "XF86ContrastAdjust\0" + "XF86Copy\0" + "XF86Cut\0" + "XF86CycleAngle\0" + "XF86Display\0" + "XF86Documents\0" + "XF86DOS\0" + "XF86Eject\0" + "XF86Excel\0" + "XF86Explorer\0" + "XF86Favorites\0" + "XF86Finance\0" + "XF86Forward\0" + "XF86FrameBack\0" + "XF86FrameForward\0" + "XF86FullScreen\0" + "XF86Game\0" + "XF86Go\0" + "XF86Green\0" + "XF86Hibernate\0" + "XF86History\0" + "XF86HomePage\0" + "XF86HotLinks\0" + "XF86iTouch\0" + "XF86KbdBrightnessDown\0" + "XF86KbdBrightnessUp\0" + "XF86KbdLightOnOff\0" + "XF86Keyboard\0" + "XF86Launch0\0" + "XF86Launch1\0" + "XF86Launch2\0" + "XF86Launch3\0" + "XF86Launch4\0" + "XF86Launch5\0" + "XF86Launch6\0" + "XF86Launch7\0" + "XF86Launch8\0" + "XF86Launch9\0" + "XF86LaunchA\0" + "XF86LaunchB\0" + "XF86LaunchC\0" + "XF86LaunchD\0" + "XF86LaunchE\0" + "XF86LaunchF\0" + "XF86LightBulb\0" + "XF86LogGrabInfo\0" + "XF86LogOff\0" + "XF86LogWindowTree\0" + "XF86Mail\0" + "XF86MailForward\0" + "XF86Market\0" + "XF86Meeting\0" + "XF86Memo\0" + "XF86MenuKB\0" + "XF86MenuPB\0" + "XF86Messenger\0" + "XF86ModeLock\0" + "XF86MonBrightnessCycle\0" + "XF86MonBrightnessDown\0" + "XF86MonBrightnessUp\0" + "XF86Music\0" + "XF86MyComputer\0" + "XF86MySites\0" + "XF86New\0" + "XF86News\0" + "XF86Next_VMode\0" + "XF86OfficeHome\0" + "XF86Open\0" + "XF86OpenURL\0" + "XF86Option\0" + "XF86Paste\0" + "XF86Phone\0" + "XF86Pictures\0" + "XF86PowerDown\0" + "XF86PowerOff\0" + "XF86Prev_VMode\0" + "XF86Q\0" + "XF86Red\0" + "XF86Refresh\0" + "XF86Reload\0" + "XF86Reply\0" + "XF86RFKill\0" + "XF86RockerDown\0" + "XF86RockerEnter\0" + "XF86RockerUp\0" + "XF86RotateWindows\0" + "XF86RotationKB\0" + "XF86RotationLockToggle\0" + "XF86RotationPB\0" + "XF86Save\0" + "XF86ScreenSaver\0" + "XF86ScrollClick\0" + "XF86ScrollDown\0" + "XF86ScrollUp\0" + "XF86Search\0" + "XF86Select\0" + "XF86Send\0" + "XF86Shop\0" + "XF86Sleep\0" + "XF86Spell\0" + "XF86SplitScreen\0" + "XF86Standby\0" + "XF86Start\0" + "XF86Stop\0" + "XF86Subtitle\0" + "XF86Support\0" + "XF86Suspend\0" + "XF86Switch_VT_1\0" + "XF86Switch_VT_10\0" + "XF86Switch_VT_11\0" + "XF86Switch_VT_12\0" + "XF86Switch_VT_2\0" + "XF86Switch_VT_3\0" + "XF86Switch_VT_4\0" + "XF86Switch_VT_5\0" + "XF86Switch_VT_6\0" + "XF86Switch_VT_7\0" + "XF86Switch_VT_8\0" + "XF86Switch_VT_9\0" + "XF86TaskPane\0" + "XF86Terminal\0" + "XF86Time\0" + "XF86ToDoList\0" + "XF86Tools\0" + "XF86TopMenu\0" + "XF86TouchpadOff\0" + "XF86TouchpadOn\0" + "XF86TouchpadToggle\0" + "XF86Travel\0" + "XF86Ungrab\0" + "XF86User1KB\0" + "XF86User2KB\0" + "XF86UserPB\0" + "XF86UWB\0" + "XF86VendorHome\0" + "XF86Video\0" + "XF86View\0" + "XF86WakeUp\0" + "XF86WebCam\0" + "XF86WheelButton\0" + "XF86WLAN\0" + "XF86Word\0" + "XF86WWAN\0" + "XF86WWW\0" + "XF86Xfer\0" + "XF86Yellow\0" + "XF86ZoomIn\0" + "XF86ZoomOut\0" + "Y\0" + "y\0" + "Yacute\0" + "yacute\0" + "Ybelowdot\0" + "ybelowdot\0" + "Ycircumflex\0" + "ycircumflex\0" + "ydiaeresis\0" + "Ydiaeresis\0" + "yen\0" + "Ygrave\0" + "ygrave\0" + "Yhook\0" + "yhook\0" + "Ytilde\0" + "ytilde\0" + "Z\0" + "z\0" + "Zabovedot\0" + "zabovedot\0" + "Zacute\0" + "zacute\0" + "Zcaron\0" + "zcaron\0" + "Zen_Koho\0" + "Zenkaku\0" + "Zenkaku_Hankaku\0" + "zerosubscript\0" + "zerosuperior\0" + "Zstroke\0" + "zstroke\0" +; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +struct name_keysym { + xkb_keysym_t keysym; + uint32_t offset; +}; + +static const struct name_keysym name_to_keysym[] = { + { 0x00000030, 0 }, /* 0 */ + { 0x00000031, 2 }, /* 1 */ + { 0x00000032, 4 }, /* 2 */ + { 0x00000033, 6 }, /* 3 */ + { 0x0000fd10, 8 }, /* 3270_AltCursor */ + { 0x0000fd0e, 23 }, /* 3270_Attn */ + { 0x0000fd05, 33 }, /* 3270_BackTab */ + { 0x0000fd19, 46 }, /* 3270_ChangeScreen */ + { 0x0000fd15, 64 }, /* 3270_Copy */ + { 0x0000fd0f, 74 }, /* 3270_CursorBlink */ + { 0x0000fd1c, 91 }, /* 3270_CursorSelect */ + { 0x0000fd1a, 109 }, /* 3270_DeleteWord */ + { 0x0000fd01, 125 }, /* 3270_Duplicate */ + { 0x0000fd1e, 140 }, /* 3270_Enter */ + { 0x0000fd06, 151 }, /* 3270_EraseEOF */ + { 0x0000fd07, 165 }, /* 3270_EraseInput */ + { 0x0000fd1b, 181 }, /* 3270_ExSelect */ + { 0x0000fd02, 195 }, /* 3270_FieldMark */ + { 0x0000fd13, 210 }, /* 3270_Ident */ + { 0x0000fd12, 221 }, /* 3270_Jump */ + { 0x0000fd11, 231 }, /* 3270_KeyClick */ + { 0x0000fd04, 245 }, /* 3270_Left2 */ + { 0x0000fd0a, 256 }, /* 3270_PA1 */ + { 0x0000fd0b, 265 }, /* 3270_PA2 */ + { 0x0000fd0c, 274 }, /* 3270_PA3 */ + { 0x0000fd16, 283 }, /* 3270_Play */ + { 0x0000fd1d, 293 }, /* 3270_PrintScreen */ + { 0x0000fd09, 310 }, /* 3270_Quit */ + { 0x0000fd18, 320 }, /* 3270_Record */ + { 0x0000fd08, 332 }, /* 3270_Reset */ + { 0x0000fd03, 343 }, /* 3270_Right2 */ + { 0x0000fd14, 355 }, /* 3270_Rule */ + { 0x0000fd17, 365 }, /* 3270_Setup */ + { 0x0000fd0d, 376 }, /* 3270_Test */ + { 0x00000034, 386 }, /* 4 */ + { 0x00000035, 388 }, /* 5 */ + { 0x00000036, 390 }, /* 6 */ + { 0x00000037, 392 }, /* 7 */ + { 0x00000038, 394 }, /* 8 */ + { 0x00000039, 396 }, /* 9 */ + { 0x00000041, 398 }, /* A */ + { 0x00000061, 400 }, /* a */ + { 0x000000c1, 402 }, /* Aacute */ + { 0x000000e1, 409 }, /* aacute */ + { 0x01001ea0, 416 }, /* Abelowdot */ + { 0x01001ea1, 426 }, /* abelowdot */ + { 0x000001ff, 436 }, /* abovedot */ + { 0x000001c3, 445 }, /* Abreve */ + { 0x000001e3, 452 }, /* abreve */ + { 0x01001eae, 459 }, /* Abreveacute */ + { 0x01001eaf, 471 }, /* abreveacute */ + { 0x01001eb6, 483 }, /* Abrevebelowdot */ + { 0x01001eb7, 498 }, /* abrevebelowdot */ + { 0x01001eb0, 513 }, /* Abrevegrave */ + { 0x01001eb1, 525 }, /* abrevegrave */ + { 0x01001eb2, 537 }, /* Abrevehook */ + { 0x01001eb3, 548 }, /* abrevehook */ + { 0x01001eb4, 559 }, /* Abrevetilde */ + { 0x01001eb5, 571 }, /* abrevetilde */ + { 0x0000fe70, 583 }, /* AccessX_Enable */ + { 0x0000fe71, 598 }, /* AccessX_Feedback_Enable */ + { 0x000000c2, 622 }, /* Acircumflex */ + { 0x000000e2, 634 }, /* acircumflex */ + { 0x01001ea4, 646 }, /* Acircumflexacute */ + { 0x01001ea5, 663 }, /* acircumflexacute */ + { 0x01001eac, 680 }, /* Acircumflexbelowdot */ + { 0x01001ead, 700 }, /* acircumflexbelowdot */ + { 0x01001ea6, 720 }, /* Acircumflexgrave */ + { 0x01001ea7, 737 }, /* acircumflexgrave */ + { 0x01001ea8, 754 }, /* Acircumflexhook */ + { 0x01001ea9, 770 }, /* acircumflexhook */ + { 0x01001eaa, 786 }, /* Acircumflextilde */ + { 0x01001eab, 803 }, /* acircumflextilde */ + { 0x000000b4, 820 }, /* acute */ + { 0x000000c4, 826 }, /* Adiaeresis */ + { 0x000000e4, 837 }, /* adiaeresis */ + { 0x000000c6, 848 }, /* AE */ + { 0x000000e6, 851 }, /* ae */ + { 0x000000c0, 854 }, /* Agrave */ + { 0x000000e0, 861 }, /* agrave */ + { 0x01001ea2, 868 }, /* Ahook */ + { 0x01001ea3, 874 }, /* ahook */ + { 0x0000ffe9, 880 }, /* Alt_L */ + { 0x0000ffea, 886 }, /* Alt_R */ + { 0x000003c0, 892 }, /* Amacron */ + { 0x000003e0, 900 }, /* amacron */ + { 0x00000026, 908 }, /* ampersand */ + { 0x000001a1, 918 }, /* Aogonek */ + { 0x000001b1, 926 }, /* aogonek */ + { 0x00000027, 934 }, /* apostrophe */ + { 0x01002248, 945 }, /* approxeq */ + { 0x000008c8, 954 }, /* approximate */ + { 0x01000660, 966 }, /* Arabic_0 */ + { 0x01000661, 975 }, /* Arabic_1 */ + { 0x01000662, 984 }, /* Arabic_2 */ + { 0x01000663, 993 }, /* Arabic_3 */ + { 0x01000664, 1002 }, /* Arabic_4 */ + { 0x01000665, 1011 }, /* Arabic_5 */ + { 0x01000666, 1020 }, /* Arabic_6 */ + { 0x01000667, 1029 }, /* Arabic_7 */ + { 0x01000668, 1038 }, /* Arabic_8 */ + { 0x01000669, 1047 }, /* Arabic_9 */ + { 0x000005d9, 1056 }, /* Arabic_ain */ + { 0x000005c7, 1067 }, /* Arabic_alef */ + { 0x000005e9, 1079 }, /* Arabic_alefmaksura */ + { 0x000005c8, 1098 }, /* Arabic_beh */ + { 0x000005ac, 1109 }, /* Arabic_comma */ + { 0x000005d6, 1122 }, /* Arabic_dad */ + { 0x000005cf, 1133 }, /* Arabic_dal */ + { 0x000005ef, 1144 }, /* Arabic_damma */ + { 0x000005ec, 1157 }, /* Arabic_dammatan */ + { 0x01000688, 1173 }, /* Arabic_ddal */ + { 0x010006cc, 1185 }, /* Arabic_farsi_yeh */ + { 0x000005ee, 1202 }, /* Arabic_fatha */ + { 0x000005eb, 1215 }, /* Arabic_fathatan */ + { 0x000005e1, 1231 }, /* Arabic_feh */ + { 0x010006d4, 1242 }, /* Arabic_fullstop */ + { 0x010006af, 1258 }, /* Arabic_gaf */ + { 0x000005da, 1269 }, /* Arabic_ghain */ + { 0x000005e7, 1282 }, /* Arabic_ha */ + { 0x000005cd, 1292 }, /* Arabic_hah */ + { 0x000005c1, 1303 }, /* Arabic_hamza */ + { 0x01000654, 1316 }, /* Arabic_hamza_above */ + { 0x01000655, 1335 }, /* Arabic_hamza_below */ + { 0x000005c3, 1354 }, /* Arabic_hamzaonalef */ + { 0x000005c4, 1373 }, /* Arabic_hamzaonwaw */ + { 0x000005c6, 1391 }, /* Arabic_hamzaonyeh */ + { 0x000005c5, 1409 }, /* Arabic_hamzaunderalef */ + { 0x000005e7, 1431 }, /* Arabic_heh */ + { 0x010006be, 1442 }, /* Arabic_heh_doachashmee */ + { 0x010006c1, 1465 }, /* Arabic_heh_goal */ + { 0x000005cc, 1481 }, /* Arabic_jeem */ + { 0x01000698, 1493 }, /* Arabic_jeh */ + { 0x000005e3, 1504 }, /* Arabic_kaf */ + { 0x000005f0, 1515 }, /* Arabic_kasra */ + { 0x000005ed, 1528 }, /* Arabic_kasratan */ + { 0x010006a9, 1544 }, /* Arabic_keheh */ + { 0x000005ce, 1557 }, /* Arabic_khah */ + { 0x000005e4, 1569 }, /* Arabic_lam */ + { 0x01000653, 1580 }, /* Arabic_madda_above */ + { 0x000005c2, 1599 }, /* Arabic_maddaonalef */ + { 0x000005e5, 1618 }, /* Arabic_meem */ + { 0x000005e6, 1630 }, /* Arabic_noon */ + { 0x010006ba, 1642 }, /* Arabic_noon_ghunna */ + { 0x0100067e, 1661 }, /* Arabic_peh */ + { 0x0100066a, 1672 }, /* Arabic_percent */ + { 0x000005e2, 1687 }, /* Arabic_qaf */ + { 0x000005bf, 1698 }, /* Arabic_question_mark */ + { 0x000005d1, 1719 }, /* Arabic_ra */ + { 0x01000691, 1729 }, /* Arabic_rreh */ + { 0x000005d5, 1741 }, /* Arabic_sad */ + { 0x000005d3, 1752 }, /* Arabic_seen */ + { 0x000005bb, 1764 }, /* Arabic_semicolon */ + { 0x000005f1, 1781 }, /* Arabic_shadda */ + { 0x000005d4, 1795 }, /* Arabic_sheen */ + { 0x000005f2, 1808 }, /* Arabic_sukun */ + { 0x01000670, 1821 }, /* Arabic_superscript_alef */ + { 0x0000ff7e, 1845 }, /* Arabic_switch */ + { 0x000005d7, 1859 }, /* Arabic_tah */ + { 0x000005e0, 1870 }, /* Arabic_tatweel */ + { 0x01000686, 1885 }, /* Arabic_tcheh */ + { 0x000005ca, 1898 }, /* Arabic_teh */ + { 0x000005c9, 1909 }, /* Arabic_tehmarbuta */ + { 0x000005d0, 1927 }, /* Arabic_thal */ + { 0x000005cb, 1939 }, /* Arabic_theh */ + { 0x01000679, 1951 }, /* Arabic_tteh */ + { 0x010006a4, 1963 }, /* Arabic_veh */ + { 0x000005e8, 1974 }, /* Arabic_waw */ + { 0x000005ea, 1985 }, /* Arabic_yeh */ + { 0x010006d2, 1996 }, /* Arabic_yeh_baree */ + { 0x000005d8, 2013 }, /* Arabic_zah */ + { 0x000005d2, 2024 }, /* Arabic_zain */ + { 0x000000c5, 2036 }, /* Aring */ + { 0x000000e5, 2042 }, /* aring */ + { 0x0100055b, 2048 }, /* Armenian_accent */ + { 0x0100055c, 2064 }, /* Armenian_amanak */ + { 0x0100055a, 2080 }, /* Armenian_apostrophe */ + { 0x01000538, 2100 }, /* Armenian_AT */ + { 0x01000568, 2112 }, /* Armenian_at */ + { 0x01000531, 2124 }, /* Armenian_AYB */ + { 0x01000561, 2137 }, /* Armenian_ayb */ + { 0x01000532, 2150 }, /* Armenian_BEN */ + { 0x01000562, 2163 }, /* Armenian_ben */ + { 0x0100055d, 2176 }, /* Armenian_but */ + { 0x01000549, 2189 }, /* Armenian_CHA */ + { 0x01000579, 2202 }, /* Armenian_cha */ + { 0x01000534, 2215 }, /* Armenian_DA */ + { 0x01000564, 2227 }, /* Armenian_da */ + { 0x01000541, 2239 }, /* Armenian_DZA */ + { 0x01000571, 2252 }, /* Armenian_dza */ + { 0x01000537, 2265 }, /* Armenian_E */ + { 0x01000567, 2276 }, /* Armenian_e */ + { 0x0100055c, 2287 }, /* Armenian_exclam */ + { 0x01000556, 2303 }, /* Armenian_FE */ + { 0x01000586, 2315 }, /* Armenian_fe */ + { 0x01000589, 2327 }, /* Armenian_full_stop */ + { 0x01000542, 2346 }, /* Armenian_GHAT */ + { 0x01000572, 2360 }, /* Armenian_ghat */ + { 0x01000533, 2374 }, /* Armenian_GIM */ + { 0x01000563, 2387 }, /* Armenian_gim */ + { 0x01000545, 2400 }, /* Armenian_HI */ + { 0x01000575, 2412 }, /* Armenian_hi */ + { 0x01000540, 2424 }, /* Armenian_HO */ + { 0x01000570, 2436 }, /* Armenian_ho */ + { 0x0100058a, 2448 }, /* Armenian_hyphen */ + { 0x0100053b, 2464 }, /* Armenian_INI */ + { 0x0100056b, 2477 }, /* Armenian_ini */ + { 0x0100054b, 2490 }, /* Armenian_JE */ + { 0x0100057b, 2502 }, /* Armenian_je */ + { 0x01000554, 2514 }, /* Armenian_KE */ + { 0x01000584, 2526 }, /* Armenian_ke */ + { 0x0100053f, 2538 }, /* Armenian_KEN */ + { 0x0100056f, 2551 }, /* Armenian_ken */ + { 0x0100053d, 2564 }, /* Armenian_KHE */ + { 0x0100056d, 2577 }, /* Armenian_khe */ + { 0x01000587, 2590 }, /* Armenian_ligature_ew */ + { 0x0100053c, 2611 }, /* Armenian_LYUN */ + { 0x0100056c, 2625 }, /* Armenian_lyun */ + { 0x01000544, 2639 }, /* Armenian_MEN */ + { 0x01000574, 2652 }, /* Armenian_men */ + { 0x01000546, 2665 }, /* Armenian_NU */ + { 0x01000576, 2677 }, /* Armenian_nu */ + { 0x01000555, 2689 }, /* Armenian_O */ + { 0x01000585, 2700 }, /* Armenian_o */ + { 0x0100055e, 2711 }, /* Armenian_paruyk */ + { 0x0100054a, 2727 }, /* Armenian_PE */ + { 0x0100057a, 2739 }, /* Armenian_pe */ + { 0x01000553, 2751 }, /* Armenian_PYUR */ + { 0x01000583, 2765 }, /* Armenian_pyur */ + { 0x0100055e, 2779 }, /* Armenian_question */ + { 0x0100054c, 2797 }, /* Armenian_RA */ + { 0x0100057c, 2809 }, /* Armenian_ra */ + { 0x01000550, 2821 }, /* Armenian_RE */ + { 0x01000580, 2833 }, /* Armenian_re */ + { 0x0100054d, 2845 }, /* Armenian_SE */ + { 0x0100057d, 2857 }, /* Armenian_se */ + { 0x0100055d, 2869 }, /* Armenian_separation_mark */ + { 0x01000547, 2894 }, /* Armenian_SHA */ + { 0x01000577, 2907 }, /* Armenian_sha */ + { 0x0100055b, 2920 }, /* Armenian_shesht */ + { 0x01000543, 2936 }, /* Armenian_TCHE */ + { 0x01000573, 2950 }, /* Armenian_tche */ + { 0x01000539, 2964 }, /* Armenian_TO */ + { 0x01000569, 2976 }, /* Armenian_to */ + { 0x0100053e, 2988 }, /* Armenian_TSA */ + { 0x0100056e, 3001 }, /* Armenian_tsa */ + { 0x01000551, 3014 }, /* Armenian_TSO */ + { 0x01000581, 3027 }, /* Armenian_tso */ + { 0x0100054f, 3040 }, /* Armenian_TYUN */ + { 0x0100057f, 3054 }, /* Armenian_tyun */ + { 0x01000589, 3068 }, /* Armenian_verjaket */ + { 0x0100054e, 3086 }, /* Armenian_VEV */ + { 0x0100057e, 3099 }, /* Armenian_vev */ + { 0x01000548, 3112 }, /* Armenian_VO */ + { 0x01000578, 3124 }, /* Armenian_vo */ + { 0x01000552, 3136 }, /* Armenian_VYUN */ + { 0x01000582, 3150 }, /* Armenian_vyun */ + { 0x01000535, 3164 }, /* Armenian_YECH */ + { 0x01000565, 3178 }, /* Armenian_yech */ + { 0x0100058a, 3192 }, /* Armenian_yentamna */ + { 0x01000536, 3210 }, /* Armenian_ZA */ + { 0x01000566, 3222 }, /* Armenian_za */ + { 0x0100053a, 3234 }, /* Armenian_ZHE */ + { 0x0100056a, 3247 }, /* Armenian_zhe */ + { 0x0000005e, 3260 }, /* asciicircum */ + { 0x0000007e, 3272 }, /* asciitilde */ + { 0x0000002a, 3283 }, /* asterisk */ + { 0x00000040, 3292 }, /* at */ + { 0x000000c3, 3295 }, /* Atilde */ + { 0x000000e3, 3302 }, /* atilde */ + { 0x0000fe7a, 3309 }, /* AudibleBell_Enable */ + { 0x00000042, 3328 }, /* B */ + { 0x00000062, 3330 }, /* b */ + { 0x01001e02, 3332 }, /* Babovedot */ + { 0x01001e03, 3342 }, /* babovedot */ + { 0x0000005c, 3352 }, /* backslash */ + { 0x0000ff08, 3362 }, /* BackSpace */ + { 0x1000ff74, 3372 }, /* BackTab */ + { 0x00000af4, 3380 }, /* ballotcross */ + { 0x0000007c, 3392 }, /* bar */ + { 0x01002235, 3396 }, /* because */ + { 0x0000ff58, 3404 }, /* Begin */ + { 0x000009df, 3410 }, /* blank */ + { 0x100000fc, 3416 }, /* block */ + { 0x000008a5, 3422 }, /* botintegral */ + { 0x000008ac, 3434 }, /* botleftparens */ + { 0x000008a8, 3448 }, /* botleftsqbracket */ + { 0x000008b2, 3465 }, /* botleftsummation */ + { 0x000008ae, 3482 }, /* botrightparens */ + { 0x000008aa, 3497 }, /* botrightsqbracket */ + { 0x000008b6, 3515 }, /* botrightsummation */ + { 0x000009f6, 3533 }, /* bott */ + { 0x000008b4, 3538 }, /* botvertsummationconnector */ + { 0x0000fe74, 3564 }, /* BounceKeys_Enable */ + { 0x0000007b, 3582 }, /* braceleft */ + { 0x0000007d, 3592 }, /* braceright */ + { 0x0000005b, 3603 }, /* bracketleft */ + { 0x0000005d, 3615 }, /* bracketright */ + { 0x01002800, 3628 }, /* braille_blank */ + { 0x0000fff1, 3642 }, /* braille_dot_1 */ + { 0x0000fffa, 3656 }, /* braille_dot_10 */ + { 0x0000fff2, 3671 }, /* braille_dot_2 */ + { 0x0000fff3, 3685 }, /* braille_dot_3 */ + { 0x0000fff4, 3699 }, /* braille_dot_4 */ + { 0x0000fff5, 3713 }, /* braille_dot_5 */ + { 0x0000fff6, 3727 }, /* braille_dot_6 */ + { 0x0000fff7, 3741 }, /* braille_dot_7 */ + { 0x0000fff8, 3755 }, /* braille_dot_8 */ + { 0x0000fff9, 3769 }, /* braille_dot_9 */ + { 0x01002801, 3783 }, /* braille_dots_1 */ + { 0x01002803, 3798 }, /* braille_dots_12 */ + { 0x01002807, 3814 }, /* braille_dots_123 */ + { 0x0100280f, 3831 }, /* braille_dots_1234 */ + { 0x0100281f, 3849 }, /* braille_dots_12345 */ + { 0x0100283f, 3868 }, /* braille_dots_123456 */ + { 0x0100287f, 3888 }, /* braille_dots_1234567 */ + { 0x010028ff, 3909 }, /* braille_dots_12345678 */ + { 0x010028bf, 3931 }, /* braille_dots_1234568 */ + { 0x0100285f, 3952 }, /* braille_dots_123457 */ + { 0x010028df, 3972 }, /* braille_dots_1234578 */ + { 0x0100289f, 3993 }, /* braille_dots_123458 */ + { 0x0100282f, 4013 }, /* braille_dots_12346 */ + { 0x0100286f, 4032 }, /* braille_dots_123467 */ + { 0x010028ef, 4052 }, /* braille_dots_1234678 */ + { 0x010028af, 4073 }, /* braille_dots_123468 */ + { 0x0100284f, 4093 }, /* braille_dots_12347 */ + { 0x010028cf, 4112 }, /* braille_dots_123478 */ + { 0x0100288f, 4132 }, /* braille_dots_12348 */ + { 0x01002817, 4151 }, /* braille_dots_1235 */ + { 0x01002837, 4169 }, /* braille_dots_12356 */ + { 0x01002877, 4188 }, /* braille_dots_123567 */ + { 0x010028f7, 4208 }, /* braille_dots_1235678 */ + { 0x010028b7, 4229 }, /* braille_dots_123568 */ + { 0x01002857, 4249 }, /* braille_dots_12357 */ + { 0x010028d7, 4268 }, /* braille_dots_123578 */ + { 0x01002897, 4288 }, /* braille_dots_12358 */ + { 0x01002827, 4307 }, /* braille_dots_1236 */ + { 0x01002867, 4325 }, /* braille_dots_12367 */ + { 0x010028e7, 4344 }, /* braille_dots_123678 */ + { 0x010028a7, 4364 }, /* braille_dots_12368 */ + { 0x01002847, 4383 }, /* braille_dots_1237 */ + { 0x010028c7, 4401 }, /* braille_dots_12378 */ + { 0x01002887, 4420 }, /* braille_dots_1238 */ + { 0x0100280b, 4438 }, /* braille_dots_124 */ + { 0x0100281b, 4455 }, /* braille_dots_1245 */ + { 0x0100283b, 4473 }, /* braille_dots_12456 */ + { 0x0100287b, 4492 }, /* braille_dots_124567 */ + { 0x010028fb, 4512 }, /* braille_dots_1245678 */ + { 0x010028bb, 4533 }, /* braille_dots_124568 */ + { 0x0100285b, 4553 }, /* braille_dots_12457 */ + { 0x010028db, 4572 }, /* braille_dots_124578 */ + { 0x0100289b, 4592 }, /* braille_dots_12458 */ + { 0x0100282b, 4611 }, /* braille_dots_1246 */ + { 0x0100286b, 4629 }, /* braille_dots_12467 */ + { 0x010028eb, 4648 }, /* braille_dots_124678 */ + { 0x010028ab, 4668 }, /* braille_dots_12468 */ + { 0x0100284b, 4687 }, /* braille_dots_1247 */ + { 0x010028cb, 4705 }, /* braille_dots_12478 */ + { 0x0100288b, 4724 }, /* braille_dots_1248 */ + { 0x01002813, 4742 }, /* braille_dots_125 */ + { 0x01002833, 4759 }, /* braille_dots_1256 */ + { 0x01002873, 4777 }, /* braille_dots_12567 */ + { 0x010028f3, 4796 }, /* braille_dots_125678 */ + { 0x010028b3, 4816 }, /* braille_dots_12568 */ + { 0x01002853, 4835 }, /* braille_dots_1257 */ + { 0x010028d3, 4853 }, /* braille_dots_12578 */ + { 0x01002893, 4872 }, /* braille_dots_1258 */ + { 0x01002823, 4890 }, /* braille_dots_126 */ + { 0x01002863, 4907 }, /* braille_dots_1267 */ + { 0x010028e3, 4925 }, /* braille_dots_12678 */ + { 0x010028a3, 4944 }, /* braille_dots_1268 */ + { 0x01002843, 4962 }, /* braille_dots_127 */ + { 0x010028c3, 4979 }, /* braille_dots_1278 */ + { 0x01002883, 4997 }, /* braille_dots_128 */ + { 0x01002805, 5014 }, /* braille_dots_13 */ + { 0x0100280d, 5030 }, /* braille_dots_134 */ + { 0x0100281d, 5047 }, /* braille_dots_1345 */ + { 0x0100283d, 5065 }, /* braille_dots_13456 */ + { 0x0100287d, 5084 }, /* braille_dots_134567 */ + { 0x010028fd, 5104 }, /* braille_dots_1345678 */ + { 0x010028bd, 5125 }, /* braille_dots_134568 */ + { 0x0100285d, 5145 }, /* braille_dots_13457 */ + { 0x010028dd, 5164 }, /* braille_dots_134578 */ + { 0x0100289d, 5184 }, /* braille_dots_13458 */ + { 0x0100282d, 5203 }, /* braille_dots_1346 */ + { 0x0100286d, 5221 }, /* braille_dots_13467 */ + { 0x010028ed, 5240 }, /* braille_dots_134678 */ + { 0x010028ad, 5260 }, /* braille_dots_13468 */ + { 0x0100284d, 5279 }, /* braille_dots_1347 */ + { 0x010028cd, 5297 }, /* braille_dots_13478 */ + { 0x0100288d, 5316 }, /* braille_dots_1348 */ + { 0x01002815, 5334 }, /* braille_dots_135 */ + { 0x01002835, 5351 }, /* braille_dots_1356 */ + { 0x01002875, 5369 }, /* braille_dots_13567 */ + { 0x010028f5, 5388 }, /* braille_dots_135678 */ + { 0x010028b5, 5408 }, /* braille_dots_13568 */ + { 0x01002855, 5427 }, /* braille_dots_1357 */ + { 0x010028d5, 5445 }, /* braille_dots_13578 */ + { 0x01002895, 5464 }, /* braille_dots_1358 */ + { 0x01002825, 5482 }, /* braille_dots_136 */ + { 0x01002865, 5499 }, /* braille_dots_1367 */ + { 0x010028e5, 5517 }, /* braille_dots_13678 */ + { 0x010028a5, 5536 }, /* braille_dots_1368 */ + { 0x01002845, 5554 }, /* braille_dots_137 */ + { 0x010028c5, 5571 }, /* braille_dots_1378 */ + { 0x01002885, 5589 }, /* braille_dots_138 */ + { 0x01002809, 5606 }, /* braille_dots_14 */ + { 0x01002819, 5622 }, /* braille_dots_145 */ + { 0x01002839, 5639 }, /* braille_dots_1456 */ + { 0x01002879, 5657 }, /* braille_dots_14567 */ + { 0x010028f9, 5676 }, /* braille_dots_145678 */ + { 0x010028b9, 5696 }, /* braille_dots_14568 */ + { 0x01002859, 5715 }, /* braille_dots_1457 */ + { 0x010028d9, 5733 }, /* braille_dots_14578 */ + { 0x01002899, 5752 }, /* braille_dots_1458 */ + { 0x01002829, 5770 }, /* braille_dots_146 */ + { 0x01002869, 5787 }, /* braille_dots_1467 */ + { 0x010028e9, 5805 }, /* braille_dots_14678 */ + { 0x010028a9, 5824 }, /* braille_dots_1468 */ + { 0x01002849, 5842 }, /* braille_dots_147 */ + { 0x010028c9, 5859 }, /* braille_dots_1478 */ + { 0x01002889, 5877 }, /* braille_dots_148 */ + { 0x01002811, 5894 }, /* braille_dots_15 */ + { 0x01002831, 5910 }, /* braille_dots_156 */ + { 0x01002871, 5927 }, /* braille_dots_1567 */ + { 0x010028f1, 5945 }, /* braille_dots_15678 */ + { 0x010028b1, 5964 }, /* braille_dots_1568 */ + { 0x01002851, 5982 }, /* braille_dots_157 */ + { 0x010028d1, 5999 }, /* braille_dots_1578 */ + { 0x01002891, 6017 }, /* braille_dots_158 */ + { 0x01002821, 6034 }, /* braille_dots_16 */ + { 0x01002861, 6050 }, /* braille_dots_167 */ + { 0x010028e1, 6067 }, /* braille_dots_1678 */ + { 0x010028a1, 6085 }, /* braille_dots_168 */ + { 0x01002841, 6102 }, /* braille_dots_17 */ + { 0x010028c1, 6118 }, /* braille_dots_178 */ + { 0x01002881, 6135 }, /* braille_dots_18 */ + { 0x01002802, 6151 }, /* braille_dots_2 */ + { 0x01002806, 6166 }, /* braille_dots_23 */ + { 0x0100280e, 6182 }, /* braille_dots_234 */ + { 0x0100281e, 6199 }, /* braille_dots_2345 */ + { 0x0100283e, 6217 }, /* braille_dots_23456 */ + { 0x0100287e, 6236 }, /* braille_dots_234567 */ + { 0x010028fe, 6256 }, /* braille_dots_2345678 */ + { 0x010028be, 6277 }, /* braille_dots_234568 */ + { 0x0100285e, 6297 }, /* braille_dots_23457 */ + { 0x010028de, 6316 }, /* braille_dots_234578 */ + { 0x0100289e, 6336 }, /* braille_dots_23458 */ + { 0x0100282e, 6355 }, /* braille_dots_2346 */ + { 0x0100286e, 6373 }, /* braille_dots_23467 */ + { 0x010028ee, 6392 }, /* braille_dots_234678 */ + { 0x010028ae, 6412 }, /* braille_dots_23468 */ + { 0x0100284e, 6431 }, /* braille_dots_2347 */ + { 0x010028ce, 6449 }, /* braille_dots_23478 */ + { 0x0100288e, 6468 }, /* braille_dots_2348 */ + { 0x01002816, 6486 }, /* braille_dots_235 */ + { 0x01002836, 6503 }, /* braille_dots_2356 */ + { 0x01002876, 6521 }, /* braille_dots_23567 */ + { 0x010028f6, 6540 }, /* braille_dots_235678 */ + { 0x010028b6, 6560 }, /* braille_dots_23568 */ + { 0x01002856, 6579 }, /* braille_dots_2357 */ + { 0x010028d6, 6597 }, /* braille_dots_23578 */ + { 0x01002896, 6616 }, /* braille_dots_2358 */ + { 0x01002826, 6634 }, /* braille_dots_236 */ + { 0x01002866, 6651 }, /* braille_dots_2367 */ + { 0x010028e6, 6669 }, /* braille_dots_23678 */ + { 0x010028a6, 6688 }, /* braille_dots_2368 */ + { 0x01002846, 6706 }, /* braille_dots_237 */ + { 0x010028c6, 6723 }, /* braille_dots_2378 */ + { 0x01002886, 6741 }, /* braille_dots_238 */ + { 0x0100280a, 6758 }, /* braille_dots_24 */ + { 0x0100281a, 6774 }, /* braille_dots_245 */ + { 0x0100283a, 6791 }, /* braille_dots_2456 */ + { 0x0100287a, 6809 }, /* braille_dots_24567 */ + { 0x010028fa, 6828 }, /* braille_dots_245678 */ + { 0x010028ba, 6848 }, /* braille_dots_24568 */ + { 0x0100285a, 6867 }, /* braille_dots_2457 */ + { 0x010028da, 6885 }, /* braille_dots_24578 */ + { 0x0100289a, 6904 }, /* braille_dots_2458 */ + { 0x0100282a, 6922 }, /* braille_dots_246 */ + { 0x0100286a, 6939 }, /* braille_dots_2467 */ + { 0x010028ea, 6957 }, /* braille_dots_24678 */ + { 0x010028aa, 6976 }, /* braille_dots_2468 */ + { 0x0100284a, 6994 }, /* braille_dots_247 */ + { 0x010028ca, 7011 }, /* braille_dots_2478 */ + { 0x0100288a, 7029 }, /* braille_dots_248 */ + { 0x01002812, 7046 }, /* braille_dots_25 */ + { 0x01002832, 7062 }, /* braille_dots_256 */ + { 0x01002872, 7079 }, /* braille_dots_2567 */ + { 0x010028f2, 7097 }, /* braille_dots_25678 */ + { 0x010028b2, 7116 }, /* braille_dots_2568 */ + { 0x01002852, 7134 }, /* braille_dots_257 */ + { 0x010028d2, 7151 }, /* braille_dots_2578 */ + { 0x01002892, 7169 }, /* braille_dots_258 */ + { 0x01002822, 7186 }, /* braille_dots_26 */ + { 0x01002862, 7202 }, /* braille_dots_267 */ + { 0x010028e2, 7219 }, /* braille_dots_2678 */ + { 0x010028a2, 7237 }, /* braille_dots_268 */ + { 0x01002842, 7254 }, /* braille_dots_27 */ + { 0x010028c2, 7270 }, /* braille_dots_278 */ + { 0x01002882, 7287 }, /* braille_dots_28 */ + { 0x01002804, 7303 }, /* braille_dots_3 */ + { 0x0100280c, 7318 }, /* braille_dots_34 */ + { 0x0100281c, 7334 }, /* braille_dots_345 */ + { 0x0100283c, 7351 }, /* braille_dots_3456 */ + { 0x0100287c, 7369 }, /* braille_dots_34567 */ + { 0x010028fc, 7388 }, /* braille_dots_345678 */ + { 0x010028bc, 7408 }, /* braille_dots_34568 */ + { 0x0100285c, 7427 }, /* braille_dots_3457 */ + { 0x010028dc, 7445 }, /* braille_dots_34578 */ + { 0x0100289c, 7464 }, /* braille_dots_3458 */ + { 0x0100282c, 7482 }, /* braille_dots_346 */ + { 0x0100286c, 7499 }, /* braille_dots_3467 */ + { 0x010028ec, 7517 }, /* braille_dots_34678 */ + { 0x010028ac, 7536 }, /* braille_dots_3468 */ + { 0x0100284c, 7554 }, /* braille_dots_347 */ + { 0x010028cc, 7571 }, /* braille_dots_3478 */ + { 0x0100288c, 7589 }, /* braille_dots_348 */ + { 0x01002814, 7606 }, /* braille_dots_35 */ + { 0x01002834, 7622 }, /* braille_dots_356 */ + { 0x01002874, 7639 }, /* braille_dots_3567 */ + { 0x010028f4, 7657 }, /* braille_dots_35678 */ + { 0x010028b4, 7676 }, /* braille_dots_3568 */ + { 0x01002854, 7694 }, /* braille_dots_357 */ + { 0x010028d4, 7711 }, /* braille_dots_3578 */ + { 0x01002894, 7729 }, /* braille_dots_358 */ + { 0x01002824, 7746 }, /* braille_dots_36 */ + { 0x01002864, 7762 }, /* braille_dots_367 */ + { 0x010028e4, 7779 }, /* braille_dots_3678 */ + { 0x010028a4, 7797 }, /* braille_dots_368 */ + { 0x01002844, 7814 }, /* braille_dots_37 */ + { 0x010028c4, 7830 }, /* braille_dots_378 */ + { 0x01002884, 7847 }, /* braille_dots_38 */ + { 0x01002808, 7863 }, /* braille_dots_4 */ + { 0x01002818, 7878 }, /* braille_dots_45 */ + { 0x01002838, 7894 }, /* braille_dots_456 */ + { 0x01002878, 7911 }, /* braille_dots_4567 */ + { 0x010028f8, 7929 }, /* braille_dots_45678 */ + { 0x010028b8, 7948 }, /* braille_dots_4568 */ + { 0x01002858, 7966 }, /* braille_dots_457 */ + { 0x010028d8, 7983 }, /* braille_dots_4578 */ + { 0x01002898, 8001 }, /* braille_dots_458 */ + { 0x01002828, 8018 }, /* braille_dots_46 */ + { 0x01002868, 8034 }, /* braille_dots_467 */ + { 0x010028e8, 8051 }, /* braille_dots_4678 */ + { 0x010028a8, 8069 }, /* braille_dots_468 */ + { 0x01002848, 8086 }, /* braille_dots_47 */ + { 0x010028c8, 8102 }, /* braille_dots_478 */ + { 0x01002888, 8119 }, /* braille_dots_48 */ + { 0x01002810, 8135 }, /* braille_dots_5 */ + { 0x01002830, 8150 }, /* braille_dots_56 */ + { 0x01002870, 8166 }, /* braille_dots_567 */ + { 0x010028f0, 8183 }, /* braille_dots_5678 */ + { 0x010028b0, 8201 }, /* braille_dots_568 */ + { 0x01002850, 8218 }, /* braille_dots_57 */ + { 0x010028d0, 8234 }, /* braille_dots_578 */ + { 0x01002890, 8251 }, /* braille_dots_58 */ + { 0x01002820, 8267 }, /* braille_dots_6 */ + { 0x01002860, 8282 }, /* braille_dots_67 */ + { 0x010028e0, 8298 }, /* braille_dots_678 */ + { 0x010028a0, 8315 }, /* braille_dots_68 */ + { 0x01002840, 8331 }, /* braille_dots_7 */ + { 0x010028c0, 8346 }, /* braille_dots_78 */ + { 0x01002880, 8362 }, /* braille_dots_8 */ + { 0x0000ff6b, 8377 }, /* Break */ + { 0x000001a2, 8383 }, /* breve */ + { 0x000000a6, 8389 }, /* brokenbar */ + { 0x000006ae, 8399 }, /* Byelorussian_shortu */ + { 0x000006be, 8419 }, /* Byelorussian_SHORTU */ + { 0x00000043, 8439 }, /* C */ + { 0x00000063, 8441 }, /* c */ + { 0x0000fea3, 8443 }, /* c_h */ + { 0x0000fea4, 8447 }, /* C_h */ + { 0x0000fea5, 8451 }, /* C_H */ + { 0x000002c5, 8455 }, /* Cabovedot */ + { 0x000002e5, 8465 }, /* cabovedot */ + { 0x000001c6, 8475 }, /* Cacute */ + { 0x000001e6, 8482 }, /* cacute */ + { 0x0000ff69, 8489 }, /* Cancel */ + { 0x0000ffe5, 8496 }, /* Caps_Lock */ + { 0x00000ab8, 8506 }, /* careof */ + { 0x00000afc, 8513 }, /* caret */ + { 0x000001b7, 8519 }, /* caron */ + { 0x000001c8, 8525 }, /* Ccaron */ + { 0x000001e8, 8532 }, /* ccaron */ + { 0x000000c7, 8539 }, /* Ccedilla */ + { 0x000000e7, 8548 }, /* ccedilla */ + { 0x000002c6, 8557 }, /* Ccircumflex */ + { 0x000002e6, 8569 }, /* ccircumflex */ + { 0x000000b8, 8581 }, /* cedilla */ + { 0x000000a2, 8589 }, /* cent */ + { 0x0000fea0, 8594 }, /* ch */ + { 0x0000fea1, 8597 }, /* Ch */ + { 0x0000fea2, 8600 }, /* CH */ + { 0x000009e1, 8603 }, /* checkerboard */ + { 0x00000af3, 8616 }, /* checkmark */ + { 0x00000bcf, 8626 }, /* circle */ + { 0x0000ff0b, 8633 }, /* Clear */ + { 0x1000ff6f, 8639 }, /* ClearLine */ + { 0x00000aec, 8649 }, /* club */ + { 0x0000ff37, 8654 }, /* Codeinput */ + { 0x0000003a, 8664 }, /* colon */ + { 0x010020a1, 8670 }, /* ColonSign */ + { 0x0000002c, 8680 }, /* comma */ + { 0x0100220b, 8686 }, /* containsas */ + { 0x0000ffe3, 8697 }, /* Control_L */ + { 0x0000ffe4, 8707 }, /* Control_R */ + { 0x000000a9, 8717 }, /* copyright */ + { 0x000009e4, 8727 }, /* cr */ + { 0x000009ee, 8730 }, /* crossinglines */ + { 0x010020a2, 8744 }, /* CruzeiroSign */ + { 0x0100221b, 8757 }, /* cuberoot */ + { 0x000000a4, 8766 }, /* currency */ + { 0x00000aff, 8775 }, /* cursor */ + { 0x000006c1, 8782 }, /* Cyrillic_a */ + { 0x000006e1, 8793 }, /* Cyrillic_A */ + { 0x000006c2, 8804 }, /* Cyrillic_be */ + { 0x000006e2, 8816 }, /* Cyrillic_BE */ + { 0x000006de, 8828 }, /* Cyrillic_che */ + { 0x000006fe, 8841 }, /* Cyrillic_CHE */ + { 0x010004b6, 8854 }, /* Cyrillic_CHE_descender */ + { 0x010004b7, 8877 }, /* Cyrillic_che_descender */ + { 0x010004b8, 8900 }, /* Cyrillic_CHE_vertstroke */ + { 0x010004b9, 8924 }, /* Cyrillic_che_vertstroke */ + { 0x000006c4, 8948 }, /* Cyrillic_de */ + { 0x000006e4, 8960 }, /* Cyrillic_DE */ + { 0x000006af, 8972 }, /* Cyrillic_dzhe */ + { 0x000006bf, 8986 }, /* Cyrillic_DZHE */ + { 0x000006dc, 9000 }, /* Cyrillic_e */ + { 0x000006fc, 9011 }, /* Cyrillic_E */ + { 0x000006c6, 9022 }, /* Cyrillic_ef */ + { 0x000006e6, 9034 }, /* Cyrillic_EF */ + { 0x000006cc, 9046 }, /* Cyrillic_el */ + { 0x000006ec, 9058 }, /* Cyrillic_EL */ + { 0x000006cd, 9070 }, /* Cyrillic_em */ + { 0x000006ed, 9082 }, /* Cyrillic_EM */ + { 0x000006ce, 9094 }, /* Cyrillic_en */ + { 0x000006ee, 9106 }, /* Cyrillic_EN */ + { 0x010004a2, 9118 }, /* Cyrillic_EN_descender */ + { 0x010004a3, 9140 }, /* Cyrillic_en_descender */ + { 0x000006d2, 9162 }, /* Cyrillic_er */ + { 0x000006f2, 9174 }, /* Cyrillic_ER */ + { 0x000006d3, 9186 }, /* Cyrillic_es */ + { 0x000006f3, 9198 }, /* Cyrillic_ES */ + { 0x000006c7, 9210 }, /* Cyrillic_ghe */ + { 0x000006e7, 9223 }, /* Cyrillic_GHE */ + { 0x01000492, 9236 }, /* Cyrillic_GHE_bar */ + { 0x01000493, 9253 }, /* Cyrillic_ghe_bar */ + { 0x000006c8, 9270 }, /* Cyrillic_ha */ + { 0x000006e8, 9282 }, /* Cyrillic_HA */ + { 0x010004b2, 9294 }, /* Cyrillic_HA_descender */ + { 0x010004b3, 9316 }, /* Cyrillic_ha_descender */ + { 0x000006df, 9338 }, /* Cyrillic_hardsign */ + { 0x000006ff, 9356 }, /* Cyrillic_HARDSIGN */ + { 0x000006c9, 9374 }, /* Cyrillic_i */ + { 0x000006e9, 9385 }, /* Cyrillic_I */ + { 0x010004e2, 9396 }, /* Cyrillic_I_macron */ + { 0x010004e3, 9414 }, /* Cyrillic_i_macron */ + { 0x000006c5, 9432 }, /* Cyrillic_ie */ + { 0x000006e5, 9444 }, /* Cyrillic_IE */ + { 0x000006a3, 9456 }, /* Cyrillic_io */ + { 0x000006b3, 9468 }, /* Cyrillic_IO */ + { 0x000006a8, 9480 }, /* Cyrillic_je */ + { 0x000006b8, 9492 }, /* Cyrillic_JE */ + { 0x000006cb, 9504 }, /* Cyrillic_ka */ + { 0x000006eb, 9516 }, /* Cyrillic_KA */ + { 0x0100049a, 9528 }, /* Cyrillic_KA_descender */ + { 0x0100049b, 9550 }, /* Cyrillic_ka_descender */ + { 0x0100049c, 9572 }, /* Cyrillic_KA_vertstroke */ + { 0x0100049d, 9595 }, /* Cyrillic_ka_vertstroke */ + { 0x000006a9, 9618 }, /* Cyrillic_lje */ + { 0x000006b9, 9631 }, /* Cyrillic_LJE */ + { 0x000006aa, 9644 }, /* Cyrillic_nje */ + { 0x000006ba, 9657 }, /* Cyrillic_NJE */ + { 0x000006cf, 9670 }, /* Cyrillic_o */ + { 0x000006ef, 9681 }, /* Cyrillic_O */ + { 0x010004e8, 9692 }, /* Cyrillic_O_bar */ + { 0x010004e9, 9707 }, /* Cyrillic_o_bar */ + { 0x000006d0, 9722 }, /* Cyrillic_pe */ + { 0x000006f0, 9734 }, /* Cyrillic_PE */ + { 0x010004d8, 9746 }, /* Cyrillic_SCHWA */ + { 0x010004d9, 9761 }, /* Cyrillic_schwa */ + { 0x000006db, 9776 }, /* Cyrillic_sha */ + { 0x000006fb, 9789 }, /* Cyrillic_SHA */ + { 0x000006dd, 9802 }, /* Cyrillic_shcha */ + { 0x000006fd, 9817 }, /* Cyrillic_SHCHA */ + { 0x010004ba, 9832 }, /* Cyrillic_SHHA */ + { 0x010004bb, 9846 }, /* Cyrillic_shha */ + { 0x000006ca, 9860 }, /* Cyrillic_shorti */ + { 0x000006ea, 9876 }, /* Cyrillic_SHORTI */ + { 0x000006d8, 9892 }, /* Cyrillic_softsign */ + { 0x000006f8, 9910 }, /* Cyrillic_SOFTSIGN */ + { 0x000006d4, 9928 }, /* Cyrillic_te */ + { 0x000006f4, 9940 }, /* Cyrillic_TE */ + { 0x000006c3, 9952 }, /* Cyrillic_tse */ + { 0x000006e3, 9965 }, /* Cyrillic_TSE */ + { 0x000006d5, 9978 }, /* Cyrillic_u */ + { 0x000006f5, 9989 }, /* Cyrillic_U */ + { 0x010004ee, 10000 }, /* Cyrillic_U_macron */ + { 0x010004ef, 10018 }, /* Cyrillic_u_macron */ + { 0x010004ae, 10036 }, /* Cyrillic_U_straight */ + { 0x010004af, 10056 }, /* Cyrillic_u_straight */ + { 0x010004b0, 10076 }, /* Cyrillic_U_straight_bar */ + { 0x010004b1, 10100 }, /* Cyrillic_u_straight_bar */ + { 0x000006d7, 10124 }, /* Cyrillic_ve */ + { 0x000006f7, 10136 }, /* Cyrillic_VE */ + { 0x000006d1, 10148 }, /* Cyrillic_ya */ + { 0x000006f1, 10160 }, /* Cyrillic_YA */ + { 0x000006d9, 10172 }, /* Cyrillic_yeru */ + { 0x000006f9, 10186 }, /* Cyrillic_YERU */ + { 0x000006c0, 10200 }, /* Cyrillic_yu */ + { 0x000006e0, 10212 }, /* Cyrillic_YU */ + { 0x000006da, 10224 }, /* Cyrillic_ze */ + { 0x000006fa, 10236 }, /* Cyrillic_ZE */ + { 0x000006d6, 10248 }, /* Cyrillic_zhe */ + { 0x000006f6, 10261 }, /* Cyrillic_ZHE */ + { 0x01000496, 10274 }, /* Cyrillic_ZHE_descender */ + { 0x01000497, 10297 }, /* Cyrillic_zhe_descender */ + { 0x00000044, 10320 }, /* D */ + { 0x00000064, 10322 }, /* d */ + { 0x01001e0a, 10324 }, /* Dabovedot */ + { 0x01001e0b, 10334 }, /* dabovedot */ + { 0x1000fe27, 10344 }, /* Dacute_accent */ + { 0x00000af1, 10358 }, /* dagger */ + { 0x000001cf, 10365 }, /* Dcaron */ + { 0x000001ef, 10372 }, /* dcaron */ + { 0x1000fe2c, 10379 }, /* Dcedilla_accent */ + { 0x1000fe5e, 10395 }, /* Dcircumflex_accent */ + { 0x1000fe22, 10414 }, /* Ddiaeresis */ + { 0x0000fe80, 10425 }, /* dead_a */ + { 0x0000fe81, 10432 }, /* dead_A */ + { 0x0000fe64, 10439 }, /* dead_abovecomma */ + { 0x0000fe56, 10455 }, /* dead_abovedot */ + { 0x0000fe65, 10469 }, /* dead_abovereversedcomma */ + { 0x0000fe58, 10493 }, /* dead_abovering */ + { 0x0000fe91, 10508 }, /* dead_aboveverticalline */ + { 0x0000fe51, 10531 }, /* dead_acute */ + { 0x0000fe6b, 10542 }, /* dead_belowbreve */ + { 0x0000fe69, 10558 }, /* dead_belowcircumflex */ + { 0x0000fe6e, 10579 }, /* dead_belowcomma */ + { 0x0000fe6c, 10595 }, /* dead_belowdiaeresis */ + { 0x0000fe60, 10615 }, /* dead_belowdot */ + { 0x0000fe68, 10629 }, /* dead_belowmacron */ + { 0x0000fe67, 10646 }, /* dead_belowring */ + { 0x0000fe6a, 10661 }, /* dead_belowtilde */ + { 0x0000fe92, 10677 }, /* dead_belowverticalline */ + { 0x0000fe55, 10700 }, /* dead_breve */ + { 0x0000fe8b, 10711 }, /* dead_capital_schwa */ + { 0x0000fe5a, 10730 }, /* dead_caron */ + { 0x0000fe5b, 10741 }, /* dead_cedilla */ + { 0x0000fe52, 10754 }, /* dead_circumflex */ + { 0x0000fe6f, 10770 }, /* dead_currency */ + { 0x0000fe65, 10784 }, /* dead_dasia */ + { 0x0000fe57, 10795 }, /* dead_diaeresis */ + { 0x0000fe59, 10810 }, /* dead_doubleacute */ + { 0x0000fe66, 10827 }, /* dead_doublegrave */ + { 0x0000fe82, 10844 }, /* dead_e */ + { 0x0000fe83, 10851 }, /* dead_E */ + { 0x0000fe50, 10858 }, /* dead_grave */ + { 0x0000fe8c, 10869 }, /* dead_greek */ + { 0x0000fe61, 10880 }, /* dead_hook */ + { 0x0000fe62, 10890 }, /* dead_horn */ + { 0x0000fe84, 10900 }, /* dead_i */ + { 0x0000fe85, 10907 }, /* dead_I */ + { 0x0000fe6d, 10914 }, /* dead_invertedbreve */ + { 0x0000fe5d, 10933 }, /* dead_iota */ + { 0x0000fe93, 10943 }, /* dead_longsolidusoverlay */ + { 0x0000fe90, 10967 }, /* dead_lowline */ + { 0x0000fe54, 10980 }, /* dead_macron */ + { 0x0000fe86, 10992 }, /* dead_o */ + { 0x0000fe87, 10999 }, /* dead_O */ + { 0x0000fe5c, 11006 }, /* dead_ogonek */ + { 0x0000fe53, 11018 }, /* dead_perispomeni */ + { 0x0000fe64, 11035 }, /* dead_psili */ + { 0x0000fe5f, 11046 }, /* dead_semivoiced_sound */ + { 0x0000fe8a, 11068 }, /* dead_small_schwa */ + { 0x0000fe63, 11085 }, /* dead_stroke */ + { 0x0000fe53, 11097 }, /* dead_tilde */ + { 0x0000fe88, 11108 }, /* dead_u */ + { 0x0000fe89, 11115 }, /* dead_U */ + { 0x0000fe5e, 11122 }, /* dead_voiced_sound */ + { 0x00000abd, 11140 }, /* decimalpoint */ + { 0x000000b0, 11153 }, /* degree */ + { 0x0000ffff, 11160 }, /* Delete */ + { 0x1000ff73, 11167 }, /* DeleteChar */ + { 0x1000ff71, 11178 }, /* DeleteLine */ + { 0x1000fe60, 11189 }, /* Dgrave_accent */ + { 0x000000a8, 11203 }, /* diaeresis */ + { 0x00000aed, 11213 }, /* diamond */ + { 0x00000aa5, 11221 }, /* digitspace */ + { 0x0100222c, 11232 }, /* dintegral */ + { 0x000000f7, 11242 }, /* division */ + { 0x00000024, 11251 }, /* dollar */ + { 0x010020ab, 11258 }, /* DongSign */ + { 0x00000aaf, 11267 }, /* doubbaselinedot */ + { 0x000001bd, 11283 }, /* doubleacute */ + { 0x00000af2, 11295 }, /* doubledagger */ + { 0x00000afe, 11308 }, /* doublelowquotemark */ + { 0x0000ff54, 11327 }, /* Down */ + { 0x000008fe, 11332 }, /* downarrow */ + { 0x00000ba8, 11342 }, /* downcaret */ + { 0x00000bd6, 11352 }, /* downshoe */ + { 0x00000bc4, 11361 }, /* downstile */ + { 0x00000bc2, 11371 }, /* downtack */ + { 0x1000ff00, 11380 }, /* DRemove */ + { 0x1000feb0, 11388 }, /* Dring_accent */ + { 0x000001d0, 11401 }, /* Dstroke */ + { 0x000001f0, 11409 }, /* dstroke */ + { 0x1000fe7e, 11417 }, /* Dtilde */ + { 0x00000045, 11424 }, /* E */ + { 0x00000065, 11426 }, /* e */ + { 0x000003cc, 11428 }, /* Eabovedot */ + { 0x000003ec, 11438 }, /* eabovedot */ + { 0x000000c9, 11448 }, /* Eacute */ + { 0x000000e9, 11455 }, /* eacute */ + { 0x01001eb8, 11462 }, /* Ebelowdot */ + { 0x01001eb9, 11472 }, /* ebelowdot */ + { 0x000001cc, 11482 }, /* Ecaron */ + { 0x000001ec, 11489 }, /* ecaron */ + { 0x000000ca, 11496 }, /* Ecircumflex */ + { 0x000000ea, 11508 }, /* ecircumflex */ + { 0x01001ebe, 11520 }, /* Ecircumflexacute */ + { 0x01001ebf, 11537 }, /* ecircumflexacute */ + { 0x01001ec6, 11554 }, /* Ecircumflexbelowdot */ + { 0x01001ec7, 11574 }, /* ecircumflexbelowdot */ + { 0x01001ec0, 11594 }, /* Ecircumflexgrave */ + { 0x01001ec1, 11611 }, /* ecircumflexgrave */ + { 0x01001ec2, 11628 }, /* Ecircumflexhook */ + { 0x01001ec3, 11644 }, /* ecircumflexhook */ + { 0x01001ec4, 11660 }, /* Ecircumflextilde */ + { 0x01001ec5, 11677 }, /* ecircumflextilde */ + { 0x010020a0, 11694 }, /* EcuSign */ + { 0x000000cb, 11702 }, /* Ediaeresis */ + { 0x000000eb, 11713 }, /* ediaeresis */ + { 0x000000c8, 11724 }, /* Egrave */ + { 0x000000e8, 11731 }, /* egrave */ + { 0x01001eba, 11738 }, /* Ehook */ + { 0x01001ebb, 11744 }, /* ehook */ + { 0x01002088, 11750 }, /* eightsubscript */ + { 0x01002078, 11765 }, /* eightsuperior */ + { 0x0000ff2f, 11779 }, /* Eisu_Shift */ + { 0x0000ff30, 11790 }, /* Eisu_toggle */ + { 0x01002208, 11802 }, /* elementof */ + { 0x00000aae, 11812 }, /* ellipsis */ + { 0x00000aa3, 11821 }, /* em3space */ + { 0x00000aa4, 11830 }, /* em4space */ + { 0x000003aa, 11839 }, /* Emacron */ + { 0x000003ba, 11847 }, /* emacron */ + { 0x00000aa9, 11855 }, /* emdash */ + { 0x00000ade, 11862 }, /* emfilledcircle */ + { 0x00000adf, 11877 }, /* emfilledrect */ + { 0x00000ace, 11890 }, /* emopencircle */ + { 0x00000acf, 11903 }, /* emopenrectangle */ + { 0x01002205, 11919 }, /* emptyset */ + { 0x00000aa1, 11928 }, /* emspace */ + { 0x0000ff57, 11936 }, /* End */ + { 0x00000aaa, 11940 }, /* endash */ + { 0x00000ae6, 11947 }, /* enfilledcircbullet */ + { 0x00000ae7, 11966 }, /* enfilledsqbullet */ + { 0x000003bd, 11983 }, /* ENG */ + { 0x000003bf, 11987 }, /* eng */ + { 0x00000ae0, 11991 }, /* enopencircbullet */ + { 0x00000ae1, 12008 }, /* enopensquarebullet */ + { 0x00000aa2, 12027 }, /* enspace */ + { 0x000001ca, 12035 }, /* Eogonek */ + { 0x000001ea, 12043 }, /* eogonek */ + { 0x0000003d, 12051 }, /* equal */ + { 0x0000ff1b, 12057 }, /* Escape */ + { 0x000000d0, 12064 }, /* ETH */ + { 0x000000d0, 12068 }, /* Eth */ + { 0x000000f0, 12072 }, /* eth */ + { 0x01001ebc, 12076 }, /* Etilde */ + { 0x01001ebd, 12083 }, /* etilde */ + { 0x000020ac, 12090 }, /* EuroSign */ + { 0x00000021, 12099 }, /* exclam */ + { 0x000000a1, 12106 }, /* exclamdown */ + { 0x0000ff62, 12117 }, /* Execute */ + { 0x1000ff76, 12125 }, /* Ext16bit_L */ + { 0x1000ff77, 12136 }, /* Ext16bit_R */ + { 0x010001b7, 12147 }, /* EZH */ + { 0x01000292, 12151 }, /* ezh */ + { 0x00000046, 12155 }, /* F */ + { 0x00000066, 12157 }, /* f */ + { 0x0000ffbe, 12159 }, /* F1 */ + { 0x0000ffc7, 12162 }, /* F10 */ + { 0x0000ffc8, 12166 }, /* F11 */ + { 0x0000ffc9, 12170 }, /* F12 */ + { 0x0000ffca, 12174 }, /* F13 */ + { 0x0000ffcb, 12178 }, /* F14 */ + { 0x0000ffcc, 12182 }, /* F15 */ + { 0x0000ffcd, 12186 }, /* F16 */ + { 0x0000ffce, 12190 }, /* F17 */ + { 0x0000ffcf, 12194 }, /* F18 */ + { 0x0000ffd0, 12198 }, /* F19 */ + { 0x0000ffbf, 12202 }, /* F2 */ + { 0x0000ffd1, 12205 }, /* F20 */ + { 0x0000ffd2, 12209 }, /* F21 */ + { 0x0000ffd3, 12213 }, /* F22 */ + { 0x0000ffd4, 12217 }, /* F23 */ + { 0x0000ffd5, 12221 }, /* F24 */ + { 0x0000ffd6, 12225 }, /* F25 */ + { 0x0000ffd7, 12229 }, /* F26 */ + { 0x0000ffd8, 12233 }, /* F27 */ + { 0x0000ffd9, 12237 }, /* F28 */ + { 0x0000ffda, 12241 }, /* F29 */ + { 0x0000ffc0, 12245 }, /* F3 */ + { 0x0000ffdb, 12248 }, /* F30 */ + { 0x0000ffdc, 12252 }, /* F31 */ + { 0x0000ffdd, 12256 }, /* F32 */ + { 0x0000ffde, 12260 }, /* F33 */ + { 0x0000ffdf, 12264 }, /* F34 */ + { 0x0000ffe0, 12268 }, /* F35 */ + { 0x0000ffc1, 12272 }, /* F4 */ + { 0x0000ffc2, 12275 }, /* F5 */ + { 0x0000ffc3, 12278 }, /* F6 */ + { 0x0000ffc4, 12281 }, /* F7 */ + { 0x0000ffc5, 12284 }, /* F8 */ + { 0x0000ffc6, 12287 }, /* F9 */ + { 0x01001e1e, 12290 }, /* Fabovedot */ + { 0x01001e1f, 12300 }, /* fabovedot */ + { 0x010006f0, 12310 }, /* Farsi_0 */ + { 0x010006f1, 12318 }, /* Farsi_1 */ + { 0x010006f2, 12326 }, /* Farsi_2 */ + { 0x010006f3, 12334 }, /* Farsi_3 */ + { 0x010006f4, 12342 }, /* Farsi_4 */ + { 0x010006f5, 12350 }, /* Farsi_5 */ + { 0x010006f6, 12358 }, /* Farsi_6 */ + { 0x010006f7, 12366 }, /* Farsi_7 */ + { 0x010006f8, 12374 }, /* Farsi_8 */ + { 0x010006f9, 12382 }, /* Farsi_9 */ + { 0x010006cc, 12390 }, /* Farsi_yeh */ + { 0x00000af8, 12400 }, /* femalesymbol */ + { 0x000009e3, 12413 }, /* ff */ + { 0x010020a3, 12416 }, /* FFrancSign */ + { 0x00000abb, 12427 }, /* figdash */ + { 0x00000adc, 12435 }, /* filledlefttribullet */ + { 0x00000adb, 12455 }, /* filledrectbullet */ + { 0x00000add, 12472 }, /* filledrighttribullet */ + { 0x00000ae9, 12493 }, /* filledtribulletdown */ + { 0x00000ae8, 12513 }, /* filledtribulletup */ + { 0x0000ff68, 12531 }, /* Find */ + { 0x0000fed0, 12536 }, /* First_Virtual_Screen */ + { 0x00000ac5, 12557 }, /* fiveeighths */ + { 0x00000ab7, 12569 }, /* fivesixths */ + { 0x01002085, 12580 }, /* fivesubscript */ + { 0x01002075, 12594 }, /* fivesuperior */ + { 0x00000ab5, 12607 }, /* fourfifths */ + { 0x01002084, 12618 }, /* foursubscript */ + { 0x01002074, 12632 }, /* foursuperior */ + { 0x0100221c, 12645 }, /* fourthroot */ + { 0x000008f6, 12656 }, /* function */ + { 0x00000047, 12665 }, /* G */ + { 0x00000067, 12667 }, /* g */ + { 0x000002d5, 12669 }, /* Gabovedot */ + { 0x000002f5, 12679 }, /* gabovedot */ + { 0x000002ab, 12689 }, /* Gbreve */ + { 0x000002bb, 12696 }, /* gbreve */ + { 0x010001e6, 12703 }, /* Gcaron */ + { 0x010001e7, 12710 }, /* gcaron */ + { 0x000003ab, 12717 }, /* Gcedilla */ + { 0x000003bb, 12726 }, /* gcedilla */ + { 0x000002d8, 12735 }, /* Gcircumflex */ + { 0x000002f8, 12747 }, /* gcircumflex */ + { 0x010010d0, 12759 }, /* Georgian_an */ + { 0x010010d1, 12771 }, /* Georgian_ban */ + { 0x010010ea, 12784 }, /* Georgian_can */ + { 0x010010ed, 12797 }, /* Georgian_char */ + { 0x010010e9, 12811 }, /* Georgian_chin */ + { 0x010010ec, 12825 }, /* Georgian_cil */ + { 0x010010d3, 12838 }, /* Georgian_don */ + { 0x010010d4, 12851 }, /* Georgian_en */ + { 0x010010f6, 12863 }, /* Georgian_fi */ + { 0x010010d2, 12875 }, /* Georgian_gan */ + { 0x010010e6, 12888 }, /* Georgian_ghan */ + { 0x010010f0, 12902 }, /* Georgian_hae */ + { 0x010010f4, 12915 }, /* Georgian_har */ + { 0x010010f1, 12928 }, /* Georgian_he */ + { 0x010010f2, 12940 }, /* Georgian_hie */ + { 0x010010f5, 12953 }, /* Georgian_hoe */ + { 0x010010d8, 12966 }, /* Georgian_in */ + { 0x010010ef, 12978 }, /* Georgian_jhan */ + { 0x010010eb, 12992 }, /* Georgian_jil */ + { 0x010010d9, 13005 }, /* Georgian_kan */ + { 0x010010e5, 13018 }, /* Georgian_khar */ + { 0x010010da, 13032 }, /* Georgian_las */ + { 0x010010db, 13045 }, /* Georgian_man */ + { 0x010010dc, 13058 }, /* Georgian_nar */ + { 0x010010dd, 13071 }, /* Georgian_on */ + { 0x010010de, 13083 }, /* Georgian_par */ + { 0x010010e4, 13096 }, /* Georgian_phar */ + { 0x010010e7, 13110 }, /* Georgian_qar */ + { 0x010010e0, 13123 }, /* Georgian_rae */ + { 0x010010e1, 13136 }, /* Georgian_san */ + { 0x010010e8, 13149 }, /* Georgian_shin */ + { 0x010010d7, 13163 }, /* Georgian_tan */ + { 0x010010e2, 13176 }, /* Georgian_tar */ + { 0x010010e3, 13189 }, /* Georgian_un */ + { 0x010010d5, 13201 }, /* Georgian_vin */ + { 0x010010f3, 13214 }, /* Georgian_we */ + { 0x010010ee, 13226 }, /* Georgian_xan */ + { 0x010010d6, 13239 }, /* Georgian_zen */ + { 0x010010df, 13252 }, /* Georgian_zhar */ + { 0x00000060, 13266 }, /* grave */ + { 0x0000003e, 13272 }, /* greater */ + { 0x000008be, 13280 }, /* greaterthanequal */ + { 0x000007ae, 13297 }, /* Greek_accentdieresis */ + { 0x000007c1, 13318 }, /* Greek_ALPHA */ + { 0x000007e1, 13330 }, /* Greek_alpha */ + { 0x000007a1, 13342 }, /* Greek_ALPHAaccent */ + { 0x000007b1, 13360 }, /* Greek_alphaaccent */ + { 0x000007c2, 13378 }, /* Greek_BETA */ + { 0x000007e2, 13389 }, /* Greek_beta */ + { 0x000007d7, 13400 }, /* Greek_CHI */ + { 0x000007f7, 13410 }, /* Greek_chi */ + { 0x000007c4, 13420 }, /* Greek_DELTA */ + { 0x000007e4, 13432 }, /* Greek_delta */ + { 0x000007c5, 13444 }, /* Greek_EPSILON */ + { 0x000007e5, 13458 }, /* Greek_epsilon */ + { 0x000007a2, 13472 }, /* Greek_EPSILONaccent */ + { 0x000007b2, 13492 }, /* Greek_epsilonaccent */ + { 0x000007c7, 13512 }, /* Greek_ETA */ + { 0x000007e7, 13522 }, /* Greek_eta */ + { 0x000007a3, 13532 }, /* Greek_ETAaccent */ + { 0x000007b3, 13548 }, /* Greek_etaaccent */ + { 0x000007f3, 13564 }, /* Greek_finalsmallsigma */ + { 0x000007c3, 13586 }, /* Greek_GAMMA */ + { 0x000007e3, 13598 }, /* Greek_gamma */ + { 0x000007af, 13610 }, /* Greek_horizbar */ + { 0x000007c9, 13625 }, /* Greek_IOTA */ + { 0x000007e9, 13636 }, /* Greek_iota */ + { 0x000007a4, 13647 }, /* Greek_IOTAaccent */ + { 0x000007b4, 13664 }, /* Greek_iotaaccent */ + { 0x000007b6, 13681 }, /* Greek_iotaaccentdieresis */ + { 0x000007a5, 13706 }, /* Greek_IOTAdiaeresis */ + { 0x000007a5, 13726 }, /* Greek_IOTAdieresis */ + { 0x000007b5, 13745 }, /* Greek_iotadieresis */ + { 0x000007ca, 13764 }, /* Greek_KAPPA */ + { 0x000007ea, 13776 }, /* Greek_kappa */ + { 0x000007cb, 13788 }, /* Greek_LAMBDA */ + { 0x000007eb, 13801 }, /* Greek_lambda */ + { 0x000007cb, 13814 }, /* Greek_LAMDA */ + { 0x000007eb, 13826 }, /* Greek_lamda */ + { 0x000007cc, 13838 }, /* Greek_MU */ + { 0x000007ec, 13847 }, /* Greek_mu */ + { 0x000007cd, 13856 }, /* Greek_NU */ + { 0x000007ed, 13865 }, /* Greek_nu */ + { 0x000007d9, 13874 }, /* Greek_OMEGA */ + { 0x000007f9, 13886 }, /* Greek_omega */ + { 0x000007ab, 13898 }, /* Greek_OMEGAaccent */ + { 0x000007bb, 13916 }, /* Greek_omegaaccent */ + { 0x000007cf, 13934 }, /* Greek_OMICRON */ + { 0x000007ef, 13948 }, /* Greek_omicron */ + { 0x000007a7, 13962 }, /* Greek_OMICRONaccent */ + { 0x000007b7, 13982 }, /* Greek_omicronaccent */ + { 0x000007d6, 14002 }, /* Greek_PHI */ + { 0x000007f6, 14012 }, /* Greek_phi */ + { 0x000007d0, 14022 }, /* Greek_PI */ + { 0x000007f0, 14031 }, /* Greek_pi */ + { 0x000007d8, 14040 }, /* Greek_PSI */ + { 0x000007f8, 14050 }, /* Greek_psi */ + { 0x000007d1, 14060 }, /* Greek_RHO */ + { 0x000007f1, 14070 }, /* Greek_rho */ + { 0x000007d2, 14080 }, /* Greek_SIGMA */ + { 0x000007f2, 14092 }, /* Greek_sigma */ + { 0x0000ff7e, 14104 }, /* Greek_switch */ + { 0x000007d4, 14117 }, /* Greek_TAU */ + { 0x000007f4, 14127 }, /* Greek_tau */ + { 0x000007c8, 14137 }, /* Greek_THETA */ + { 0x000007e8, 14149 }, /* Greek_theta */ + { 0x000007d5, 14161 }, /* Greek_UPSILON */ + { 0x000007f5, 14175 }, /* Greek_upsilon */ + { 0x000007a8, 14189 }, /* Greek_UPSILONaccent */ + { 0x000007b8, 14209 }, /* Greek_upsilonaccent */ + { 0x000007ba, 14229 }, /* Greek_upsilonaccentdieresis */ + { 0x000007a9, 14257 }, /* Greek_UPSILONdieresis */ + { 0x000007b9, 14279 }, /* Greek_upsilondieresis */ + { 0x000007ce, 14301 }, /* Greek_XI */ + { 0x000007ee, 14310 }, /* Greek_xi */ + { 0x000007c6, 14319 }, /* Greek_ZETA */ + { 0x000007e6, 14330 }, /* Greek_zeta */ + { 0x100000be, 14341 }, /* guilder */ + { 0x000000ab, 14349 }, /* guillemotleft */ + { 0x000000bb, 14363 }, /* guillemotright */ + { 0x00000048, 14378 }, /* H */ + { 0x00000068, 14380 }, /* h */ + { 0x00000aa8, 14382 }, /* hairspace */ + { 0x0000ff31, 14392 }, /* Hangul */ + { 0x00000ebf, 14399 }, /* Hangul_A */ + { 0x00000ec0, 14408 }, /* Hangul_AE */ + { 0x00000ef6, 14418 }, /* Hangul_AraeA */ + { 0x00000ef7, 14431 }, /* Hangul_AraeAE */ + { 0x0000ff39, 14445 }, /* Hangul_Banja */ + { 0x00000eba, 14458 }, /* Hangul_Cieuc */ + { 0x0000ff37, 14471 }, /* Hangul_Codeinput */ + { 0x00000ea7, 14488 }, /* Hangul_Dikeud */ + { 0x00000ec4, 14502 }, /* Hangul_E */ + { 0x0000ff33, 14511 }, /* Hangul_End */ + { 0x00000ec3, 14522 }, /* Hangul_EO */ + { 0x00000ed1, 14532 }, /* Hangul_EU */ + { 0x0000ff34, 14542 }, /* Hangul_Hanja */ + { 0x00000ebe, 14555 }, /* Hangul_Hieuh */ + { 0x00000ed3, 14568 }, /* Hangul_I */ + { 0x00000eb7, 14577 }, /* Hangul_Ieung */ + { 0x00000eea, 14590 }, /* Hangul_J_Cieuc */ + { 0x00000eda, 14605 }, /* Hangul_J_Dikeud */ + { 0x00000eee, 14621 }, /* Hangul_J_Hieuh */ + { 0x00000ee8, 14636 }, /* Hangul_J_Ieung */ + { 0x00000ee9, 14651 }, /* Hangul_J_Jieuj */ + { 0x00000eeb, 14666 }, /* Hangul_J_Khieuq */ + { 0x00000ed4, 14682 }, /* Hangul_J_Kiyeog */ + { 0x00000ed6, 14698 }, /* Hangul_J_KiyeogSios */ + { 0x00000ef9, 14718 }, /* Hangul_J_KkogjiDalrinIeung */ + { 0x00000ee3, 14745 }, /* Hangul_J_Mieum */ + { 0x00000ed7, 14760 }, /* Hangul_J_Nieun */ + { 0x00000ed9, 14775 }, /* Hangul_J_NieunHieuh */ + { 0x00000ed8, 14795 }, /* Hangul_J_NieunJieuj */ + { 0x00000ef8, 14815 }, /* Hangul_J_PanSios */ + { 0x00000eed, 14832 }, /* Hangul_J_Phieuf */ + { 0x00000ee4, 14848 }, /* Hangul_J_Pieub */ + { 0x00000ee5, 14863 }, /* Hangul_J_PieubSios */ + { 0x00000edb, 14882 }, /* Hangul_J_Rieul */ + { 0x00000ee2, 14897 }, /* Hangul_J_RieulHieuh */ + { 0x00000edc, 14917 }, /* Hangul_J_RieulKiyeog */ + { 0x00000edd, 14938 }, /* Hangul_J_RieulMieum */ + { 0x00000ee1, 14958 }, /* Hangul_J_RieulPhieuf */ + { 0x00000ede, 14979 }, /* Hangul_J_RieulPieub */ + { 0x00000edf, 14999 }, /* Hangul_J_RieulSios */ + { 0x00000ee0, 15018 }, /* Hangul_J_RieulTieut */ + { 0x00000ee6, 15038 }, /* Hangul_J_Sios */ + { 0x00000ed5, 15052 }, /* Hangul_J_SsangKiyeog */ + { 0x00000ee7, 15073 }, /* Hangul_J_SsangSios */ + { 0x00000eec, 15092 }, /* Hangul_J_Tieut */ + { 0x00000efa, 15107 }, /* Hangul_J_YeorinHieuh */ + { 0x0000ff35, 15128 }, /* Hangul_Jamo */ + { 0x0000ff38, 15140 }, /* Hangul_Jeonja */ + { 0x00000eb8, 15154 }, /* Hangul_Jieuj */ + { 0x00000ebb, 15167 }, /* Hangul_Khieuq */ + { 0x00000ea1, 15181 }, /* Hangul_Kiyeog */ + { 0x00000ea3, 15195 }, /* Hangul_KiyeogSios */ + { 0x00000ef3, 15213 }, /* Hangul_KkogjiDalrinIeung */ + { 0x00000eb1, 15238 }, /* Hangul_Mieum */ + { 0x0000ff3d, 15251 }, /* Hangul_MultipleCandidate */ + { 0x00000ea4, 15276 }, /* Hangul_Nieun */ + { 0x00000ea6, 15289 }, /* Hangul_NieunHieuh */ + { 0x00000ea5, 15307 }, /* Hangul_NieunJieuj */ + { 0x00000ec7, 15325 }, /* Hangul_O */ + { 0x00000eca, 15334 }, /* Hangul_OE */ + { 0x00000ef2, 15344 }, /* Hangul_PanSios */ + { 0x00000ebd, 15359 }, /* Hangul_Phieuf */ + { 0x00000eb2, 15373 }, /* Hangul_Pieub */ + { 0x00000eb4, 15386 }, /* Hangul_PieubSios */ + { 0x0000ff3b, 15403 }, /* Hangul_PostHanja */ + { 0x0000ff3a, 15420 }, /* Hangul_PreHanja */ + { 0x0000ff3e, 15436 }, /* Hangul_PreviousCandidate */ + { 0x00000ea9, 15461 }, /* Hangul_Rieul */ + { 0x00000eb0, 15474 }, /* Hangul_RieulHieuh */ + { 0x00000eaa, 15492 }, /* Hangul_RieulKiyeog */ + { 0x00000eab, 15511 }, /* Hangul_RieulMieum */ + { 0x00000eaf, 15529 }, /* Hangul_RieulPhieuf */ + { 0x00000eac, 15548 }, /* Hangul_RieulPieub */ + { 0x00000ead, 15566 }, /* Hangul_RieulSios */ + { 0x00000eae, 15583 }, /* Hangul_RieulTieut */ + { 0x00000eef, 15601 }, /* Hangul_RieulYeorinHieuh */ + { 0x0000ff36, 15625 }, /* Hangul_Romaja */ + { 0x0000ff3c, 15639 }, /* Hangul_SingleCandidate */ + { 0x00000eb5, 15662 }, /* Hangul_Sios */ + { 0x0000ff3f, 15674 }, /* Hangul_Special */ + { 0x00000ea8, 15689 }, /* Hangul_SsangDikeud */ + { 0x00000eb9, 15708 }, /* Hangul_SsangJieuj */ + { 0x00000ea2, 15726 }, /* Hangul_SsangKiyeog */ + { 0x00000eb3, 15745 }, /* Hangul_SsangPieub */ + { 0x00000eb6, 15763 }, /* Hangul_SsangSios */ + { 0x0000ff32, 15780 }, /* Hangul_Start */ + { 0x00000ef0, 15793 }, /* Hangul_SunkyeongeumMieum */ + { 0x00000ef4, 15818 }, /* Hangul_SunkyeongeumPhieuf */ + { 0x00000ef1, 15844 }, /* Hangul_SunkyeongeumPieub */ + { 0x0000ff7e, 15869 }, /* Hangul_switch */ + { 0x00000ebc, 15883 }, /* Hangul_Tieut */ + { 0x00000ecc, 15896 }, /* Hangul_U */ + { 0x00000ec8, 15905 }, /* Hangul_WA */ + { 0x00000ec9, 15915 }, /* Hangul_WAE */ + { 0x00000ece, 15926 }, /* Hangul_WE */ + { 0x00000ecd, 15936 }, /* Hangul_WEO */ + { 0x00000ecf, 15947 }, /* Hangul_WI */ + { 0x00000ec1, 15957 }, /* Hangul_YA */ + { 0x00000ec2, 15967 }, /* Hangul_YAE */ + { 0x00000ec6, 15978 }, /* Hangul_YE */ + { 0x00000ec5, 15988 }, /* Hangul_YEO */ + { 0x00000ef5, 15999 }, /* Hangul_YeorinHieuh */ + { 0x00000ed2, 16018 }, /* Hangul_YI */ + { 0x00000ecb, 16028 }, /* Hangul_YO */ + { 0x00000ed0, 16038 }, /* Hangul_YU */ + { 0x0000ff29, 16048 }, /* Hankaku */ + { 0x000002a6, 16056 }, /* Hcircumflex */ + { 0x000002b6, 16068 }, /* hcircumflex */ + { 0x00000aee, 16080 }, /* heart */ + { 0x00000ce0, 16086 }, /* hebrew_aleph */ + { 0x00000cf2, 16099 }, /* hebrew_ayin */ + { 0x00000ce1, 16111 }, /* hebrew_bet */ + { 0x00000ce1, 16122 }, /* hebrew_beth */ + { 0x00000ce7, 16134 }, /* hebrew_chet */ + { 0x00000ce3, 16146 }, /* hebrew_dalet */ + { 0x00000ce3, 16159 }, /* hebrew_daleth */ + { 0x00000cdf, 16173 }, /* hebrew_doublelowline */ + { 0x00000cea, 16194 }, /* hebrew_finalkaph */ + { 0x00000ced, 16211 }, /* hebrew_finalmem */ + { 0x00000cef, 16227 }, /* hebrew_finalnun */ + { 0x00000cf3, 16243 }, /* hebrew_finalpe */ + { 0x00000cf5, 16258 }, /* hebrew_finalzade */ + { 0x00000cf5, 16275 }, /* hebrew_finalzadi */ + { 0x00000ce2, 16292 }, /* hebrew_gimel */ + { 0x00000ce2, 16305 }, /* hebrew_gimmel */ + { 0x00000ce4, 16319 }, /* hebrew_he */ + { 0x00000ce7, 16329 }, /* hebrew_het */ + { 0x00000ceb, 16340 }, /* hebrew_kaph */ + { 0x00000cf7, 16352 }, /* hebrew_kuf */ + { 0x00000cec, 16363 }, /* hebrew_lamed */ + { 0x00000cee, 16376 }, /* hebrew_mem */ + { 0x00000cf0, 16387 }, /* hebrew_nun */ + { 0x00000cf4, 16398 }, /* hebrew_pe */ + { 0x00000cf7, 16408 }, /* hebrew_qoph */ + { 0x00000cf8, 16420 }, /* hebrew_resh */ + { 0x00000cf1, 16432 }, /* hebrew_samech */ + { 0x00000cf1, 16446 }, /* hebrew_samekh */ + { 0x00000cf9, 16460 }, /* hebrew_shin */ + { 0x0000ff7e, 16472 }, /* Hebrew_switch */ + { 0x00000cfa, 16486 }, /* hebrew_taf */ + { 0x00000cfa, 16497 }, /* hebrew_taw */ + { 0x00000ce8, 16508 }, /* hebrew_tet */ + { 0x00000ce8, 16519 }, /* hebrew_teth */ + { 0x00000ce5, 16531 }, /* hebrew_waw */ + { 0x00000ce9, 16542 }, /* hebrew_yod */ + { 0x00000cf6, 16553 }, /* hebrew_zade */ + { 0x00000cf6, 16565 }, /* hebrew_zadi */ + { 0x00000ce6, 16577 }, /* hebrew_zain */ + { 0x00000ce6, 16589 }, /* hebrew_zayin */ + { 0x0000ff6a, 16602 }, /* Help */ + { 0x0000ff23, 16607 }, /* Henkan */ + { 0x0000ff23, 16614 }, /* Henkan_Mode */ + { 0x00000ada, 16626 }, /* hexagram */ + { 0x0000ff25, 16635 }, /* Hiragana */ + { 0x0000ff27, 16644 }, /* Hiragana_Katakana */ + { 0x0000ff50, 16662 }, /* Home */ + { 0x000008a3, 16667 }, /* horizconnector */ + { 0x000009ef, 16682 }, /* horizlinescan1 */ + { 0x000009f0, 16697 }, /* horizlinescan3 */ + { 0x000009f1, 16712 }, /* horizlinescan5 */ + { 0x000009f2, 16727 }, /* horizlinescan7 */ + { 0x000009f3, 16742 }, /* horizlinescan9 */ + { 0x1000ff74, 16757 }, /* hpBackTab */ + { 0x100000fc, 16767 }, /* hpblock */ + { 0x1000ff6f, 16775 }, /* hpClearLine */ + { 0x1000ff73, 16787 }, /* hpDeleteChar */ + { 0x1000ff71, 16800 }, /* hpDeleteLine */ + { 0x100000be, 16813 }, /* hpguilder */ + { 0x1000ff72, 16823 }, /* hpInsertChar */ + { 0x1000ff70, 16836 }, /* hpInsertLine */ + { 0x100000ee, 16849 }, /* hpIO */ + { 0x1000ff75, 16854 }, /* hpKP_BackTab */ + { 0x100000af, 16867 }, /* hplira */ + { 0x100000f6, 16874 }, /* hplongminus */ + { 0x1000ff48, 16886 }, /* hpModelock1 */ + { 0x1000ff49, 16898 }, /* hpModelock2 */ + { 0x100000a8, 16910 }, /* hpmute_acute */ + { 0x100000aa, 16923 }, /* hpmute_asciicircum */ + { 0x100000ac, 16942 }, /* hpmute_asciitilde */ + { 0x100000ab, 16960 }, /* hpmute_diaeresis */ + { 0x100000a9, 16977 }, /* hpmute_grave */ + { 0x1000ff6c, 16990 }, /* hpReset */ + { 0x1000ff6d, 16998 }, /* hpSystem */ + { 0x1000ff6e, 17007 }, /* hpUser */ + { 0x100000ee, 17014 }, /* hpYdiaeresis */ + { 0x000002a1, 17027 }, /* Hstroke */ + { 0x000002b1, 17035 }, /* hstroke */ + { 0x000009e2, 17043 }, /* ht */ + { 0x0000ffed, 17046 }, /* Hyper_L */ + { 0x0000ffee, 17054 }, /* Hyper_R */ + { 0x000000ad, 17062 }, /* hyphen */ + { 0x00000049, 17069 }, /* I */ + { 0x00000069, 17071 }, /* i */ + { 0x000002a9, 17073 }, /* Iabovedot */ + { 0x000000cd, 17083 }, /* Iacute */ + { 0x000000ed, 17090 }, /* iacute */ + { 0x01001eca, 17097 }, /* Ibelowdot */ + { 0x01001ecb, 17107 }, /* ibelowdot */ + { 0x0100012c, 17117 }, /* Ibreve */ + { 0x0100012d, 17124 }, /* ibreve */ + { 0x000000ce, 17131 }, /* Icircumflex */ + { 0x000000ee, 17143 }, /* icircumflex */ + { 0x000008cf, 17155 }, /* identical */ + { 0x000000cf, 17165 }, /* Idiaeresis */ + { 0x000000ef, 17176 }, /* idiaeresis */ + { 0x000002b9, 17187 }, /* idotless */ + { 0x000008cd, 17196 }, /* ifonlyif */ + { 0x000000cc, 17205 }, /* Igrave */ + { 0x000000ec, 17212 }, /* igrave */ + { 0x01001ec8, 17219 }, /* Ihook */ + { 0x01001ec9, 17225 }, /* ihook */ + { 0x000003cf, 17231 }, /* Imacron */ + { 0x000003ef, 17239 }, /* imacron */ + { 0x000008ce, 17247 }, /* implies */ + { 0x000008da, 17255 }, /* includedin */ + { 0x000008db, 17266 }, /* includes */ + { 0x000008c2, 17275 }, /* infinity */ + { 0x0000ff63, 17284 }, /* Insert */ + { 0x1000ff72, 17291 }, /* InsertChar */ + { 0x1000ff70, 17302 }, /* InsertLine */ + { 0x000008bf, 17313 }, /* integral */ + { 0x000008dc, 17322 }, /* intersection */ + { 0x100000ee, 17335 }, /* IO */ + { 0x000003c7, 17338 }, /* Iogonek */ + { 0x000003e7, 17346 }, /* iogonek */ + { 0x0000fe33, 17354 }, /* ISO_Center_Object */ + { 0x0000fe30, 17372 }, /* ISO_Continuous_Underline */ + { 0x0000fe31, 17397 }, /* ISO_Discontinuous_Underline */ + { 0x0000fe32, 17425 }, /* ISO_Emphasize */ + { 0x0000fe34, 17439 }, /* ISO_Enter */ + { 0x0000fe2f, 17449 }, /* ISO_Fast_Cursor_Down */ + { 0x0000fe2c, 17470 }, /* ISO_Fast_Cursor_Left */ + { 0x0000fe2d, 17491 }, /* ISO_Fast_Cursor_Right */ + { 0x0000fe2e, 17513 }, /* ISO_Fast_Cursor_Up */ + { 0x0000fe0c, 17532 }, /* ISO_First_Group */ + { 0x0000fe0d, 17548 }, /* ISO_First_Group_Lock */ + { 0x0000fe06, 17569 }, /* ISO_Group_Latch */ + { 0x0000fe07, 17585 }, /* ISO_Group_Lock */ + { 0x0000ff7e, 17600 }, /* ISO_Group_Shift */ + { 0x0000fe0e, 17616 }, /* ISO_Last_Group */ + { 0x0000fe0f, 17631 }, /* ISO_Last_Group_Lock */ + { 0x0000fe20, 17651 }, /* ISO_Left_Tab */ + { 0x0000fe02, 17664 }, /* ISO_Level2_Latch */ + { 0x0000fe04, 17681 }, /* ISO_Level3_Latch */ + { 0x0000fe05, 17698 }, /* ISO_Level3_Lock */ + { 0x0000fe03, 17714 }, /* ISO_Level3_Shift */ + { 0x0000fe12, 17731 }, /* ISO_Level5_Latch */ + { 0x0000fe13, 17748 }, /* ISO_Level5_Lock */ + { 0x0000fe11, 17764 }, /* ISO_Level5_Shift */ + { 0x0000fe01, 17781 }, /* ISO_Lock */ + { 0x0000fe22, 17790 }, /* ISO_Move_Line_Down */ + { 0x0000fe21, 17809 }, /* ISO_Move_Line_Up */ + { 0x0000fe08, 17826 }, /* ISO_Next_Group */ + { 0x0000fe09, 17841 }, /* ISO_Next_Group_Lock */ + { 0x0000fe24, 17861 }, /* ISO_Partial_Line_Down */ + { 0x0000fe23, 17883 }, /* ISO_Partial_Line_Up */ + { 0x0000fe25, 17903 }, /* ISO_Partial_Space_Left */ + { 0x0000fe26, 17926 }, /* ISO_Partial_Space_Right */ + { 0x0000fe0a, 17950 }, /* ISO_Prev_Group */ + { 0x0000fe0b, 17965 }, /* ISO_Prev_Group_Lock */ + { 0x0000fe2b, 17985 }, /* ISO_Release_Both_Margins */ + { 0x0000fe29, 18010 }, /* ISO_Release_Margin_Left */ + { 0x0000fe2a, 18034 }, /* ISO_Release_Margin_Right */ + { 0x0000fe27, 18059 }, /* ISO_Set_Margin_Left */ + { 0x0000fe28, 18079 }, /* ISO_Set_Margin_Right */ + { 0x000003a5, 18100 }, /* Itilde */ + { 0x000003b5, 18107 }, /* itilde */ + { 0x0000004a, 18114 }, /* J */ + { 0x0000006a, 18116 }, /* j */ + { 0x000002ac, 18118 }, /* Jcircumflex */ + { 0x000002bc, 18130 }, /* jcircumflex */ + { 0x00000bca, 18142 }, /* jot */ + { 0x0000004b, 18146 }, /* K */ + { 0x0000006b, 18148 }, /* k */ + { 0x000004a7, 18150 }, /* kana_a */ + { 0x000004b1, 18157 }, /* kana_A */ + { 0x000004c1, 18164 }, /* kana_CHI */ + { 0x000004a3, 18173 }, /* kana_closingbracket */ + { 0x000004a4, 18193 }, /* kana_comma */ + { 0x000004a5, 18204 }, /* kana_conjunctive */ + { 0x000004aa, 18221 }, /* kana_e */ + { 0x000004b4, 18228 }, /* kana_E */ + { 0x000004cc, 18235 }, /* kana_FU */ + { 0x000004a1, 18243 }, /* kana_fullstop */ + { 0x000004ca, 18257 }, /* kana_HA */ + { 0x000004cd, 18265 }, /* kana_HE */ + { 0x000004cb, 18273 }, /* kana_HI */ + { 0x000004ce, 18281 }, /* kana_HO */ + { 0x000004cc, 18289 }, /* kana_HU */ + { 0x000004a8, 18297 }, /* kana_i */ + { 0x000004b2, 18304 }, /* kana_I */ + { 0x000004b6, 18311 }, /* kana_KA */ + { 0x000004b9, 18319 }, /* kana_KE */ + { 0x000004b7, 18327 }, /* kana_KI */ + { 0x000004ba, 18335 }, /* kana_KO */ + { 0x000004b8, 18343 }, /* kana_KU */ + { 0x0000ff2d, 18351 }, /* Kana_Lock */ + { 0x000004cf, 18361 }, /* kana_MA */ + { 0x000004d2, 18369 }, /* kana_ME */ + { 0x000004d0, 18377 }, /* kana_MI */ + { 0x000004a5, 18385 }, /* kana_middledot */ + { 0x000004d3, 18400 }, /* kana_MO */ + { 0x000004d1, 18408 }, /* kana_MU */ + { 0x000004dd, 18416 }, /* kana_N */ + { 0x000004c5, 18423 }, /* kana_NA */ + { 0x000004c8, 18431 }, /* kana_NE */ + { 0x000004c6, 18439 }, /* kana_NI */ + { 0x000004c9, 18447 }, /* kana_NO */ + { 0x000004c7, 18455 }, /* kana_NU */ + { 0x000004ab, 18463 }, /* kana_o */ + { 0x000004b5, 18470 }, /* kana_O */ + { 0x000004a2, 18477 }, /* kana_openingbracket */ + { 0x000004d7, 18497 }, /* kana_RA */ + { 0x000004da, 18505 }, /* kana_RE */ + { 0x000004d8, 18513 }, /* kana_RI */ + { 0x000004db, 18521 }, /* kana_RO */ + { 0x000004d9, 18529 }, /* kana_RU */ + { 0x000004bb, 18537 }, /* kana_SA */ + { 0x000004be, 18545 }, /* kana_SE */ + { 0x000004bc, 18553 }, /* kana_SHI */ + { 0x0000ff2e, 18562 }, /* Kana_Shift */ + { 0x000004bf, 18573 }, /* kana_SO */ + { 0x000004bd, 18581 }, /* kana_SU */ + { 0x0000ff7e, 18589 }, /* kana_switch */ + { 0x000004c0, 18601 }, /* kana_TA */ + { 0x000004c3, 18609 }, /* kana_TE */ + { 0x000004c1, 18617 }, /* kana_TI */ + { 0x000004c4, 18625 }, /* kana_TO */ + { 0x000004af, 18633 }, /* kana_tsu */ + { 0x000004c2, 18642 }, /* kana_TSU */ + { 0x000004af, 18651 }, /* kana_tu */ + { 0x000004c2, 18659 }, /* kana_TU */ + { 0x000004a9, 18667 }, /* kana_u */ + { 0x000004b3, 18674 }, /* kana_U */ + { 0x000004dc, 18681 }, /* kana_WA */ + { 0x000004a6, 18689 }, /* kana_WO */ + { 0x000004ac, 18697 }, /* kana_ya */ + { 0x000004d4, 18705 }, /* kana_YA */ + { 0x000004ae, 18713 }, /* kana_yo */ + { 0x000004d6, 18721 }, /* kana_YO */ + { 0x000004ad, 18729 }, /* kana_yu */ + { 0x000004d5, 18737 }, /* kana_YU */ + { 0x0000ff21, 18745 }, /* Kanji */ + { 0x0000ff37, 18751 }, /* Kanji_Bangou */ + { 0x000003a2, 18764 }, /* kappa */ + { 0x0000ff26, 18770 }, /* Katakana */ + { 0x000003d3, 18779 }, /* Kcedilla */ + { 0x000003f3, 18788 }, /* kcedilla */ + { 0x00000eff, 18797 }, /* Korean_Won */ + { 0x0000ffb0, 18808 }, /* KP_0 */ + { 0x0000ffb1, 18813 }, /* KP_1 */ + { 0x0000ffb2, 18818 }, /* KP_2 */ + { 0x0000ffb3, 18823 }, /* KP_3 */ + { 0x0000ffb4, 18828 }, /* KP_4 */ + { 0x0000ffb5, 18833 }, /* KP_5 */ + { 0x0000ffb6, 18838 }, /* KP_6 */ + { 0x0000ffb7, 18843 }, /* KP_7 */ + { 0x0000ffb8, 18848 }, /* KP_8 */ + { 0x0000ffb9, 18853 }, /* KP_9 */ + { 0x0000ffab, 18858 }, /* KP_Add */ + { 0x1000ff75, 18865 }, /* KP_BackTab */ + { 0x0000ff9d, 18876 }, /* KP_Begin */ + { 0x0000ffae, 18885 }, /* KP_Decimal */ + { 0x0000ff9f, 18896 }, /* KP_Delete */ + { 0x0000ffaf, 18906 }, /* KP_Divide */ + { 0x0000ff99, 18916 }, /* KP_Down */ + { 0x0000ff9c, 18924 }, /* KP_End */ + { 0x0000ff8d, 18931 }, /* KP_Enter */ + { 0x0000ffbd, 18940 }, /* KP_Equal */ + { 0x0000ff91, 18949 }, /* KP_F1 */ + { 0x0000ff92, 18955 }, /* KP_F2 */ + { 0x0000ff93, 18961 }, /* KP_F3 */ + { 0x0000ff94, 18967 }, /* KP_F4 */ + { 0x0000ff95, 18973 }, /* KP_Home */ + { 0x0000ff9e, 18981 }, /* KP_Insert */ + { 0x0000ff96, 18991 }, /* KP_Left */ + { 0x0000ffaa, 18999 }, /* KP_Multiply */ + { 0x0000ff9b, 19011 }, /* KP_Next */ + { 0x0000ff9b, 19019 }, /* KP_Page_Down */ + { 0x0000ff9a, 19032 }, /* KP_Page_Up */ + { 0x0000ff9a, 19043 }, /* KP_Prior */ + { 0x0000ff98, 19052 }, /* KP_Right */ + { 0x0000ffac, 19061 }, /* KP_Separator */ + { 0x0000ff80, 19074 }, /* KP_Space */ + { 0x0000ffad, 19083 }, /* KP_Subtract */ + { 0x0000ff89, 19095 }, /* KP_Tab */ + { 0x0000ff97, 19102 }, /* KP_Up */ + { 0x000003a2, 19108 }, /* kra */ + { 0x0000004c, 19112 }, /* L */ + { 0x0000006c, 19114 }, /* l */ + { 0x0000ffc8, 19116 }, /* L1 */ + { 0x0000ffd1, 19119 }, /* L10 */ + { 0x0000ffc9, 19123 }, /* L2 */ + { 0x0000ffca, 19126 }, /* L3 */ + { 0x0000ffcb, 19129 }, /* L4 */ + { 0x0000ffcc, 19132 }, /* L5 */ + { 0x0000ffcd, 19135 }, /* L6 */ + { 0x0000ffce, 19138 }, /* L7 */ + { 0x0000ffcf, 19141 }, /* L8 */ + { 0x0000ffd0, 19144 }, /* L9 */ + { 0x000001c5, 19147 }, /* Lacute */ + { 0x000001e5, 19154 }, /* lacute */ + { 0x0000fed4, 19161 }, /* Last_Virtual_Screen */ + { 0x00000ad9, 19181 }, /* latincross */ + { 0x01001e36, 19192 }, /* Lbelowdot */ + { 0x01001e37, 19202 }, /* lbelowdot */ + { 0x000001a5, 19212 }, /* Lcaron */ + { 0x000001b5, 19219 }, /* lcaron */ + { 0x000003a6, 19226 }, /* Lcedilla */ + { 0x000003b6, 19235 }, /* lcedilla */ + { 0x0000ff51, 19244 }, /* Left */ + { 0x00000abc, 19249 }, /* leftanglebracket */ + { 0x000008fb, 19266 }, /* leftarrow */ + { 0x00000ba3, 19276 }, /* leftcaret */ + { 0x00000ad2, 19286 }, /* leftdoublequotemark */ + { 0x000008af, 19306 }, /* leftmiddlecurlybrace */ + { 0x00000acc, 19327 }, /* leftopentriangle */ + { 0x00000aea, 19344 }, /* leftpointer */ + { 0x000008a1, 19356 }, /* leftradical */ + { 0x00000bda, 19368 }, /* leftshoe */ + { 0x00000ad0, 19377 }, /* leftsinglequotemark */ + { 0x000009f4, 19397 }, /* leftt */ + { 0x00000bdc, 19403 }, /* lefttack */ + { 0x0000003c, 19412 }, /* less */ + { 0x000008bc, 19417 }, /* lessthanequal */ + { 0x000009e5, 19431 }, /* lf */ + { 0x0000ff0a, 19434 }, /* Linefeed */ + { 0x100000af, 19443 }, /* lira */ + { 0x010020a4, 19448 }, /* LiraSign */ + { 0x000008de, 19457 }, /* logicaland */ + { 0x000008df, 19468 }, /* logicalor */ + { 0x100000f6, 19478 }, /* longminus */ + { 0x000009ed, 19488 }, /* lowleftcorner */ + { 0x000009ea, 19502 }, /* lowrightcorner */ + { 0x000001a3, 19517 }, /* Lstroke */ + { 0x000001b3, 19525 }, /* lstroke */ + { 0x0000004d, 19533 }, /* M */ + { 0x0000006d, 19535 }, /* m */ + { 0x01001e40, 19537 }, /* Mabovedot */ + { 0x01001e41, 19547 }, /* mabovedot */ + { 0x000006a5, 19557 }, /* Macedonia_dse */ + { 0x000006b5, 19571 }, /* Macedonia_DSE */ + { 0x000006a2, 19585 }, /* Macedonia_gje */ + { 0x000006b2, 19599 }, /* Macedonia_GJE */ + { 0x000006ac, 19613 }, /* Macedonia_kje */ + { 0x000006bc, 19627 }, /* Macedonia_KJE */ + { 0x000000af, 19641 }, /* macron */ + { 0x0000ff3e, 19648 }, /* Mae_Koho */ + { 0x00000af7, 19657 }, /* malesymbol */ + { 0x00000af0, 19668 }, /* maltesecross */ + { 0x00000abf, 19681 }, /* marker */ + { 0x000000ba, 19688 }, /* masculine */ + { 0x0000ff2c, 19698 }, /* Massyo */ + { 0x0000ff67, 19705 }, /* Menu */ + { 0x0000ffe7, 19710 }, /* Meta_L */ + { 0x0000ffe8, 19717 }, /* Meta_R */ + { 0x010020a5, 19724 }, /* MillSign */ + { 0x0000002d, 19733 }, /* minus */ + { 0x00000ad6, 19739 }, /* minutes */ + { 0x0000ff7e, 19747 }, /* Mode_switch */ + { 0x0000fe77, 19759 }, /* MouseKeys_Accel_Enable */ + { 0x0000fe76, 19782 }, /* MouseKeys_Enable */ + { 0x000000b5, 19799 }, /* mu */ + { 0x0000ff22, 19802 }, /* Muhenkan */ + { 0x0000ff20, 19811 }, /* Multi_key */ + { 0x0000ff3d, 19821 }, /* MultipleCandidate */ + { 0x000000d7, 19839 }, /* multiply */ + { 0x00000af6, 19848 }, /* musicalflat */ + { 0x00000af5, 19860 }, /* musicalsharp */ + { 0x100000a8, 19873 }, /* mute_acute */ + { 0x100000aa, 19884 }, /* mute_asciicircum */ + { 0x100000ac, 19901 }, /* mute_asciitilde */ + { 0x100000ab, 19917 }, /* mute_diaeresis */ + { 0x100000a9, 19932 }, /* mute_grave */ + { 0x0000004e, 19943 }, /* N */ + { 0x0000006e, 19945 }, /* n */ + { 0x000008c5, 19947 }, /* nabla */ + { 0x000001d1, 19953 }, /* Nacute */ + { 0x000001f1, 19960 }, /* nacute */ + { 0x010020a6, 19967 }, /* NairaSign */ + { 0x000001d2, 19977 }, /* Ncaron */ + { 0x000001f2, 19984 }, /* ncaron */ + { 0x000003d1, 19991 }, /* Ncedilla */ + { 0x000003f1, 20000 }, /* ncedilla */ + { 0x010020aa, 20009 }, /* NewSheqelSign */ + { 0x0000ff56, 20023 }, /* Next */ + { 0x0000fed2, 20028 }, /* Next_Virtual_Screen */ + { 0x01002089, 20048 }, /* ninesubscript */ + { 0x01002079, 20062 }, /* ninesuperior */ + { 0x000009e8, 20075 }, /* nl */ + { 0x000000a0, 20078 }, /* nobreakspace */ + { 0x00000000, 20091 }, /* NoSymbol */ + { 0x01002247, 20100 }, /* notapproxeq */ + { 0x01002209, 20112 }, /* notelementof */ + { 0x000008bd, 20125 }, /* notequal */ + { 0x01002262, 20134 }, /* notidentical */ + { 0x000000ac, 20147 }, /* notsign */ + { 0x000000d1, 20155 }, /* Ntilde */ + { 0x000000f1, 20162 }, /* ntilde */ + { 0x0000ff7f, 20169 }, /* Num_Lock */ + { 0x00000023, 20178 }, /* numbersign */ + { 0x000006b0, 20189 }, /* numerosign */ + { 0x0000004f, 20200 }, /* O */ + { 0x0000006f, 20202 }, /* o */ + { 0x000000d3, 20204 }, /* Oacute */ + { 0x000000f3, 20211 }, /* oacute */ + { 0x0100019f, 20218 }, /* Obarred */ + { 0x01000275, 20226 }, /* obarred */ + { 0x01001ecc, 20234 }, /* Obelowdot */ + { 0x01001ecd, 20244 }, /* obelowdot */ + { 0x010001d1, 20254 }, /* Ocaron */ + { 0x010001d2, 20261 }, /* ocaron */ + { 0x000000d4, 20268 }, /* Ocircumflex */ + { 0x000000f4, 20280 }, /* ocircumflex */ + { 0x01001ed0, 20292 }, /* Ocircumflexacute */ + { 0x01001ed1, 20309 }, /* ocircumflexacute */ + { 0x01001ed8, 20326 }, /* Ocircumflexbelowdot */ + { 0x01001ed9, 20346 }, /* ocircumflexbelowdot */ + { 0x01001ed2, 20366 }, /* Ocircumflexgrave */ + { 0x01001ed3, 20383 }, /* ocircumflexgrave */ + { 0x01001ed4, 20400 }, /* Ocircumflexhook */ + { 0x01001ed5, 20416 }, /* ocircumflexhook */ + { 0x01001ed6, 20432 }, /* Ocircumflextilde */ + { 0x01001ed7, 20449 }, /* ocircumflextilde */ + { 0x000000d6, 20466 }, /* Odiaeresis */ + { 0x000000f6, 20477 }, /* odiaeresis */ + { 0x000001d5, 20488 }, /* Odoubleacute */ + { 0x000001f5, 20501 }, /* odoubleacute */ + { 0x000013bc, 20514 }, /* OE */ + { 0x000013bd, 20517 }, /* oe */ + { 0x000001b2, 20520 }, /* ogonek */ + { 0x000000d2, 20527 }, /* Ograve */ + { 0x000000f2, 20534 }, /* ograve */ + { 0x01001ece, 20541 }, /* Ohook */ + { 0x01001ecf, 20547 }, /* ohook */ + { 0x010001a0, 20553 }, /* Ohorn */ + { 0x010001a1, 20559 }, /* ohorn */ + { 0x01001eda, 20565 }, /* Ohornacute */ + { 0x01001edb, 20576 }, /* ohornacute */ + { 0x01001ee2, 20587 }, /* Ohornbelowdot */ + { 0x01001ee3, 20601 }, /* ohornbelowdot */ + { 0x01001edc, 20615 }, /* Ohorngrave */ + { 0x01001edd, 20626 }, /* ohorngrave */ + { 0x01001ede, 20637 }, /* Ohornhook */ + { 0x01001edf, 20647 }, /* ohornhook */ + { 0x01001ee0, 20657 }, /* Ohorntilde */ + { 0x01001ee1, 20668 }, /* ohorntilde */ + { 0x000003d2, 20679 }, /* Omacron */ + { 0x000003f2, 20687 }, /* omacron */ + { 0x00000ac3, 20695 }, /* oneeighth */ + { 0x00000ab2, 20705 }, /* onefifth */ + { 0x000000bd, 20714 }, /* onehalf */ + { 0x000000bc, 20722 }, /* onequarter */ + { 0x00000ab6, 20733 }, /* onesixth */ + { 0x01002081, 20742 }, /* onesubscript */ + { 0x000000b9, 20755 }, /* onesuperior */ + { 0x00000ab0, 20767 }, /* onethird */ + { 0x000000d8, 20776 }, /* Ooblique */ + { 0x000000f8, 20785 }, /* ooblique */ + { 0x00000ae2, 20794 }, /* openrectbullet */ + { 0x00000ae5, 20809 }, /* openstar */ + { 0x00000ae4, 20818 }, /* opentribulletdown */ + { 0x00000ae3, 20836 }, /* opentribulletup */ + { 0x000000aa, 20852 }, /* ordfeminine */ + { 0x1004ff44, 20864 }, /* osfActivate */ + { 0x1004ff31, 20876 }, /* osfAddMode */ + { 0x1004ff08, 20887 }, /* osfBackSpace */ + { 0x1004ff07, 20900 }, /* osfBackTab */ + { 0x1004ff5a, 20911 }, /* osfBeginData */ + { 0x1004ff58, 20924 }, /* osfBeginLine */ + { 0x1004ff69, 20937 }, /* osfCancel */ + { 0x1004ff0b, 20947 }, /* osfClear */ + { 0x1004ff02, 20956 }, /* osfCopy */ + { 0x1004ff03, 20964 }, /* osfCut */ + { 0x1004ffff, 20971 }, /* osfDelete */ + { 0x1004ff72, 20981 }, /* osfDeselectAll */ + { 0x1004ff54, 20996 }, /* osfDown */ + { 0x1004ff59, 21004 }, /* osfEndData */ + { 0x1004ff57, 21015 }, /* osfEndLine */ + { 0x1004ff1b, 21026 }, /* osfEscape */ + { 0x1004ff74, 21036 }, /* osfExtend */ + { 0x1004ff6a, 21046 }, /* osfHelp */ + { 0x1004ff63, 21054 }, /* osfInsert */ + { 0x1004ff51, 21064 }, /* osfLeft */ + { 0x1004ff67, 21072 }, /* osfMenu */ + { 0x1004ff45, 21080 }, /* osfMenuBar */ + { 0x1004ff5e, 21091 }, /* osfNextField */ + { 0x1004ff5c, 21104 }, /* osfNextMenu */ + { 0x1004ff42, 21116 }, /* osfPageDown */ + { 0x1004ff40, 21128 }, /* osfPageLeft */ + { 0x1004ff43, 21140 }, /* osfPageRight */ + { 0x1004ff41, 21153 }, /* osfPageUp */ + { 0x1004ff04, 21163 }, /* osfPaste */ + { 0x1004ff5d, 21172 }, /* osfPrevField */ + { 0x1004ff5b, 21185 }, /* osfPrevMenu */ + { 0x1004ff32, 21197 }, /* osfPrimaryPaste */ + { 0x1004ff33, 21213 }, /* osfQuickPaste */ + { 0x1004ff73, 21227 }, /* osfReselect */ + { 0x1004ff78, 21239 }, /* osfRestore */ + { 0x1004ff53, 21250 }, /* osfRight */ + { 0x1004ff60, 21259 }, /* osfSelect */ + { 0x1004ff71, 21269 }, /* osfSelectAll */ + { 0x1004ff65, 21282 }, /* osfUndo */ + { 0x1004ff52, 21290 }, /* osfUp */ + { 0x000000d8, 21296 }, /* Oslash */ + { 0x000000f8, 21303 }, /* oslash */ + { 0x000000d5, 21310 }, /* Otilde */ + { 0x000000f5, 21317 }, /* otilde */ + { 0x00000bc0, 21324 }, /* overbar */ + { 0x0000fe78, 21332 }, /* Overlay1_Enable */ + { 0x0000fe79, 21348 }, /* Overlay2_Enable */ + { 0x0000047e, 21364 }, /* overline */ + { 0x00000050, 21373 }, /* P */ + { 0x00000070, 21375 }, /* p */ + { 0x01001e56, 21377 }, /* Pabovedot */ + { 0x01001e57, 21387 }, /* pabovedot */ + { 0x0000ff56, 21397 }, /* Page_Down */ + { 0x0000ff55, 21407 }, /* Page_Up */ + { 0x000000b6, 21415 }, /* paragraph */ + { 0x00000028, 21425 }, /* parenleft */ + { 0x00000029, 21435 }, /* parenright */ + { 0x01002202, 21446 }, /* partdifferential */ + { 0x000008ef, 21463 }, /* partialderivative */ + { 0x0000ff13, 21481 }, /* Pause */ + { 0x00000025, 21487 }, /* percent */ + { 0x0000002e, 21495 }, /* period */ + { 0x000000b7, 21502 }, /* periodcentered */ + { 0x00000ad5, 21517 }, /* permille */ + { 0x010020a7, 21526 }, /* PesetaSign */ + { 0x00000afb, 21537 }, /* phonographcopyright */ + { 0x0000002b, 21557 }, /* plus */ + { 0x000000b1, 21562 }, /* plusminus */ + { 0x0000fefa, 21572 }, /* Pointer_Accelerate */ + { 0x0000fee9, 21591 }, /* Pointer_Button1 */ + { 0x0000feea, 21607 }, /* Pointer_Button2 */ + { 0x0000feeb, 21623 }, /* Pointer_Button3 */ + { 0x0000feec, 21639 }, /* Pointer_Button4 */ + { 0x0000feed, 21655 }, /* Pointer_Button5 */ + { 0x0000fee8, 21671 }, /* Pointer_Button_Dflt */ + { 0x0000feef, 21691 }, /* Pointer_DblClick1 */ + { 0x0000fef0, 21709 }, /* Pointer_DblClick2 */ + { 0x0000fef1, 21727 }, /* Pointer_DblClick3 */ + { 0x0000fef2, 21745 }, /* Pointer_DblClick4 */ + { 0x0000fef3, 21763 }, /* Pointer_DblClick5 */ + { 0x0000feee, 21781 }, /* Pointer_DblClick_Dflt */ + { 0x0000fefb, 21803 }, /* Pointer_DfltBtnNext */ + { 0x0000fefc, 21823 }, /* Pointer_DfltBtnPrev */ + { 0x0000fee3, 21843 }, /* Pointer_Down */ + { 0x0000fee6, 21856 }, /* Pointer_DownLeft */ + { 0x0000fee7, 21873 }, /* Pointer_DownRight */ + { 0x0000fef5, 21891 }, /* Pointer_Drag1 */ + { 0x0000fef6, 21905 }, /* Pointer_Drag2 */ + { 0x0000fef7, 21919 }, /* Pointer_Drag3 */ + { 0x0000fef8, 21933 }, /* Pointer_Drag4 */ + { 0x0000fefd, 21947 }, /* Pointer_Drag5 */ + { 0x0000fef4, 21961 }, /* Pointer_Drag_Dflt */ + { 0x0000fef9, 21979 }, /* Pointer_EnableKeys */ + { 0x0000fee0, 21998 }, /* Pointer_Left */ + { 0x0000fee1, 22011 }, /* Pointer_Right */ + { 0x0000fee2, 22025 }, /* Pointer_Up */ + { 0x0000fee4, 22036 }, /* Pointer_UpLeft */ + { 0x0000fee5, 22051 }, /* Pointer_UpRight */ + { 0x00000ad4, 22067 }, /* prescription */ + { 0x0000fed1, 22080 }, /* Prev_Virtual_Screen */ + { 0x0000ff3e, 22100 }, /* PreviousCandidate */ + { 0x0000ff61, 22118 }, /* Print */ + { 0x0000ff55, 22124 }, /* Prior */ + { 0x000004b0, 22130 }, /* prolongedsound */ + { 0x00000aa6, 22145 }, /* punctspace */ + { 0x00000051, 22156 }, /* Q */ + { 0x00000071, 22158 }, /* q */ + { 0x00000bcc, 22160 }, /* quad */ + { 0x0000003f, 22165 }, /* question */ + { 0x000000bf, 22174 }, /* questiondown */ + { 0x00000022, 22187 }, /* quotedbl */ + { 0x00000060, 22196 }, /* quoteleft */ + { 0x00000027, 22206 }, /* quoteright */ + { 0x00000052, 22217 }, /* R */ + { 0x00000072, 22219 }, /* r */ + { 0x0000ffd2, 22221 }, /* R1 */ + { 0x0000ffdb, 22224 }, /* R10 */ + { 0x0000ffdc, 22228 }, /* R11 */ + { 0x0000ffdd, 22232 }, /* R12 */ + { 0x0000ffde, 22236 }, /* R13 */ + { 0x0000ffdf, 22240 }, /* R14 */ + { 0x0000ffe0, 22244 }, /* R15 */ + { 0x0000ffd3, 22248 }, /* R2 */ + { 0x0000ffd4, 22251 }, /* R3 */ + { 0x0000ffd5, 22254 }, /* R4 */ + { 0x0000ffd6, 22257 }, /* R5 */ + { 0x0000ffd7, 22260 }, /* R6 */ + { 0x0000ffd8, 22263 }, /* R7 */ + { 0x0000ffd9, 22266 }, /* R8 */ + { 0x0000ffda, 22269 }, /* R9 */ + { 0x000001c0, 22272 }, /* Racute */ + { 0x000001e0, 22279 }, /* racute */ + { 0x000008d6, 22286 }, /* radical */ + { 0x000001d8, 22294 }, /* Rcaron */ + { 0x000001f8, 22301 }, /* rcaron */ + { 0x000003a3, 22308 }, /* Rcedilla */ + { 0x000003b3, 22317 }, /* rcedilla */ + { 0x0000ff66, 22326 }, /* Redo */ + { 0x000000ae, 22331 }, /* registered */ + { 0x0000fe72, 22342 }, /* RepeatKeys_Enable */ + { 0x1000ff6c, 22360 }, /* Reset */ + { 0x0000ff0d, 22366 }, /* Return */ + { 0x0000ff53, 22373 }, /* Right */ + { 0x00000abe, 22379 }, /* rightanglebracket */ + { 0x000008fd, 22397 }, /* rightarrow */ + { 0x00000ba6, 22408 }, /* rightcaret */ + { 0x00000ad3, 22419 }, /* rightdoublequotemark */ + { 0x000008b0, 22440 }, /* rightmiddlecurlybrace */ + { 0x000008b7, 22462 }, /* rightmiddlesummation */ + { 0x00000acd, 22483 }, /* rightopentriangle */ + { 0x00000aeb, 22501 }, /* rightpointer */ + { 0x00000bd8, 22514 }, /* rightshoe */ + { 0x00000ad1, 22524 }, /* rightsinglequotemark */ + { 0x000009f5, 22545 }, /* rightt */ + { 0x00000bfc, 22552 }, /* righttack */ + { 0x0000ff24, 22562 }, /* Romaji */ + { 0x010020a8, 22569 }, /* RupeeSign */ + { 0x00000053, 22579 }, /* S */ + { 0x00000073, 22581 }, /* s */ + { 0x01001e60, 22583 }, /* Sabovedot */ + { 0x01001e61, 22593 }, /* sabovedot */ + { 0x000001a6, 22603 }, /* Sacute */ + { 0x000001b6, 22610 }, /* sacute */ + { 0x000001a9, 22617 }, /* Scaron */ + { 0x000001b9, 22624 }, /* scaron */ + { 0x000001aa, 22631 }, /* Scedilla */ + { 0x000001ba, 22640 }, /* scedilla */ + { 0x0100018f, 22649 }, /* SCHWA */ + { 0x01000259, 22655 }, /* schwa */ + { 0x000002de, 22661 }, /* Scircumflex */ + { 0x000002fe, 22673 }, /* scircumflex */ + { 0x0000ff7e, 22685 }, /* script_switch */ + { 0x0000ff14, 22699 }, /* Scroll_Lock */ + { 0x00000ad7, 22711 }, /* seconds */ + { 0x000000a7, 22719 }, /* section */ + { 0x0000ff60, 22727 }, /* Select */ + { 0x0000003b, 22734 }, /* semicolon */ + { 0x000004df, 22744 }, /* semivoicedsound */ + { 0x000006a1, 22760 }, /* Serbian_dje */ + { 0x000006b1, 22772 }, /* Serbian_DJE */ + { 0x000006af, 22784 }, /* Serbian_dze */ + { 0x000006bf, 22796 }, /* Serbian_DZE */ + { 0x000006a8, 22808 }, /* Serbian_je */ + { 0x000006b8, 22819 }, /* Serbian_JE */ + { 0x000006a9, 22830 }, /* Serbian_lje */ + { 0x000006b9, 22842 }, /* Serbian_LJE */ + { 0x000006aa, 22854 }, /* Serbian_nje */ + { 0x000006ba, 22866 }, /* Serbian_NJE */ + { 0x000006ab, 22878 }, /* Serbian_tshe */ + { 0x000006bb, 22891 }, /* Serbian_TSHE */ + { 0x00000ac6, 22904 }, /* seveneighths */ + { 0x01002087, 22917 }, /* sevensubscript */ + { 0x01002077, 22932 }, /* sevensuperior */ + { 0x0000ffe1, 22946 }, /* Shift_L */ + { 0x0000ffe6, 22954 }, /* Shift_Lock */ + { 0x0000ffe2, 22965 }, /* Shift_R */ + { 0x00000aca, 22973 }, /* signaturemark */ + { 0x00000aac, 22987 }, /* signifblank */ + { 0x000008c9, 22999 }, /* similarequal */ + { 0x0000ff3c, 23012 }, /* SingleCandidate */ + { 0x00000afd, 23028 }, /* singlelowquotemark */ + { 0x01000d85, 23047 }, /* Sinh_a */ + { 0x01000d86, 23054 }, /* Sinh_aa */ + { 0x01000dcf, 23062 }, /* Sinh_aa2 */ + { 0x01000d87, 23071 }, /* Sinh_ae */ + { 0x01000dd0, 23079 }, /* Sinh_ae2 */ + { 0x01000d88, 23088 }, /* Sinh_aee */ + { 0x01000dd1, 23097 }, /* Sinh_aee2 */ + { 0x01000d93, 23107 }, /* Sinh_ai */ + { 0x01000ddb, 23115 }, /* Sinh_ai2 */ + { 0x01000dca, 23124 }, /* Sinh_al */ + { 0x01000d96, 23132 }, /* Sinh_au */ + { 0x01000dde, 23140 }, /* Sinh_au2 */ + { 0x01000db6, 23149 }, /* Sinh_ba */ + { 0x01000db7, 23157 }, /* Sinh_bha */ + { 0x01000da0, 23166 }, /* Sinh_ca */ + { 0x01000da1, 23174 }, /* Sinh_cha */ + { 0x01000da9, 23183 }, /* Sinh_dda */ + { 0x01000daa, 23192 }, /* Sinh_ddha */ + { 0x01000daf, 23202 }, /* Sinh_dha */ + { 0x01000db0, 23211 }, /* Sinh_dhha */ + { 0x01000d91, 23221 }, /* Sinh_e */ + { 0x01000dd9, 23228 }, /* Sinh_e2 */ + { 0x01000d92, 23236 }, /* Sinh_ee */ + { 0x01000dda, 23244 }, /* Sinh_ee2 */ + { 0x01000dc6, 23253 }, /* Sinh_fa */ + { 0x01000d9c, 23261 }, /* Sinh_ga */ + { 0x01000d9d, 23269 }, /* Sinh_gha */ + { 0x01000d83, 23278 }, /* Sinh_h2 */ + { 0x01000dc4, 23286 }, /* Sinh_ha */ + { 0x01000d89, 23294 }, /* Sinh_i */ + { 0x01000dd2, 23301 }, /* Sinh_i2 */ + { 0x01000d8a, 23309 }, /* Sinh_ii */ + { 0x01000dd3, 23317 }, /* Sinh_ii2 */ + { 0x01000da2, 23326 }, /* Sinh_ja */ + { 0x01000da3, 23334 }, /* Sinh_jha */ + { 0x01000da5, 23343 }, /* Sinh_jnya */ + { 0x01000d9a, 23353 }, /* Sinh_ka */ + { 0x01000d9b, 23361 }, /* Sinh_kha */ + { 0x01000df4, 23370 }, /* Sinh_kunddaliya */ + { 0x01000dbd, 23386 }, /* Sinh_la */ + { 0x01000dc5, 23394 }, /* Sinh_lla */ + { 0x01000d8f, 23403 }, /* Sinh_lu */ + { 0x01000ddf, 23411 }, /* Sinh_lu2 */ + { 0x01000d90, 23420 }, /* Sinh_luu */ + { 0x01000df3, 23429 }, /* Sinh_luu2 */ + { 0x01000db8, 23439 }, /* Sinh_ma */ + { 0x01000db9, 23447 }, /* Sinh_mba */ + { 0x01000db1, 23456 }, /* Sinh_na */ + { 0x01000dac, 23464 }, /* Sinh_ndda */ + { 0x01000db3, 23474 }, /* Sinh_ndha */ + { 0x01000d82, 23484 }, /* Sinh_ng */ + { 0x01000d9e, 23492 }, /* Sinh_ng2 */ + { 0x01000d9f, 23501 }, /* Sinh_nga */ + { 0x01000da6, 23510 }, /* Sinh_nja */ + { 0x01000dab, 23519 }, /* Sinh_nna */ + { 0x01000da4, 23528 }, /* Sinh_nya */ + { 0x01000d94, 23537 }, /* Sinh_o */ + { 0x01000ddc, 23544 }, /* Sinh_o2 */ + { 0x01000d95, 23552 }, /* Sinh_oo */ + { 0x01000ddd, 23560 }, /* Sinh_oo2 */ + { 0x01000db4, 23569 }, /* Sinh_pa */ + { 0x01000db5, 23577 }, /* Sinh_pha */ + { 0x01000dbb, 23586 }, /* Sinh_ra */ + { 0x01000d8d, 23594 }, /* Sinh_ri */ + { 0x01000d8e, 23602 }, /* Sinh_rii */ + { 0x01000dd8, 23611 }, /* Sinh_ru2 */ + { 0x01000df2, 23620 }, /* Sinh_ruu2 */ + { 0x01000dc3, 23630 }, /* Sinh_sa */ + { 0x01000dc1, 23638 }, /* Sinh_sha */ + { 0x01000dc2, 23647 }, /* Sinh_ssha */ + { 0x01000dad, 23657 }, /* Sinh_tha */ + { 0x01000dae, 23666 }, /* Sinh_thha */ + { 0x01000da7, 23676 }, /* Sinh_tta */ + { 0x01000da8, 23685 }, /* Sinh_ttha */ + { 0x01000d8b, 23695 }, /* Sinh_u */ + { 0x01000dd4, 23702 }, /* Sinh_u2 */ + { 0x01000d8c, 23710 }, /* Sinh_uu */ + { 0x01000dd6, 23718 }, /* Sinh_uu2 */ + { 0x01000dc0, 23727 }, /* Sinh_va */ + { 0x01000dba, 23735 }, /* Sinh_ya */ + { 0x01002086, 23743 }, /* sixsubscript */ + { 0x01002076, 23756 }, /* sixsuperior */ + { 0x0000002f, 23768 }, /* slash */ + { 0x0000fe73, 23774 }, /* SlowKeys_Enable */ + { 0x000009e0, 23790 }, /* soliddiamond */ + { 0x00000020, 23803 }, /* space */ + { 0x0100221a, 23809 }, /* squareroot */ + { 0x000000df, 23820 }, /* ssharp */ + { 0x000000a3, 23827 }, /* sterling */ + { 0x0000fe75, 23836 }, /* StickyKeys_Enable */ + { 0x01002263, 23854 }, /* stricteq */ + { 0x0000ff66, 23863 }, /* SunAgain */ + { 0x0000ff7e, 23872 }, /* SunAltGraph */ + { 0x1005ff77, 23884 }, /* SunAudioLowerVolume */ + { 0x1005ff78, 23904 }, /* SunAudioMute */ + { 0x1005ff79, 23917 }, /* SunAudioRaiseVolume */ + { 0x0000ff20, 23937 }, /* SunCompose */ + { 0x1005ff72, 23948 }, /* SunCopy */ + { 0x1005ff75, 23956 }, /* SunCut */ + { 0x1005ff10, 23963 }, /* SunF36 */ + { 0x1005ff11, 23970 }, /* SunF37 */ + { 0x1005ff03, 23977 }, /* SunFA_Acute */ + { 0x1005ff05, 23989 }, /* SunFA_Cedilla */ + { 0x1005ff01, 24003 }, /* SunFA_Circum */ + { 0x1005ff04, 24016 }, /* SunFA_Diaeresis */ + { 0x1005ff00, 24032 }, /* SunFA_Grave */ + { 0x1005ff02, 24044 }, /* SunFA_Tilde */ + { 0x0000ff68, 24056 }, /* SunFind */ + { 0x1005ff71, 24064 }, /* SunFront */ + { 0x1005ff73, 24073 }, /* SunOpen */ + { 0x0000ff56, 24081 }, /* SunPageDown */ + { 0x0000ff55, 24093 }, /* SunPageUp */ + { 0x1005ff74, 24103 }, /* SunPaste */ + { 0x1005ff76, 24112 }, /* SunPowerSwitch */ + { 0x1005ff7d, 24127 }, /* SunPowerSwitchShift */ + { 0x0000ff61, 24147 }, /* SunPrint_Screen */ + { 0x1005ff70, 24163 }, /* SunProps */ + { 0x0000ff69, 24172 }, /* SunStop */ + { 0x1005ff60, 24180 }, /* SunSys_Req */ + { 0x0000ff65, 24191 }, /* SunUndo */ + { 0x1005ff7a, 24199 }, /* SunVideoDegauss */ + { 0x1005ff7b, 24215 }, /* SunVideoLowerBrightness */ + { 0x1005ff7c, 24239 }, /* SunVideoRaiseBrightness */ + { 0x0000ffeb, 24263 }, /* Super_L */ + { 0x0000ffec, 24271 }, /* Super_R */ + { 0x0000ff15, 24279 }, /* Sys_Req */ + { 0x1000ff6d, 24287 }, /* System */ + { 0x00000054, 24294 }, /* T */ + { 0x00000074, 24296 }, /* t */ + { 0x0000ff09, 24298 }, /* Tab */ + { 0x01001e6a, 24302 }, /* Tabovedot */ + { 0x01001e6b, 24312 }, /* tabovedot */ + { 0x000001ab, 24322 }, /* Tcaron */ + { 0x000001bb, 24329 }, /* tcaron */ + { 0x000001de, 24336 }, /* Tcedilla */ + { 0x000001fe, 24345 }, /* tcedilla */ + { 0x00000af9, 24354 }, /* telephone */ + { 0x00000afa, 24364 }, /* telephonerecorder */ + { 0x0000fed5, 24382 }, /* Terminate_Server */ + { 0x00000ddf, 24399 }, /* Thai_baht */ + { 0x00000dba, 24409 }, /* Thai_bobaimai */ + { 0x00000da8, 24423 }, /* Thai_chochan */ + { 0x00000daa, 24436 }, /* Thai_chochang */ + { 0x00000da9, 24450 }, /* Thai_choching */ + { 0x00000dac, 24464 }, /* Thai_chochoe */ + { 0x00000dae, 24477 }, /* Thai_dochada */ + { 0x00000db4, 24490 }, /* Thai_dodek */ + { 0x00000dbd, 24501 }, /* Thai_fofa */ + { 0x00000dbf, 24511 }, /* Thai_fofan */ + { 0x00000dcb, 24522 }, /* Thai_hohip */ + { 0x00000dce, 24533 }, /* Thai_honokhuk */ + { 0x00000da2, 24547 }, /* Thai_khokhai */ + { 0x00000da5, 24560 }, /* Thai_khokhon */ + { 0x00000da3, 24573 }, /* Thai_khokhuat */ + { 0x00000da4, 24587 }, /* Thai_khokhwai */ + { 0x00000da6, 24601 }, /* Thai_khorakhang */ + { 0x00000da1, 24617 }, /* Thai_kokai */ + { 0x00000de5, 24628 }, /* Thai_lakkhangyao */ + { 0x00000df7, 24645 }, /* Thai_lekchet */ + { 0x00000df5, 24658 }, /* Thai_lekha */ + { 0x00000df6, 24669 }, /* Thai_lekhok */ + { 0x00000df9, 24681 }, /* Thai_lekkao */ + { 0x00000df1, 24693 }, /* Thai_leknung */ + { 0x00000df8, 24706 }, /* Thai_lekpaet */ + { 0x00000df3, 24719 }, /* Thai_leksam */ + { 0x00000df4, 24731 }, /* Thai_leksi */ + { 0x00000df2, 24742 }, /* Thai_leksong */ + { 0x00000df0, 24755 }, /* Thai_leksun */ + { 0x00000dcc, 24767 }, /* Thai_lochula */ + { 0x00000dc5, 24780 }, /* Thai_loling */ + { 0x00000dc6, 24792 }, /* Thai_lu */ + { 0x00000deb, 24800 }, /* Thai_maichattawa */ + { 0x00000de8, 24817 }, /* Thai_maiek */ + { 0x00000dd1, 24828 }, /* Thai_maihanakat */ + { 0x00000dde, 24844 }, /* Thai_maihanakat_maitho */ + { 0x00000de7, 24867 }, /* Thai_maitaikhu */ + { 0x00000de9, 24882 }, /* Thai_maitho */ + { 0x00000dea, 24894 }, /* Thai_maitri */ + { 0x00000de6, 24906 }, /* Thai_maiyamok */ + { 0x00000dc1, 24920 }, /* Thai_moma */ + { 0x00000da7, 24930 }, /* Thai_ngongu */ + { 0x00000ded, 24942 }, /* Thai_nikhahit */ + { 0x00000db3, 24956 }, /* Thai_nonen */ + { 0x00000db9, 24967 }, /* Thai_nonu */ + { 0x00000dcd, 24977 }, /* Thai_oang */ + { 0x00000dcf, 24987 }, /* Thai_paiyannoi */ + { 0x00000dda, 25002 }, /* Thai_phinthu */ + { 0x00000dbe, 25015 }, /* Thai_phophan */ + { 0x00000dbc, 25028 }, /* Thai_phophung */ + { 0x00000dc0, 25042 }, /* Thai_phosamphao */ + { 0x00000dbb, 25058 }, /* Thai_popla */ + { 0x00000dc3, 25069 }, /* Thai_rorua */ + { 0x00000dc4, 25080 }, /* Thai_ru */ + { 0x00000dd0, 25088 }, /* Thai_saraa */ + { 0x00000dd2, 25099 }, /* Thai_saraaa */ + { 0x00000de1, 25111 }, /* Thai_saraae */ + { 0x00000de4, 25123 }, /* Thai_saraaimaimalai */ + { 0x00000de3, 25143 }, /* Thai_saraaimaimuan */ + { 0x00000dd3, 25162 }, /* Thai_saraam */ + { 0x00000de0, 25174 }, /* Thai_sarae */ + { 0x00000dd4, 25185 }, /* Thai_sarai */ + { 0x00000dd5, 25196 }, /* Thai_saraii */ + { 0x00000de2, 25208 }, /* Thai_sarao */ + { 0x00000dd8, 25219 }, /* Thai_sarau */ + { 0x00000dd6, 25230 }, /* Thai_saraue */ + { 0x00000dd7, 25242 }, /* Thai_sarauee */ + { 0x00000dd9, 25255 }, /* Thai_sarauu */ + { 0x00000dc9, 25267 }, /* Thai_sorusi */ + { 0x00000dc8, 25279 }, /* Thai_sosala */ + { 0x00000dab, 25291 }, /* Thai_soso */ + { 0x00000dca, 25301 }, /* Thai_sosua */ + { 0x00000dec, 25312 }, /* Thai_thanthakhat */ + { 0x00000db1, 25329 }, /* Thai_thonangmontho */ + { 0x00000db2, 25348 }, /* Thai_thophuthao */ + { 0x00000db7, 25364 }, /* Thai_thothahan */ + { 0x00000db0, 25379 }, /* Thai_thothan */ + { 0x00000db8, 25392 }, /* Thai_thothong */ + { 0x00000db6, 25406 }, /* Thai_thothung */ + { 0x00000daf, 25420 }, /* Thai_topatak */ + { 0x00000db5, 25433 }, /* Thai_totao */ + { 0x00000dc7, 25444 }, /* Thai_wowaen */ + { 0x00000dc2, 25456 }, /* Thai_yoyak */ + { 0x00000dad, 25467 }, /* Thai_yoying */ + { 0x000008c0, 25479 }, /* therefore */ + { 0x00000aa7, 25489 }, /* thinspace */ + { 0x000000de, 25499 }, /* THORN */ + { 0x000000de, 25505 }, /* Thorn */ + { 0x000000fe, 25511 }, /* thorn */ + { 0x00000ac4, 25517 }, /* threeeighths */ + { 0x00000ab4, 25530 }, /* threefifths */ + { 0x000000be, 25542 }, /* threequarters */ + { 0x01002083, 25556 }, /* threesubscript */ + { 0x000000b3, 25571 }, /* threesuperior */ + { 0x0100222d, 25585 }, /* tintegral */ + { 0x000008a4, 25595 }, /* topintegral */ + { 0x000008ab, 25607 }, /* topleftparens */ + { 0x000008a2, 25621 }, /* topleftradical */ + { 0x000008a7, 25636 }, /* topleftsqbracket */ + { 0x000008b1, 25653 }, /* topleftsummation */ + { 0x000008ad, 25670 }, /* toprightparens */ + { 0x000008a9, 25685 }, /* toprightsqbracket */ + { 0x000008b5, 25703 }, /* toprightsummation */ + { 0x000009f7, 25721 }, /* topt */ + { 0x000008b3, 25726 }, /* topvertsummationconnector */ + { 0x0000ff2b, 25752 }, /* Touroku */ + { 0x00000ac9, 25760 }, /* trademark */ + { 0x00000acb, 25770 }, /* trademarkincircle */ + { 0x000003ac, 25788 }, /* Tslash */ + { 0x000003bc, 25795 }, /* tslash */ + { 0x00000ab3, 25802 }, /* twofifths */ + { 0x01002082, 25812 }, /* twosubscript */ + { 0x000000b2, 25825 }, /* twosuperior */ + { 0x00000ab1, 25837 }, /* twothirds */ + { 0x00000055, 25847 }, /* U */ + { 0x00000075, 25849 }, /* u */ + { 0x000000da, 25851 }, /* Uacute */ + { 0x000000fa, 25858 }, /* uacute */ + { 0x01001ee4, 25865 }, /* Ubelowdot */ + { 0x01001ee5, 25875 }, /* ubelowdot */ + { 0x000002dd, 25885 }, /* Ubreve */ + { 0x000002fd, 25892 }, /* ubreve */ + { 0x000000db, 25899 }, /* Ucircumflex */ + { 0x000000fb, 25911 }, /* ucircumflex */ + { 0x000000dc, 25923 }, /* Udiaeresis */ + { 0x000000fc, 25934 }, /* udiaeresis */ + { 0x000001db, 25945 }, /* Udoubleacute */ + { 0x000001fb, 25958 }, /* udoubleacute */ + { 0x000000d9, 25971 }, /* Ugrave */ + { 0x000000f9, 25978 }, /* ugrave */ + { 0x01001ee6, 25985 }, /* Uhook */ + { 0x01001ee7, 25991 }, /* uhook */ + { 0x010001af, 25997 }, /* Uhorn */ + { 0x010001b0, 26003 }, /* uhorn */ + { 0x01001ee8, 26009 }, /* Uhornacute */ + { 0x01001ee9, 26020 }, /* uhornacute */ + { 0x01001ef0, 26031 }, /* Uhornbelowdot */ + { 0x01001ef1, 26045 }, /* uhornbelowdot */ + { 0x01001eea, 26059 }, /* Uhorngrave */ + { 0x01001eeb, 26070 }, /* uhorngrave */ + { 0x01001eec, 26081 }, /* Uhornhook */ + { 0x01001eed, 26091 }, /* uhornhook */ + { 0x01001eee, 26101 }, /* Uhorntilde */ + { 0x01001eef, 26112 }, /* uhorntilde */ + { 0x000006ad, 26123 }, /* Ukrainian_ghe_with_upturn */ + { 0x000006bd, 26149 }, /* Ukrainian_GHE_WITH_UPTURN */ + { 0x000006a6, 26175 }, /* Ukrainian_i */ + { 0x000006b6, 26187 }, /* Ukrainian_I */ + { 0x000006a4, 26199 }, /* Ukrainian_ie */ + { 0x000006b4, 26212 }, /* Ukrainian_IE */ + { 0x000006a7, 26225 }, /* Ukrainian_yi */ + { 0x000006b7, 26238 }, /* Ukrainian_YI */ + { 0x000006a6, 26251 }, /* Ukranian_i */ + { 0x000006b6, 26262 }, /* Ukranian_I */ + { 0x000006a4, 26273 }, /* Ukranian_je */ + { 0x000006b4, 26285 }, /* Ukranian_JE */ + { 0x000006a7, 26297 }, /* Ukranian_yi */ + { 0x000006b7, 26309 }, /* Ukranian_YI */ + { 0x000003de, 26321 }, /* Umacron */ + { 0x000003fe, 26329 }, /* umacron */ + { 0x00000bc6, 26337 }, /* underbar */ + { 0x0000005f, 26346 }, /* underscore */ + { 0x0000ff65, 26357 }, /* Undo */ + { 0x000008dd, 26362 }, /* union */ + { 0x000003d9, 26368 }, /* Uogonek */ + { 0x000003f9, 26376 }, /* uogonek */ + { 0x0000ff52, 26384 }, /* Up */ + { 0x000008fc, 26387 }, /* uparrow */ + { 0x00000ba9, 26395 }, /* upcaret */ + { 0x000009ec, 26403 }, /* upleftcorner */ + { 0x000009eb, 26416 }, /* uprightcorner */ + { 0x00000bc3, 26430 }, /* upshoe */ + { 0x00000bd3, 26437 }, /* upstile */ + { 0x00000bce, 26445 }, /* uptack */ + { 0x000001d9, 26452 }, /* Uring */ + { 0x000001f9, 26458 }, /* uring */ + { 0x1000ff6e, 26464 }, /* User */ + { 0x000003dd, 26469 }, /* Utilde */ + { 0x000003fd, 26476 }, /* utilde */ + { 0x00000056, 26483 }, /* V */ + { 0x00000076, 26485 }, /* v */ + { 0x000008c1, 26487 }, /* variation */ + { 0x000009f8, 26497 }, /* vertbar */ + { 0x000008a6, 26505 }, /* vertconnector */ + { 0x000004de, 26519 }, /* voicedsound */ + { 0x00ffffff, 26531 }, /* VoidSymbol */ + { 0x000009e9, 26542 }, /* vt */ + { 0x00000057, 26545 }, /* W */ + { 0x00000077, 26547 }, /* w */ + { 0x01001e82, 26549 }, /* Wacute */ + { 0x01001e83, 26556 }, /* wacute */ + { 0x01000174, 26563 }, /* Wcircumflex */ + { 0x01000175, 26575 }, /* wcircumflex */ + { 0x01001e84, 26587 }, /* Wdiaeresis */ + { 0x01001e85, 26598 }, /* wdiaeresis */ + { 0x01001e80, 26609 }, /* Wgrave */ + { 0x01001e81, 26616 }, /* wgrave */ + { 0x010020a9, 26623 }, /* WonSign */ + { 0x00000058, 26631 }, /* X */ + { 0x00000078, 26633 }, /* x */ + { 0x01001e8a, 26635 }, /* Xabovedot */ + { 0x01001e8b, 26645 }, /* xabovedot */ + { 0x1008ff39, 26655 }, /* XF86AddFavorite */ + { 0x1008ff50, 26671 }, /* XF86ApplicationLeft */ + { 0x1008ff51, 26691 }, /* XF86ApplicationRight */ + { 0x1008ff9b, 26712 }, /* XF86AudioCycleTrack */ + { 0x1008ff97, 26732 }, /* XF86AudioForward */ + { 0x1008ff11, 26749 }, /* XF86AudioLowerVolume */ + { 0x1008ff32, 26770 }, /* XF86AudioMedia */ + { 0x1008ffb2, 26785 }, /* XF86AudioMicMute */ + { 0x1008ff12, 26802 }, /* XF86AudioMute */ + { 0x1008ff17, 26816 }, /* XF86AudioNext */ + { 0x1008ff31, 26830 }, /* XF86AudioPause */ + { 0x1008ff14, 26845 }, /* XF86AudioPlay */ + { 0x1008ffb6, 26859 }, /* XF86AudioPreset */ + { 0x1008ff16, 26875 }, /* XF86AudioPrev */ + { 0x1008ff13, 26889 }, /* XF86AudioRaiseVolume */ + { 0x1008ff99, 26910 }, /* XF86AudioRandomPlay */ + { 0x1008ff1c, 26930 }, /* XF86AudioRecord */ + { 0x1008ff98, 26946 }, /* XF86AudioRepeat */ + { 0x1008ff3e, 26962 }, /* XF86AudioRewind */ + { 0x1008ff15, 26978 }, /* XF86AudioStop */ + { 0x1008ff8d, 26992 }, /* XF86Away */ + { 0x1008ff26, 27001 }, /* XF86Back */ + { 0x1008ff3f, 27010 }, /* XF86BackForward */ + { 0x1008ff93, 27026 }, /* XF86Battery */ + { 0x1008ffa6, 27038 }, /* XF86Blue */ + { 0x1008ff94, 27047 }, /* XF86Bluetooth */ + { 0x1008ff52, 27061 }, /* XF86Book */ + { 0x1008ff3b, 27070 }, /* XF86BrightnessAdjust */ + { 0x1008ff54, 27091 }, /* XF86Calculater */ + { 0x1008ff1d, 27106 }, /* XF86Calculator */ + { 0x1008ff20, 27121 }, /* XF86Calendar */ + { 0x1008ff53, 27134 }, /* XF86CD */ + { 0x1008ff55, 27141 }, /* XF86Clear */ + { 0x1008fe21, 27151 }, /* XF86ClearGrab */ + { 0x1008ff56, 27165 }, /* XF86Close */ + { 0x1008ff3d, 27175 }, /* XF86Community */ + { 0x1008ff22, 27189 }, /* XF86ContrastAdjust */ + { 0x1008ff57, 27208 }, /* XF86Copy */ + { 0x1008ff58, 27217 }, /* XF86Cut */ + { 0x1008ff9c, 27225 }, /* XF86CycleAngle */ + { 0x1008ff59, 27240 }, /* XF86Display */ + { 0x1008ff5b, 27252 }, /* XF86Documents */ + { 0x1008ff5a, 27266 }, /* XF86DOS */ + { 0x1008ff2c, 27274 }, /* XF86Eject */ + { 0x1008ff5c, 27284 }, /* XF86Excel */ + { 0x1008ff5d, 27294 }, /* XF86Explorer */ + { 0x1008ff30, 27307 }, /* XF86Favorites */ + { 0x1008ff3c, 27321 }, /* XF86Finance */ + { 0x1008ff27, 27333 }, /* XF86Forward */ + { 0x1008ff9d, 27345 }, /* XF86FrameBack */ + { 0x1008ff9e, 27359 }, /* XF86FrameForward */ + { 0x1008ffb8, 27376 }, /* XF86FullScreen */ + { 0x1008ff5e, 27391 }, /* XF86Game */ + { 0x1008ff5f, 27400 }, /* XF86Go */ + { 0x1008ffa4, 27407 }, /* XF86Green */ + { 0x1008ffa8, 27417 }, /* XF86Hibernate */ + { 0x1008ff37, 27431 }, /* XF86History */ + { 0x1008ff18, 27443 }, /* XF86HomePage */ + { 0x1008ff3a, 27456 }, /* XF86HotLinks */ + { 0x1008ff60, 27469 }, /* XF86iTouch */ + { 0x1008ff06, 27480 }, /* XF86KbdBrightnessDown */ + { 0x1008ff05, 27502 }, /* XF86KbdBrightnessUp */ + { 0x1008ff04, 27522 }, /* XF86KbdLightOnOff */ + { 0x1008ffb3, 27540 }, /* XF86Keyboard */ + { 0x1008ff40, 27553 }, /* XF86Launch0 */ + { 0x1008ff41, 27565 }, /* XF86Launch1 */ + { 0x1008ff42, 27577 }, /* XF86Launch2 */ + { 0x1008ff43, 27589 }, /* XF86Launch3 */ + { 0x1008ff44, 27601 }, /* XF86Launch4 */ + { 0x1008ff45, 27613 }, /* XF86Launch5 */ + { 0x1008ff46, 27625 }, /* XF86Launch6 */ + { 0x1008ff47, 27637 }, /* XF86Launch7 */ + { 0x1008ff48, 27649 }, /* XF86Launch8 */ + { 0x1008ff49, 27661 }, /* XF86Launch9 */ + { 0x1008ff4a, 27673 }, /* XF86LaunchA */ + { 0x1008ff4b, 27685 }, /* XF86LaunchB */ + { 0x1008ff4c, 27697 }, /* XF86LaunchC */ + { 0x1008ff4d, 27709 }, /* XF86LaunchD */ + { 0x1008ff4e, 27721 }, /* XF86LaunchE */ + { 0x1008ff4f, 27733 }, /* XF86LaunchF */ + { 0x1008ff35, 27745 }, /* XF86LightBulb */ + { 0x1008fe25, 27759 }, /* XF86LogGrabInfo */ + { 0x1008ff61, 27775 }, /* XF86LogOff */ + { 0x1008fe24, 27786 }, /* XF86LogWindowTree */ + { 0x1008ff19, 27804 }, /* XF86Mail */ + { 0x1008ff90, 27813 }, /* XF86MailForward */ + { 0x1008ff62, 27829 }, /* XF86Market */ + { 0x1008ff63, 27840 }, /* XF86Meeting */ + { 0x1008ff1e, 27852 }, /* XF86Memo */ + { 0x1008ff65, 27861 }, /* XF86MenuKB */ + { 0x1008ff66, 27872 }, /* XF86MenuPB */ + { 0x1008ff8e, 27883 }, /* XF86Messenger */ + { 0x1008ff01, 27897 }, /* XF86ModeLock */ + { 0x1008ff07, 27910 }, /* XF86MonBrightnessCycle */ + { 0x1008ff03, 27933 }, /* XF86MonBrightnessDown */ + { 0x1008ff02, 27955 }, /* XF86MonBrightnessUp */ + { 0x1008ff92, 27975 }, /* XF86Music */ + { 0x1008ff33, 27985 }, /* XF86MyComputer */ + { 0x1008ff67, 28000 }, /* XF86MySites */ + { 0x1008ff68, 28012 }, /* XF86New */ + { 0x1008ff69, 28020 }, /* XF86News */ + { 0x1008fe22, 28029 }, /* XF86Next_VMode */ + { 0x1008ff6a, 28044 }, /* XF86OfficeHome */ + { 0x1008ff6b, 28059 }, /* XF86Open */ + { 0x1008ff38, 28068 }, /* XF86OpenURL */ + { 0x1008ff6c, 28080 }, /* XF86Option */ + { 0x1008ff6d, 28091 }, /* XF86Paste */ + { 0x1008ff6e, 28101 }, /* XF86Phone */ + { 0x1008ff91, 28111 }, /* XF86Pictures */ + { 0x1008ff21, 28124 }, /* XF86PowerDown */ + { 0x1008ff2a, 28138 }, /* XF86PowerOff */ + { 0x1008fe23, 28151 }, /* XF86Prev_VMode */ + { 0x1008ff70, 28166 }, /* XF86Q */ + { 0x1008ffa3, 28172 }, /* XF86Red */ + { 0x1008ff29, 28180 }, /* XF86Refresh */ + { 0x1008ff73, 28192 }, /* XF86Reload */ + { 0x1008ff72, 28203 }, /* XF86Reply */ + { 0x1008ffb5, 28213 }, /* XF86RFKill */ + { 0x1008ff24, 28224 }, /* XF86RockerDown */ + { 0x1008ff25, 28239 }, /* XF86RockerEnter */ + { 0x1008ff23, 28255 }, /* XF86RockerUp */ + { 0x1008ff74, 28268 }, /* XF86RotateWindows */ + { 0x1008ff76, 28286 }, /* XF86RotationKB */ + { 0x1008ffb7, 28301 }, /* XF86RotationLockToggle */ + { 0x1008ff75, 28324 }, /* XF86RotationPB */ + { 0x1008ff77, 28339 }, /* XF86Save */ + { 0x1008ff2d, 28348 }, /* XF86ScreenSaver */ + { 0x1008ff7a, 28364 }, /* XF86ScrollClick */ + { 0x1008ff79, 28380 }, /* XF86ScrollDown */ + { 0x1008ff78, 28395 }, /* XF86ScrollUp */ + { 0x1008ff1b, 28408 }, /* XF86Search */ + { 0x1008ffa0, 28419 }, /* XF86Select */ + { 0x1008ff7b, 28430 }, /* XF86Send */ + { 0x1008ff36, 28439 }, /* XF86Shop */ + { 0x1008ff2f, 28448 }, /* XF86Sleep */ + { 0x1008ff7c, 28458 }, /* XF86Spell */ + { 0x1008ff7d, 28468 }, /* XF86SplitScreen */ + { 0x1008ff10, 28484 }, /* XF86Standby */ + { 0x1008ff1a, 28496 }, /* XF86Start */ + { 0x1008ff28, 28506 }, /* XF86Stop */ + { 0x1008ff9a, 28515 }, /* XF86Subtitle */ + { 0x1008ff7e, 28528 }, /* XF86Support */ + { 0x1008ffa7, 28540 }, /* XF86Suspend */ + { 0x1008fe01, 28552 }, /* XF86Switch_VT_1 */ + { 0x1008fe0a, 28568 }, /* XF86Switch_VT_10 */ + { 0x1008fe0b, 28585 }, /* XF86Switch_VT_11 */ + { 0x1008fe0c, 28602 }, /* XF86Switch_VT_12 */ + { 0x1008fe02, 28619 }, /* XF86Switch_VT_2 */ + { 0x1008fe03, 28635 }, /* XF86Switch_VT_3 */ + { 0x1008fe04, 28651 }, /* XF86Switch_VT_4 */ + { 0x1008fe05, 28667 }, /* XF86Switch_VT_5 */ + { 0x1008fe06, 28683 }, /* XF86Switch_VT_6 */ + { 0x1008fe07, 28699 }, /* XF86Switch_VT_7 */ + { 0x1008fe08, 28715 }, /* XF86Switch_VT_8 */ + { 0x1008fe09, 28731 }, /* XF86Switch_VT_9 */ + { 0x1008ff7f, 28747 }, /* XF86TaskPane */ + { 0x1008ff80, 28760 }, /* XF86Terminal */ + { 0x1008ff9f, 28773 }, /* XF86Time */ + { 0x1008ff1f, 28782 }, /* XF86ToDoList */ + { 0x1008ff81, 28795 }, /* XF86Tools */ + { 0x1008ffa2, 28805 }, /* XF86TopMenu */ + { 0x1008ffb1, 28817 }, /* XF86TouchpadOff */ + { 0x1008ffb0, 28833 }, /* XF86TouchpadOn */ + { 0x1008ffa9, 28848 }, /* XF86TouchpadToggle */ + { 0x1008ff82, 28867 }, /* XF86Travel */ + { 0x1008fe20, 28878 }, /* XF86Ungrab */ + { 0x1008ff85, 28889 }, /* XF86User1KB */ + { 0x1008ff86, 28901 }, /* XF86User2KB */ + { 0x1008ff84, 28913 }, /* XF86UserPB */ + { 0x1008ff96, 28924 }, /* XF86UWB */ + { 0x1008ff34, 28932 }, /* XF86VendorHome */ + { 0x1008ff87, 28947 }, /* XF86Video */ + { 0x1008ffa1, 28957 }, /* XF86View */ + { 0x1008ff2b, 28966 }, /* XF86WakeUp */ + { 0x1008ff8f, 28977 }, /* XF86WebCam */ + { 0x1008ff88, 28988 }, /* XF86WheelButton */ + { 0x1008ff95, 29004 }, /* XF86WLAN */ + { 0x1008ff89, 29013 }, /* XF86Word */ + { 0x1008ffb4, 29022 }, /* XF86WWAN */ + { 0x1008ff2e, 29031 }, /* XF86WWW */ + { 0x1008ff8a, 29039 }, /* XF86Xfer */ + { 0x1008ffa5, 29048 }, /* XF86Yellow */ + { 0x1008ff8b, 29059 }, /* XF86ZoomIn */ + { 0x1008ff8c, 29070 }, /* XF86ZoomOut */ + { 0x00000059, 29082 }, /* Y */ + { 0x00000079, 29084 }, /* y */ + { 0x000000dd, 29086 }, /* Yacute */ + { 0x000000fd, 29093 }, /* yacute */ + { 0x01001ef4, 29100 }, /* Ybelowdot */ + { 0x01001ef5, 29110 }, /* ybelowdot */ + { 0x01000176, 29120 }, /* Ycircumflex */ + { 0x01000177, 29132 }, /* ycircumflex */ + { 0x000000ff, 29144 }, /* ydiaeresis */ + { 0x000013be, 29155 }, /* Ydiaeresis */ + { 0x000000a5, 29166 }, /* yen */ + { 0x01001ef2, 29170 }, /* Ygrave */ + { 0x01001ef3, 29177 }, /* ygrave */ + { 0x01001ef6, 29184 }, /* Yhook */ + { 0x01001ef7, 29190 }, /* yhook */ + { 0x01001ef8, 29196 }, /* Ytilde */ + { 0x01001ef9, 29203 }, /* ytilde */ + { 0x0000005a, 29210 }, /* Z */ + { 0x0000007a, 29212 }, /* z */ + { 0x000001af, 29214 }, /* Zabovedot */ + { 0x000001bf, 29224 }, /* zabovedot */ + { 0x000001ac, 29234 }, /* Zacute */ + { 0x000001bc, 29241 }, /* zacute */ + { 0x000001ae, 29248 }, /* Zcaron */ + { 0x000001be, 29255 }, /* zcaron */ + { 0x0000ff3d, 29262 }, /* Zen_Koho */ + { 0x0000ff28, 29271 }, /* Zenkaku */ + { 0x0000ff2a, 29279 }, /* Zenkaku_Hankaku */ + { 0x01002080, 29295 }, /* zerosubscript */ + { 0x01002070, 29309 }, /* zerosuperior */ + { 0x010001b5, 29322 }, /* Zstroke */ + { 0x010001b6, 29330 }, /* zstroke */ +}; + +static const struct name_keysym keysym_to_name[] = { + { 0x00000000, 20091 }, /* NoSymbol */ + { 0x00000020, 23803 }, /* space */ + { 0x00000021, 12099 }, /* exclam */ + { 0x00000022, 22187 }, /* quotedbl */ + { 0x00000023, 20178 }, /* numbersign */ + { 0x00000024, 11251 }, /* dollar */ + { 0x00000025, 21487 }, /* percent */ + { 0x00000026, 908 }, /* ampersand */ + { 0x00000027, 934 }, /* apostrophe */ + { 0x00000028, 21425 }, /* parenleft */ + { 0x00000029, 21435 }, /* parenright */ + { 0x0000002a, 3283 }, /* asterisk */ + { 0x0000002b, 21557 }, /* plus */ + { 0x0000002c, 8680 }, /* comma */ + { 0x0000002d, 19733 }, /* minus */ + { 0x0000002e, 21495 }, /* period */ + { 0x0000002f, 23768 }, /* slash */ + { 0x00000030, 0 }, /* 0 */ + { 0x00000031, 2 }, /* 1 */ + { 0x00000032, 4 }, /* 2 */ + { 0x00000033, 6 }, /* 3 */ + { 0x00000034, 386 }, /* 4 */ + { 0x00000035, 388 }, /* 5 */ + { 0x00000036, 390 }, /* 6 */ + { 0x00000037, 392 }, /* 7 */ + { 0x00000038, 394 }, /* 8 */ + { 0x00000039, 396 }, /* 9 */ + { 0x0000003a, 8664 }, /* colon */ + { 0x0000003b, 22734 }, /* semicolon */ + { 0x0000003c, 19412 }, /* less */ + { 0x0000003d, 12051 }, /* equal */ + { 0x0000003e, 13272 }, /* greater */ + { 0x0000003f, 22165 }, /* question */ + { 0x00000040, 3292 }, /* at */ + { 0x00000041, 398 }, /* A */ + { 0x00000042, 3328 }, /* B */ + { 0x00000043, 8439 }, /* C */ + { 0x00000044, 10320 }, /* D */ + { 0x00000045, 11424 }, /* E */ + { 0x00000046, 12155 }, /* F */ + { 0x00000047, 12665 }, /* G */ + { 0x00000048, 14378 }, /* H */ + { 0x00000049, 17069 }, /* I */ + { 0x0000004a, 18114 }, /* J */ + { 0x0000004b, 18146 }, /* K */ + { 0x0000004c, 19112 }, /* L */ + { 0x0000004d, 19533 }, /* M */ + { 0x0000004e, 19943 }, /* N */ + { 0x0000004f, 20200 }, /* O */ + { 0x00000050, 21373 }, /* P */ + { 0x00000051, 22156 }, /* Q */ + { 0x00000052, 22217 }, /* R */ + { 0x00000053, 22579 }, /* S */ + { 0x00000054, 24294 }, /* T */ + { 0x00000055, 25847 }, /* U */ + { 0x00000056, 26483 }, /* V */ + { 0x00000057, 26545 }, /* W */ + { 0x00000058, 26631 }, /* X */ + { 0x00000059, 29082 }, /* Y */ + { 0x0000005a, 29210 }, /* Z */ + { 0x0000005b, 3603 }, /* bracketleft */ + { 0x0000005c, 3352 }, /* backslash */ + { 0x0000005d, 3615 }, /* bracketright */ + { 0x0000005e, 3260 }, /* asciicircum */ + { 0x0000005f, 26346 }, /* underscore */ + { 0x00000060, 13266 }, /* grave */ + { 0x00000061, 400 }, /* a */ + { 0x00000062, 3330 }, /* b */ + { 0x00000063, 8441 }, /* c */ + { 0x00000064, 10322 }, /* d */ + { 0x00000065, 11426 }, /* e */ + { 0x00000066, 12157 }, /* f */ + { 0x00000067, 12667 }, /* g */ + { 0x00000068, 14380 }, /* h */ + { 0x00000069, 17071 }, /* i */ + { 0x0000006a, 18116 }, /* j */ + { 0x0000006b, 18148 }, /* k */ + { 0x0000006c, 19114 }, /* l */ + { 0x0000006d, 19535 }, /* m */ + { 0x0000006e, 19945 }, /* n */ + { 0x0000006f, 20202 }, /* o */ + { 0x00000070, 21375 }, /* p */ + { 0x00000071, 22158 }, /* q */ + { 0x00000072, 22219 }, /* r */ + { 0x00000073, 22581 }, /* s */ + { 0x00000074, 24296 }, /* t */ + { 0x00000075, 25849 }, /* u */ + { 0x00000076, 26485 }, /* v */ + { 0x00000077, 26547 }, /* w */ + { 0x00000078, 26633 }, /* x */ + { 0x00000079, 29084 }, /* y */ + { 0x0000007a, 29212 }, /* z */ + { 0x0000007b, 3582 }, /* braceleft */ + { 0x0000007c, 3392 }, /* bar */ + { 0x0000007d, 3592 }, /* braceright */ + { 0x0000007e, 3272 }, /* asciitilde */ + { 0x000000a0, 20078 }, /* nobreakspace */ + { 0x000000a1, 12106 }, /* exclamdown */ + { 0x000000a2, 8589 }, /* cent */ + { 0x000000a3, 23827 }, /* sterling */ + { 0x000000a4, 8766 }, /* currency */ + { 0x000000a5, 29166 }, /* yen */ + { 0x000000a6, 8389 }, /* brokenbar */ + { 0x000000a7, 22719 }, /* section */ + { 0x000000a8, 11203 }, /* diaeresis */ + { 0x000000a9, 8717 }, /* copyright */ + { 0x000000aa, 20852 }, /* ordfeminine */ + { 0x000000ab, 14349 }, /* guillemotleft */ + { 0x000000ac, 20147 }, /* notsign */ + { 0x000000ad, 17062 }, /* hyphen */ + { 0x000000ae, 22331 }, /* registered */ + { 0x000000af, 19641 }, /* macron */ + { 0x000000b0, 11153 }, /* degree */ + { 0x000000b1, 21562 }, /* plusminus */ + { 0x000000b2, 25825 }, /* twosuperior */ + { 0x000000b3, 25571 }, /* threesuperior */ + { 0x000000b4, 820 }, /* acute */ + { 0x000000b5, 19799 }, /* mu */ + { 0x000000b6, 21415 }, /* paragraph */ + { 0x000000b7, 21502 }, /* periodcentered */ + { 0x000000b8, 8581 }, /* cedilla */ + { 0x000000b9, 20755 }, /* onesuperior */ + { 0x000000ba, 19688 }, /* masculine */ + { 0x000000bb, 14363 }, /* guillemotright */ + { 0x000000bc, 20722 }, /* onequarter */ + { 0x000000bd, 20714 }, /* onehalf */ + { 0x000000be, 25542 }, /* threequarters */ + { 0x000000bf, 22174 }, /* questiondown */ + { 0x000000c0, 854 }, /* Agrave */ + { 0x000000c1, 402 }, /* Aacute */ + { 0x000000c2, 622 }, /* Acircumflex */ + { 0x000000c3, 3295 }, /* Atilde */ + { 0x000000c4, 826 }, /* Adiaeresis */ + { 0x000000c5, 2036 }, /* Aring */ + { 0x000000c6, 848 }, /* AE */ + { 0x000000c7, 8539 }, /* Ccedilla */ + { 0x000000c8, 11724 }, /* Egrave */ + { 0x000000c9, 11448 }, /* Eacute */ + { 0x000000ca, 11496 }, /* Ecircumflex */ + { 0x000000cb, 11702 }, /* Ediaeresis */ + { 0x000000cc, 17205 }, /* Igrave */ + { 0x000000cd, 17083 }, /* Iacute */ + { 0x000000ce, 17131 }, /* Icircumflex */ + { 0x000000cf, 17165 }, /* Idiaeresis */ + { 0x000000d0, 12064 }, /* ETH */ + { 0x000000d1, 20155 }, /* Ntilde */ + { 0x000000d2, 20527 }, /* Ograve */ + { 0x000000d3, 20204 }, /* Oacute */ + { 0x000000d4, 20268 }, /* Ocircumflex */ + { 0x000000d5, 21310 }, /* Otilde */ + { 0x000000d6, 20466 }, /* Odiaeresis */ + { 0x000000d7, 19839 }, /* multiply */ + { 0x000000d8, 21296 }, /* Oslash */ + { 0x000000d9, 25971 }, /* Ugrave */ + { 0x000000da, 25851 }, /* Uacute */ + { 0x000000db, 25899 }, /* Ucircumflex */ + { 0x000000dc, 25923 }, /* Udiaeresis */ + { 0x000000dd, 29086 }, /* Yacute */ + { 0x000000de, 25499 }, /* THORN */ + { 0x000000df, 23820 }, /* ssharp */ + { 0x000000e0, 861 }, /* agrave */ + { 0x000000e1, 409 }, /* aacute */ + { 0x000000e2, 634 }, /* acircumflex */ + { 0x000000e3, 3302 }, /* atilde */ + { 0x000000e4, 837 }, /* adiaeresis */ + { 0x000000e5, 2042 }, /* aring */ + { 0x000000e6, 851 }, /* ae */ + { 0x000000e7, 8548 }, /* ccedilla */ + { 0x000000e8, 11731 }, /* egrave */ + { 0x000000e9, 11455 }, /* eacute */ + { 0x000000ea, 11508 }, /* ecircumflex */ + { 0x000000eb, 11713 }, /* ediaeresis */ + { 0x000000ec, 17212 }, /* igrave */ + { 0x000000ed, 17090 }, /* iacute */ + { 0x000000ee, 17143 }, /* icircumflex */ + { 0x000000ef, 17176 }, /* idiaeresis */ + { 0x000000f0, 12072 }, /* eth */ + { 0x000000f1, 20162 }, /* ntilde */ + { 0x000000f2, 20534 }, /* ograve */ + { 0x000000f3, 20211 }, /* oacute */ + { 0x000000f4, 20280 }, /* ocircumflex */ + { 0x000000f5, 21317 }, /* otilde */ + { 0x000000f6, 20477 }, /* odiaeresis */ + { 0x000000f7, 11242 }, /* division */ + { 0x000000f8, 21303 }, /* oslash */ + { 0x000000f9, 25978 }, /* ugrave */ + { 0x000000fa, 25858 }, /* uacute */ + { 0x000000fb, 25911 }, /* ucircumflex */ + { 0x000000fc, 25934 }, /* udiaeresis */ + { 0x000000fd, 29093 }, /* yacute */ + { 0x000000fe, 25511 }, /* thorn */ + { 0x000000ff, 29144 }, /* ydiaeresis */ + { 0x000001a1, 918 }, /* Aogonek */ + { 0x000001a2, 8383 }, /* breve */ + { 0x000001a3, 19517 }, /* Lstroke */ + { 0x000001a5, 19212 }, /* Lcaron */ + { 0x000001a6, 22603 }, /* Sacute */ + { 0x000001a9, 22617 }, /* Scaron */ + { 0x000001aa, 22631 }, /* Scedilla */ + { 0x000001ab, 24322 }, /* Tcaron */ + { 0x000001ac, 29234 }, /* Zacute */ + { 0x000001ae, 29248 }, /* Zcaron */ + { 0x000001af, 29214 }, /* Zabovedot */ + { 0x000001b1, 926 }, /* aogonek */ + { 0x000001b2, 20520 }, /* ogonek */ + { 0x000001b3, 19525 }, /* lstroke */ + { 0x000001b5, 19219 }, /* lcaron */ + { 0x000001b6, 22610 }, /* sacute */ + { 0x000001b7, 8519 }, /* caron */ + { 0x000001b9, 22624 }, /* scaron */ + { 0x000001ba, 22640 }, /* scedilla */ + { 0x000001bb, 24329 }, /* tcaron */ + { 0x000001bc, 29241 }, /* zacute */ + { 0x000001bd, 11283 }, /* doubleacute */ + { 0x000001be, 29255 }, /* zcaron */ + { 0x000001bf, 29224 }, /* zabovedot */ + { 0x000001c0, 22272 }, /* Racute */ + { 0x000001c3, 445 }, /* Abreve */ + { 0x000001c5, 19147 }, /* Lacute */ + { 0x000001c6, 8475 }, /* Cacute */ + { 0x000001c8, 8525 }, /* Ccaron */ + { 0x000001ca, 12035 }, /* Eogonek */ + { 0x000001cc, 11482 }, /* Ecaron */ + { 0x000001cf, 10365 }, /* Dcaron */ + { 0x000001d0, 11401 }, /* Dstroke */ + { 0x000001d1, 19953 }, /* Nacute */ + { 0x000001d2, 19977 }, /* Ncaron */ + { 0x000001d5, 20488 }, /* Odoubleacute */ + { 0x000001d8, 22294 }, /* Rcaron */ + { 0x000001d9, 26452 }, /* Uring */ + { 0x000001db, 25945 }, /* Udoubleacute */ + { 0x000001de, 24336 }, /* Tcedilla */ + { 0x000001e0, 22279 }, /* racute */ + { 0x000001e3, 452 }, /* abreve */ + { 0x000001e5, 19154 }, /* lacute */ + { 0x000001e6, 8482 }, /* cacute */ + { 0x000001e8, 8532 }, /* ccaron */ + { 0x000001ea, 12043 }, /* eogonek */ + { 0x000001ec, 11489 }, /* ecaron */ + { 0x000001ef, 10372 }, /* dcaron */ + { 0x000001f0, 11409 }, /* dstroke */ + { 0x000001f1, 19960 }, /* nacute */ + { 0x000001f2, 19984 }, /* ncaron */ + { 0x000001f5, 20501 }, /* odoubleacute */ + { 0x000001f8, 22301 }, /* rcaron */ + { 0x000001f9, 26458 }, /* uring */ + { 0x000001fb, 25958 }, /* udoubleacute */ + { 0x000001fe, 24345 }, /* tcedilla */ + { 0x000001ff, 436 }, /* abovedot */ + { 0x000002a1, 17027 }, /* Hstroke */ + { 0x000002a6, 16056 }, /* Hcircumflex */ + { 0x000002a9, 17073 }, /* Iabovedot */ + { 0x000002ab, 12689 }, /* Gbreve */ + { 0x000002ac, 18118 }, /* Jcircumflex */ + { 0x000002b1, 17035 }, /* hstroke */ + { 0x000002b6, 16068 }, /* hcircumflex */ + { 0x000002b9, 17187 }, /* idotless */ + { 0x000002bb, 12696 }, /* gbreve */ + { 0x000002bc, 18130 }, /* jcircumflex */ + { 0x000002c5, 8455 }, /* Cabovedot */ + { 0x000002c6, 8557 }, /* Ccircumflex */ + { 0x000002d5, 12669 }, /* Gabovedot */ + { 0x000002d8, 12735 }, /* Gcircumflex */ + { 0x000002dd, 25885 }, /* Ubreve */ + { 0x000002de, 22661 }, /* Scircumflex */ + { 0x000002e5, 8465 }, /* cabovedot */ + { 0x000002e6, 8569 }, /* ccircumflex */ + { 0x000002f5, 12679 }, /* gabovedot */ + { 0x000002f8, 12747 }, /* gcircumflex */ + { 0x000002fd, 25892 }, /* ubreve */ + { 0x000002fe, 22673 }, /* scircumflex */ + { 0x000003a2, 19108 }, /* kra */ + { 0x000003a3, 22308 }, /* Rcedilla */ + { 0x000003a5, 18100 }, /* Itilde */ + { 0x000003a6, 19226 }, /* Lcedilla */ + { 0x000003aa, 11839 }, /* Emacron */ + { 0x000003ab, 12717 }, /* Gcedilla */ + { 0x000003ac, 25788 }, /* Tslash */ + { 0x000003b3, 22317 }, /* rcedilla */ + { 0x000003b5, 18107 }, /* itilde */ + { 0x000003b6, 19235 }, /* lcedilla */ + { 0x000003ba, 11847 }, /* emacron */ + { 0x000003bb, 12726 }, /* gcedilla */ + { 0x000003bc, 25795 }, /* tslash */ + { 0x000003bd, 11983 }, /* ENG */ + { 0x000003bf, 11987 }, /* eng */ + { 0x000003c0, 892 }, /* Amacron */ + { 0x000003c7, 17338 }, /* Iogonek */ + { 0x000003cc, 11428 }, /* Eabovedot */ + { 0x000003cf, 17231 }, /* Imacron */ + { 0x000003d1, 19991 }, /* Ncedilla */ + { 0x000003d2, 20679 }, /* Omacron */ + { 0x000003d3, 18779 }, /* Kcedilla */ + { 0x000003d9, 26368 }, /* Uogonek */ + { 0x000003dd, 26469 }, /* Utilde */ + { 0x000003de, 26321 }, /* Umacron */ + { 0x000003e0, 900 }, /* amacron */ + { 0x000003e7, 17346 }, /* iogonek */ + { 0x000003ec, 11438 }, /* eabovedot */ + { 0x000003ef, 17239 }, /* imacron */ + { 0x000003f1, 20000 }, /* ncedilla */ + { 0x000003f2, 20687 }, /* omacron */ + { 0x000003f3, 18788 }, /* kcedilla */ + { 0x000003f9, 26376 }, /* uogonek */ + { 0x000003fd, 26476 }, /* utilde */ + { 0x000003fe, 26329 }, /* umacron */ + { 0x0000047e, 21364 }, /* overline */ + { 0x000004a1, 18243 }, /* kana_fullstop */ + { 0x000004a2, 18477 }, /* kana_openingbracket */ + { 0x000004a3, 18173 }, /* kana_closingbracket */ + { 0x000004a4, 18193 }, /* kana_comma */ + { 0x000004a5, 18204 }, /* kana_conjunctive */ + { 0x000004a6, 18689 }, /* kana_WO */ + { 0x000004a7, 18150 }, /* kana_a */ + { 0x000004a8, 18297 }, /* kana_i */ + { 0x000004a9, 18667 }, /* kana_u */ + { 0x000004aa, 18221 }, /* kana_e */ + { 0x000004ab, 18463 }, /* kana_o */ + { 0x000004ac, 18697 }, /* kana_ya */ + { 0x000004ad, 18729 }, /* kana_yu */ + { 0x000004ae, 18713 }, /* kana_yo */ + { 0x000004af, 18633 }, /* kana_tsu */ + { 0x000004b0, 22130 }, /* prolongedsound */ + { 0x000004b1, 18157 }, /* kana_A */ + { 0x000004b2, 18304 }, /* kana_I */ + { 0x000004b3, 18674 }, /* kana_U */ + { 0x000004b4, 18228 }, /* kana_E */ + { 0x000004b5, 18470 }, /* kana_O */ + { 0x000004b6, 18311 }, /* kana_KA */ + { 0x000004b7, 18327 }, /* kana_KI */ + { 0x000004b8, 18343 }, /* kana_KU */ + { 0x000004b9, 18319 }, /* kana_KE */ + { 0x000004ba, 18335 }, /* kana_KO */ + { 0x000004bb, 18537 }, /* kana_SA */ + { 0x000004bc, 18553 }, /* kana_SHI */ + { 0x000004bd, 18581 }, /* kana_SU */ + { 0x000004be, 18545 }, /* kana_SE */ + { 0x000004bf, 18573 }, /* kana_SO */ + { 0x000004c0, 18601 }, /* kana_TA */ + { 0x000004c1, 18164 }, /* kana_CHI */ + { 0x000004c2, 18642 }, /* kana_TSU */ + { 0x000004c3, 18609 }, /* kana_TE */ + { 0x000004c4, 18625 }, /* kana_TO */ + { 0x000004c5, 18423 }, /* kana_NA */ + { 0x000004c6, 18439 }, /* kana_NI */ + { 0x000004c7, 18455 }, /* kana_NU */ + { 0x000004c8, 18431 }, /* kana_NE */ + { 0x000004c9, 18447 }, /* kana_NO */ + { 0x000004ca, 18257 }, /* kana_HA */ + { 0x000004cb, 18273 }, /* kana_HI */ + { 0x000004cc, 18235 }, /* kana_FU */ + { 0x000004cd, 18265 }, /* kana_HE */ + { 0x000004ce, 18281 }, /* kana_HO */ + { 0x000004cf, 18361 }, /* kana_MA */ + { 0x000004d0, 18377 }, /* kana_MI */ + { 0x000004d1, 18408 }, /* kana_MU */ + { 0x000004d2, 18369 }, /* kana_ME */ + { 0x000004d3, 18400 }, /* kana_MO */ + { 0x000004d4, 18705 }, /* kana_YA */ + { 0x000004d5, 18737 }, /* kana_YU */ + { 0x000004d6, 18721 }, /* kana_YO */ + { 0x000004d7, 18497 }, /* kana_RA */ + { 0x000004d8, 18513 }, /* kana_RI */ + { 0x000004d9, 18529 }, /* kana_RU */ + { 0x000004da, 18505 }, /* kana_RE */ + { 0x000004db, 18521 }, /* kana_RO */ + { 0x000004dc, 18681 }, /* kana_WA */ + { 0x000004dd, 18416 }, /* kana_N */ + { 0x000004de, 26519 }, /* voicedsound */ + { 0x000004df, 22744 }, /* semivoicedsound */ + { 0x000005ac, 1109 }, /* Arabic_comma */ + { 0x000005bb, 1764 }, /* Arabic_semicolon */ + { 0x000005bf, 1698 }, /* Arabic_question_mark */ + { 0x000005c1, 1303 }, /* Arabic_hamza */ + { 0x000005c2, 1599 }, /* Arabic_maddaonalef */ + { 0x000005c3, 1354 }, /* Arabic_hamzaonalef */ + { 0x000005c4, 1373 }, /* Arabic_hamzaonwaw */ + { 0x000005c5, 1409 }, /* Arabic_hamzaunderalef */ + { 0x000005c6, 1391 }, /* Arabic_hamzaonyeh */ + { 0x000005c7, 1067 }, /* Arabic_alef */ + { 0x000005c8, 1098 }, /* Arabic_beh */ + { 0x000005c9, 1909 }, /* Arabic_tehmarbuta */ + { 0x000005ca, 1898 }, /* Arabic_teh */ + { 0x000005cb, 1939 }, /* Arabic_theh */ + { 0x000005cc, 1481 }, /* Arabic_jeem */ + { 0x000005cd, 1292 }, /* Arabic_hah */ + { 0x000005ce, 1557 }, /* Arabic_khah */ + { 0x000005cf, 1133 }, /* Arabic_dal */ + { 0x000005d0, 1927 }, /* Arabic_thal */ + { 0x000005d1, 1719 }, /* Arabic_ra */ + { 0x000005d2, 2024 }, /* Arabic_zain */ + { 0x000005d3, 1752 }, /* Arabic_seen */ + { 0x000005d4, 1795 }, /* Arabic_sheen */ + { 0x000005d5, 1741 }, /* Arabic_sad */ + { 0x000005d6, 1122 }, /* Arabic_dad */ + { 0x000005d7, 1859 }, /* Arabic_tah */ + { 0x000005d8, 2013 }, /* Arabic_zah */ + { 0x000005d9, 1056 }, /* Arabic_ain */ + { 0x000005da, 1269 }, /* Arabic_ghain */ + { 0x000005e0, 1870 }, /* Arabic_tatweel */ + { 0x000005e1, 1231 }, /* Arabic_feh */ + { 0x000005e2, 1687 }, /* Arabic_qaf */ + { 0x000005e3, 1504 }, /* Arabic_kaf */ + { 0x000005e4, 1569 }, /* Arabic_lam */ + { 0x000005e5, 1618 }, /* Arabic_meem */ + { 0x000005e6, 1630 }, /* Arabic_noon */ + { 0x000005e7, 1282 }, /* Arabic_ha */ + { 0x000005e8, 1974 }, /* Arabic_waw */ + { 0x000005e9, 1079 }, /* Arabic_alefmaksura */ + { 0x000005ea, 1985 }, /* Arabic_yeh */ + { 0x000005eb, 1215 }, /* Arabic_fathatan */ + { 0x000005ec, 1157 }, /* Arabic_dammatan */ + { 0x000005ed, 1528 }, /* Arabic_kasratan */ + { 0x000005ee, 1202 }, /* Arabic_fatha */ + { 0x000005ef, 1144 }, /* Arabic_damma */ + { 0x000005f0, 1515 }, /* Arabic_kasra */ + { 0x000005f1, 1781 }, /* Arabic_shadda */ + { 0x000005f2, 1808 }, /* Arabic_sukun */ + { 0x000006a1, 22760 }, /* Serbian_dje */ + { 0x000006a2, 19585 }, /* Macedonia_gje */ + { 0x000006a3, 9456 }, /* Cyrillic_io */ + { 0x000006a4, 26199 }, /* Ukrainian_ie */ + { 0x000006a5, 19557 }, /* Macedonia_dse */ + { 0x000006a6, 26175 }, /* Ukrainian_i */ + { 0x000006a7, 26225 }, /* Ukrainian_yi */ + { 0x000006a8, 9480 }, /* Cyrillic_je */ + { 0x000006a9, 9618 }, /* Cyrillic_lje */ + { 0x000006aa, 9644 }, /* Cyrillic_nje */ + { 0x000006ab, 22878 }, /* Serbian_tshe */ + { 0x000006ac, 19613 }, /* Macedonia_kje */ + { 0x000006ad, 26123 }, /* Ukrainian_ghe_with_upturn */ + { 0x000006ae, 8399 }, /* Byelorussian_shortu */ + { 0x000006af, 8972 }, /* Cyrillic_dzhe */ + { 0x000006b0, 20189 }, /* numerosign */ + { 0x000006b1, 22772 }, /* Serbian_DJE */ + { 0x000006b2, 19599 }, /* Macedonia_GJE */ + { 0x000006b3, 9468 }, /* Cyrillic_IO */ + { 0x000006b4, 26212 }, /* Ukrainian_IE */ + { 0x000006b5, 19571 }, /* Macedonia_DSE */ + { 0x000006b6, 26187 }, /* Ukrainian_I */ + { 0x000006b7, 26238 }, /* Ukrainian_YI */ + { 0x000006b8, 9492 }, /* Cyrillic_JE */ + { 0x000006b9, 9631 }, /* Cyrillic_LJE */ + { 0x000006ba, 9657 }, /* Cyrillic_NJE */ + { 0x000006bb, 22891 }, /* Serbian_TSHE */ + { 0x000006bc, 19627 }, /* Macedonia_KJE */ + { 0x000006bd, 26149 }, /* Ukrainian_GHE_WITH_UPTURN */ + { 0x000006be, 8419 }, /* Byelorussian_SHORTU */ + { 0x000006bf, 8986 }, /* Cyrillic_DZHE */ + { 0x000006c0, 10200 }, /* Cyrillic_yu */ + { 0x000006c1, 8782 }, /* Cyrillic_a */ + { 0x000006c2, 8804 }, /* Cyrillic_be */ + { 0x000006c3, 9952 }, /* Cyrillic_tse */ + { 0x000006c4, 8948 }, /* Cyrillic_de */ + { 0x000006c5, 9432 }, /* Cyrillic_ie */ + { 0x000006c6, 9022 }, /* Cyrillic_ef */ + { 0x000006c7, 9210 }, /* Cyrillic_ghe */ + { 0x000006c8, 9270 }, /* Cyrillic_ha */ + { 0x000006c9, 9374 }, /* Cyrillic_i */ + { 0x000006ca, 9860 }, /* Cyrillic_shorti */ + { 0x000006cb, 9504 }, /* Cyrillic_ka */ + { 0x000006cc, 9046 }, /* Cyrillic_el */ + { 0x000006cd, 9070 }, /* Cyrillic_em */ + { 0x000006ce, 9094 }, /* Cyrillic_en */ + { 0x000006cf, 9670 }, /* Cyrillic_o */ + { 0x000006d0, 9722 }, /* Cyrillic_pe */ + { 0x000006d1, 10148 }, /* Cyrillic_ya */ + { 0x000006d2, 9162 }, /* Cyrillic_er */ + { 0x000006d3, 9186 }, /* Cyrillic_es */ + { 0x000006d4, 9928 }, /* Cyrillic_te */ + { 0x000006d5, 9978 }, /* Cyrillic_u */ + { 0x000006d6, 10248 }, /* Cyrillic_zhe */ + { 0x000006d7, 10124 }, /* Cyrillic_ve */ + { 0x000006d8, 9892 }, /* Cyrillic_softsign */ + { 0x000006d9, 10172 }, /* Cyrillic_yeru */ + { 0x000006da, 10224 }, /* Cyrillic_ze */ + { 0x000006db, 9776 }, /* Cyrillic_sha */ + { 0x000006dc, 9000 }, /* Cyrillic_e */ + { 0x000006dd, 9802 }, /* Cyrillic_shcha */ + { 0x000006de, 8828 }, /* Cyrillic_che */ + { 0x000006df, 9338 }, /* Cyrillic_hardsign */ + { 0x000006e0, 10212 }, /* Cyrillic_YU */ + { 0x000006e1, 8793 }, /* Cyrillic_A */ + { 0x000006e2, 8816 }, /* Cyrillic_BE */ + { 0x000006e3, 9965 }, /* Cyrillic_TSE */ + { 0x000006e4, 8960 }, /* Cyrillic_DE */ + { 0x000006e5, 9444 }, /* Cyrillic_IE */ + { 0x000006e6, 9034 }, /* Cyrillic_EF */ + { 0x000006e7, 9223 }, /* Cyrillic_GHE */ + { 0x000006e8, 9282 }, /* Cyrillic_HA */ + { 0x000006e9, 9385 }, /* Cyrillic_I */ + { 0x000006ea, 9876 }, /* Cyrillic_SHORTI */ + { 0x000006eb, 9516 }, /* Cyrillic_KA */ + { 0x000006ec, 9058 }, /* Cyrillic_EL */ + { 0x000006ed, 9082 }, /* Cyrillic_EM */ + { 0x000006ee, 9106 }, /* Cyrillic_EN */ + { 0x000006ef, 9681 }, /* Cyrillic_O */ + { 0x000006f0, 9734 }, /* Cyrillic_PE */ + { 0x000006f1, 10160 }, /* Cyrillic_YA */ + { 0x000006f2, 9174 }, /* Cyrillic_ER */ + { 0x000006f3, 9198 }, /* Cyrillic_ES */ + { 0x000006f4, 9940 }, /* Cyrillic_TE */ + { 0x000006f5, 9989 }, /* Cyrillic_U */ + { 0x000006f6, 10261 }, /* Cyrillic_ZHE */ + { 0x000006f7, 10136 }, /* Cyrillic_VE */ + { 0x000006f8, 9910 }, /* Cyrillic_SOFTSIGN */ + { 0x000006f9, 10186 }, /* Cyrillic_YERU */ + { 0x000006fa, 10236 }, /* Cyrillic_ZE */ + { 0x000006fb, 9789 }, /* Cyrillic_SHA */ + { 0x000006fc, 9011 }, /* Cyrillic_E */ + { 0x000006fd, 9817 }, /* Cyrillic_SHCHA */ + { 0x000006fe, 8841 }, /* Cyrillic_CHE */ + { 0x000006ff, 9356 }, /* Cyrillic_HARDSIGN */ + { 0x000007a1, 13342 }, /* Greek_ALPHAaccent */ + { 0x000007a2, 13472 }, /* Greek_EPSILONaccent */ + { 0x000007a3, 13532 }, /* Greek_ETAaccent */ + { 0x000007a4, 13647 }, /* Greek_IOTAaccent */ + { 0x000007a5, 13726 }, /* Greek_IOTAdieresis */ + { 0x000007a7, 13962 }, /* Greek_OMICRONaccent */ + { 0x000007a8, 14189 }, /* Greek_UPSILONaccent */ + { 0x000007a9, 14257 }, /* Greek_UPSILONdieresis */ + { 0x000007ab, 13898 }, /* Greek_OMEGAaccent */ + { 0x000007ae, 13297 }, /* Greek_accentdieresis */ + { 0x000007af, 13610 }, /* Greek_horizbar */ + { 0x000007b1, 13360 }, /* Greek_alphaaccent */ + { 0x000007b2, 13492 }, /* Greek_epsilonaccent */ + { 0x000007b3, 13548 }, /* Greek_etaaccent */ + { 0x000007b4, 13664 }, /* Greek_iotaaccent */ + { 0x000007b5, 13745 }, /* Greek_iotadieresis */ + { 0x000007b6, 13681 }, /* Greek_iotaaccentdieresis */ + { 0x000007b7, 13982 }, /* Greek_omicronaccent */ + { 0x000007b8, 14209 }, /* Greek_upsilonaccent */ + { 0x000007b9, 14279 }, /* Greek_upsilondieresis */ + { 0x000007ba, 14229 }, /* Greek_upsilonaccentdieresis */ + { 0x000007bb, 13916 }, /* Greek_omegaaccent */ + { 0x000007c1, 13318 }, /* Greek_ALPHA */ + { 0x000007c2, 13378 }, /* Greek_BETA */ + { 0x000007c3, 13586 }, /* Greek_GAMMA */ + { 0x000007c4, 13420 }, /* Greek_DELTA */ + { 0x000007c5, 13444 }, /* Greek_EPSILON */ + { 0x000007c6, 14319 }, /* Greek_ZETA */ + { 0x000007c7, 13512 }, /* Greek_ETA */ + { 0x000007c8, 14137 }, /* Greek_THETA */ + { 0x000007c9, 13625 }, /* Greek_IOTA */ + { 0x000007ca, 13764 }, /* Greek_KAPPA */ + { 0x000007cb, 13814 }, /* Greek_LAMDA */ + { 0x000007cc, 13838 }, /* Greek_MU */ + { 0x000007cd, 13856 }, /* Greek_NU */ + { 0x000007ce, 14301 }, /* Greek_XI */ + { 0x000007cf, 13934 }, /* Greek_OMICRON */ + { 0x000007d0, 14022 }, /* Greek_PI */ + { 0x000007d1, 14060 }, /* Greek_RHO */ + { 0x000007d2, 14080 }, /* Greek_SIGMA */ + { 0x000007d4, 14117 }, /* Greek_TAU */ + { 0x000007d5, 14161 }, /* Greek_UPSILON */ + { 0x000007d6, 14002 }, /* Greek_PHI */ + { 0x000007d7, 13400 }, /* Greek_CHI */ + { 0x000007d8, 14040 }, /* Greek_PSI */ + { 0x000007d9, 13874 }, /* Greek_OMEGA */ + { 0x000007e1, 13330 }, /* Greek_alpha */ + { 0x000007e2, 13389 }, /* Greek_beta */ + { 0x000007e3, 13598 }, /* Greek_gamma */ + { 0x000007e4, 13432 }, /* Greek_delta */ + { 0x000007e5, 13458 }, /* Greek_epsilon */ + { 0x000007e6, 14330 }, /* Greek_zeta */ + { 0x000007e7, 13522 }, /* Greek_eta */ + { 0x000007e8, 14149 }, /* Greek_theta */ + { 0x000007e9, 13636 }, /* Greek_iota */ + { 0x000007ea, 13776 }, /* Greek_kappa */ + { 0x000007eb, 13826 }, /* Greek_lamda */ + { 0x000007ec, 13847 }, /* Greek_mu */ + { 0x000007ed, 13865 }, /* Greek_nu */ + { 0x000007ee, 14310 }, /* Greek_xi */ + { 0x000007ef, 13948 }, /* Greek_omicron */ + { 0x000007f0, 14031 }, /* Greek_pi */ + { 0x000007f1, 14070 }, /* Greek_rho */ + { 0x000007f2, 14092 }, /* Greek_sigma */ + { 0x000007f3, 13564 }, /* Greek_finalsmallsigma */ + { 0x000007f4, 14127 }, /* Greek_tau */ + { 0x000007f5, 14175 }, /* Greek_upsilon */ + { 0x000007f6, 14012 }, /* Greek_phi */ + { 0x000007f7, 13410 }, /* Greek_chi */ + { 0x000007f8, 14050 }, /* Greek_psi */ + { 0x000007f9, 13886 }, /* Greek_omega */ + { 0x000008a1, 19356 }, /* leftradical */ + { 0x000008a2, 25621 }, /* topleftradical */ + { 0x000008a3, 16667 }, /* horizconnector */ + { 0x000008a4, 25595 }, /* topintegral */ + { 0x000008a5, 3422 }, /* botintegral */ + { 0x000008a6, 26505 }, /* vertconnector */ + { 0x000008a7, 25636 }, /* topleftsqbracket */ + { 0x000008a8, 3448 }, /* botleftsqbracket */ + { 0x000008a9, 25685 }, /* toprightsqbracket */ + { 0x000008aa, 3497 }, /* botrightsqbracket */ + { 0x000008ab, 25607 }, /* topleftparens */ + { 0x000008ac, 3434 }, /* botleftparens */ + { 0x000008ad, 25670 }, /* toprightparens */ + { 0x000008ae, 3482 }, /* botrightparens */ + { 0x000008af, 19306 }, /* leftmiddlecurlybrace */ + { 0x000008b0, 22440 }, /* rightmiddlecurlybrace */ + { 0x000008b1, 25653 }, /* topleftsummation */ + { 0x000008b2, 3465 }, /* botleftsummation */ + { 0x000008b3, 25726 }, /* topvertsummationconnector */ + { 0x000008b4, 3538 }, /* botvertsummationconnector */ + { 0x000008b5, 25703 }, /* toprightsummation */ + { 0x000008b6, 3515 }, /* botrightsummation */ + { 0x000008b7, 22462 }, /* rightmiddlesummation */ + { 0x000008bc, 19417 }, /* lessthanequal */ + { 0x000008bd, 20125 }, /* notequal */ + { 0x000008be, 13280 }, /* greaterthanequal */ + { 0x000008bf, 17313 }, /* integral */ + { 0x000008c0, 25479 }, /* therefore */ + { 0x000008c1, 26487 }, /* variation */ + { 0x000008c2, 17275 }, /* infinity */ + { 0x000008c5, 19947 }, /* nabla */ + { 0x000008c8, 954 }, /* approximate */ + { 0x000008c9, 22999 }, /* similarequal */ + { 0x000008cd, 17196 }, /* ifonlyif */ + { 0x000008ce, 17247 }, /* implies */ + { 0x000008cf, 17155 }, /* identical */ + { 0x000008d6, 22286 }, /* radical */ + { 0x000008da, 17255 }, /* includedin */ + { 0x000008db, 17266 }, /* includes */ + { 0x000008dc, 17322 }, /* intersection */ + { 0x000008dd, 26362 }, /* union */ + { 0x000008de, 19457 }, /* logicaland */ + { 0x000008df, 19468 }, /* logicalor */ + { 0x000008ef, 21463 }, /* partialderivative */ + { 0x000008f6, 12656 }, /* function */ + { 0x000008fb, 19266 }, /* leftarrow */ + { 0x000008fc, 26387 }, /* uparrow */ + { 0x000008fd, 22397 }, /* rightarrow */ + { 0x000008fe, 11332 }, /* downarrow */ + { 0x000009df, 3410 }, /* blank */ + { 0x000009e0, 23790 }, /* soliddiamond */ + { 0x000009e1, 8603 }, /* checkerboard */ + { 0x000009e2, 17043 }, /* ht */ + { 0x000009e3, 12413 }, /* ff */ + { 0x000009e4, 8727 }, /* cr */ + { 0x000009e5, 19431 }, /* lf */ + { 0x000009e8, 20075 }, /* nl */ + { 0x000009e9, 26542 }, /* vt */ + { 0x000009ea, 19502 }, /* lowrightcorner */ + { 0x000009eb, 26416 }, /* uprightcorner */ + { 0x000009ec, 26403 }, /* upleftcorner */ + { 0x000009ed, 19488 }, /* lowleftcorner */ + { 0x000009ee, 8730 }, /* crossinglines */ + { 0x000009ef, 16682 }, /* horizlinescan1 */ + { 0x000009f0, 16697 }, /* horizlinescan3 */ + { 0x000009f1, 16712 }, /* horizlinescan5 */ + { 0x000009f2, 16727 }, /* horizlinescan7 */ + { 0x000009f3, 16742 }, /* horizlinescan9 */ + { 0x000009f4, 19397 }, /* leftt */ + { 0x000009f5, 22545 }, /* rightt */ + { 0x000009f6, 3533 }, /* bott */ + { 0x000009f7, 25721 }, /* topt */ + { 0x000009f8, 26497 }, /* vertbar */ + { 0x00000aa1, 11928 }, /* emspace */ + { 0x00000aa2, 12027 }, /* enspace */ + { 0x00000aa3, 11821 }, /* em3space */ + { 0x00000aa4, 11830 }, /* em4space */ + { 0x00000aa5, 11221 }, /* digitspace */ + { 0x00000aa6, 22145 }, /* punctspace */ + { 0x00000aa7, 25489 }, /* thinspace */ + { 0x00000aa8, 14382 }, /* hairspace */ + { 0x00000aa9, 11855 }, /* emdash */ + { 0x00000aaa, 11940 }, /* endash */ + { 0x00000aac, 22987 }, /* signifblank */ + { 0x00000aae, 11812 }, /* ellipsis */ + { 0x00000aaf, 11267 }, /* doubbaselinedot */ + { 0x00000ab0, 20767 }, /* onethird */ + { 0x00000ab1, 25837 }, /* twothirds */ + { 0x00000ab2, 20705 }, /* onefifth */ + { 0x00000ab3, 25802 }, /* twofifths */ + { 0x00000ab4, 25530 }, /* threefifths */ + { 0x00000ab5, 12607 }, /* fourfifths */ + { 0x00000ab6, 20733 }, /* onesixth */ + { 0x00000ab7, 12569 }, /* fivesixths */ + { 0x00000ab8, 8506 }, /* careof */ + { 0x00000abb, 12427 }, /* figdash */ + { 0x00000abc, 19249 }, /* leftanglebracket */ + { 0x00000abd, 11140 }, /* decimalpoint */ + { 0x00000abe, 22379 }, /* rightanglebracket */ + { 0x00000abf, 19681 }, /* marker */ + { 0x00000ac3, 20695 }, /* oneeighth */ + { 0x00000ac4, 25517 }, /* threeeighths */ + { 0x00000ac5, 12557 }, /* fiveeighths */ + { 0x00000ac6, 22904 }, /* seveneighths */ + { 0x00000ac9, 25760 }, /* trademark */ + { 0x00000aca, 22973 }, /* signaturemark */ + { 0x00000acb, 25770 }, /* trademarkincircle */ + { 0x00000acc, 19327 }, /* leftopentriangle */ + { 0x00000acd, 22483 }, /* rightopentriangle */ + { 0x00000ace, 11890 }, /* emopencircle */ + { 0x00000acf, 11903 }, /* emopenrectangle */ + { 0x00000ad0, 19377 }, /* leftsinglequotemark */ + { 0x00000ad1, 22524 }, /* rightsinglequotemark */ + { 0x00000ad2, 19286 }, /* leftdoublequotemark */ + { 0x00000ad3, 22419 }, /* rightdoublequotemark */ + { 0x00000ad4, 22067 }, /* prescription */ + { 0x00000ad5, 21517 }, /* permille */ + { 0x00000ad6, 19739 }, /* minutes */ + { 0x00000ad7, 22711 }, /* seconds */ + { 0x00000ad9, 19181 }, /* latincross */ + { 0x00000ada, 16626 }, /* hexagram */ + { 0x00000adb, 12455 }, /* filledrectbullet */ + { 0x00000adc, 12435 }, /* filledlefttribullet */ + { 0x00000add, 12472 }, /* filledrighttribullet */ + { 0x00000ade, 11862 }, /* emfilledcircle */ + { 0x00000adf, 11877 }, /* emfilledrect */ + { 0x00000ae0, 11991 }, /* enopencircbullet */ + { 0x00000ae1, 12008 }, /* enopensquarebullet */ + { 0x00000ae2, 20794 }, /* openrectbullet */ + { 0x00000ae3, 20836 }, /* opentribulletup */ + { 0x00000ae4, 20818 }, /* opentribulletdown */ + { 0x00000ae5, 20809 }, /* openstar */ + { 0x00000ae6, 11947 }, /* enfilledcircbullet */ + { 0x00000ae7, 11966 }, /* enfilledsqbullet */ + { 0x00000ae8, 12513 }, /* filledtribulletup */ + { 0x00000ae9, 12493 }, /* filledtribulletdown */ + { 0x00000aea, 19344 }, /* leftpointer */ + { 0x00000aeb, 22501 }, /* rightpointer */ + { 0x00000aec, 8649 }, /* club */ + { 0x00000aed, 11213 }, /* diamond */ + { 0x00000aee, 16080 }, /* heart */ + { 0x00000af0, 19668 }, /* maltesecross */ + { 0x00000af1, 10358 }, /* dagger */ + { 0x00000af2, 11295 }, /* doubledagger */ + { 0x00000af3, 8616 }, /* checkmark */ + { 0x00000af4, 3380 }, /* ballotcross */ + { 0x00000af5, 19860 }, /* musicalsharp */ + { 0x00000af6, 19848 }, /* musicalflat */ + { 0x00000af7, 19657 }, /* malesymbol */ + { 0x00000af8, 12400 }, /* femalesymbol */ + { 0x00000af9, 24354 }, /* telephone */ + { 0x00000afa, 24364 }, /* telephonerecorder */ + { 0x00000afb, 21537 }, /* phonographcopyright */ + { 0x00000afc, 8513 }, /* caret */ + { 0x00000afd, 23028 }, /* singlelowquotemark */ + { 0x00000afe, 11308 }, /* doublelowquotemark */ + { 0x00000aff, 8775 }, /* cursor */ + { 0x00000ba3, 19276 }, /* leftcaret */ + { 0x00000ba6, 22408 }, /* rightcaret */ + { 0x00000ba8, 11342 }, /* downcaret */ + { 0x00000ba9, 26395 }, /* upcaret */ + { 0x00000bc0, 21324 }, /* overbar */ + { 0x00000bc2, 11371 }, /* downtack */ + { 0x00000bc3, 26430 }, /* upshoe */ + { 0x00000bc4, 11361 }, /* downstile */ + { 0x00000bc6, 26337 }, /* underbar */ + { 0x00000bca, 18142 }, /* jot */ + { 0x00000bcc, 22160 }, /* quad */ + { 0x00000bce, 26445 }, /* uptack */ + { 0x00000bcf, 8626 }, /* circle */ + { 0x00000bd3, 26437 }, /* upstile */ + { 0x00000bd6, 11352 }, /* downshoe */ + { 0x00000bd8, 22514 }, /* rightshoe */ + { 0x00000bda, 19368 }, /* leftshoe */ + { 0x00000bdc, 19403 }, /* lefttack */ + { 0x00000bfc, 22552 }, /* righttack */ + { 0x00000cdf, 16173 }, /* hebrew_doublelowline */ + { 0x00000ce0, 16086 }, /* hebrew_aleph */ + { 0x00000ce1, 16111 }, /* hebrew_bet */ + { 0x00000ce2, 16292 }, /* hebrew_gimel */ + { 0x00000ce3, 16146 }, /* hebrew_dalet */ + { 0x00000ce4, 16319 }, /* hebrew_he */ + { 0x00000ce5, 16531 }, /* hebrew_waw */ + { 0x00000ce6, 16577 }, /* hebrew_zain */ + { 0x00000ce7, 16134 }, /* hebrew_chet */ + { 0x00000ce8, 16508 }, /* hebrew_tet */ + { 0x00000ce9, 16542 }, /* hebrew_yod */ + { 0x00000cea, 16194 }, /* hebrew_finalkaph */ + { 0x00000ceb, 16340 }, /* hebrew_kaph */ + { 0x00000cec, 16363 }, /* hebrew_lamed */ + { 0x00000ced, 16211 }, /* hebrew_finalmem */ + { 0x00000cee, 16376 }, /* hebrew_mem */ + { 0x00000cef, 16227 }, /* hebrew_finalnun */ + { 0x00000cf0, 16387 }, /* hebrew_nun */ + { 0x00000cf1, 16432 }, /* hebrew_samech */ + { 0x00000cf2, 16099 }, /* hebrew_ayin */ + { 0x00000cf3, 16243 }, /* hebrew_finalpe */ + { 0x00000cf4, 16398 }, /* hebrew_pe */ + { 0x00000cf5, 16258 }, /* hebrew_finalzade */ + { 0x00000cf6, 16553 }, /* hebrew_zade */ + { 0x00000cf7, 16408 }, /* hebrew_qoph */ + { 0x00000cf8, 16420 }, /* hebrew_resh */ + { 0x00000cf9, 16460 }, /* hebrew_shin */ + { 0x00000cfa, 16497 }, /* hebrew_taw */ + { 0x00000da1, 24617 }, /* Thai_kokai */ + { 0x00000da2, 24547 }, /* Thai_khokhai */ + { 0x00000da3, 24573 }, /* Thai_khokhuat */ + { 0x00000da4, 24587 }, /* Thai_khokhwai */ + { 0x00000da5, 24560 }, /* Thai_khokhon */ + { 0x00000da6, 24601 }, /* Thai_khorakhang */ + { 0x00000da7, 24930 }, /* Thai_ngongu */ + { 0x00000da8, 24423 }, /* Thai_chochan */ + { 0x00000da9, 24450 }, /* Thai_choching */ + { 0x00000daa, 24436 }, /* Thai_chochang */ + { 0x00000dab, 25291 }, /* Thai_soso */ + { 0x00000dac, 24464 }, /* Thai_chochoe */ + { 0x00000dad, 25467 }, /* Thai_yoying */ + { 0x00000dae, 24477 }, /* Thai_dochada */ + { 0x00000daf, 25420 }, /* Thai_topatak */ + { 0x00000db0, 25379 }, /* Thai_thothan */ + { 0x00000db1, 25329 }, /* Thai_thonangmontho */ + { 0x00000db2, 25348 }, /* Thai_thophuthao */ + { 0x00000db3, 24956 }, /* Thai_nonen */ + { 0x00000db4, 24490 }, /* Thai_dodek */ + { 0x00000db5, 25433 }, /* Thai_totao */ + { 0x00000db6, 25406 }, /* Thai_thothung */ + { 0x00000db7, 25364 }, /* Thai_thothahan */ + { 0x00000db8, 25392 }, /* Thai_thothong */ + { 0x00000db9, 24967 }, /* Thai_nonu */ + { 0x00000dba, 24409 }, /* Thai_bobaimai */ + { 0x00000dbb, 25058 }, /* Thai_popla */ + { 0x00000dbc, 25028 }, /* Thai_phophung */ + { 0x00000dbd, 24501 }, /* Thai_fofa */ + { 0x00000dbe, 25015 }, /* Thai_phophan */ + { 0x00000dbf, 24511 }, /* Thai_fofan */ + { 0x00000dc0, 25042 }, /* Thai_phosamphao */ + { 0x00000dc1, 24920 }, /* Thai_moma */ + { 0x00000dc2, 25456 }, /* Thai_yoyak */ + { 0x00000dc3, 25069 }, /* Thai_rorua */ + { 0x00000dc4, 25080 }, /* Thai_ru */ + { 0x00000dc5, 24780 }, /* Thai_loling */ + { 0x00000dc6, 24792 }, /* Thai_lu */ + { 0x00000dc7, 25444 }, /* Thai_wowaen */ + { 0x00000dc8, 25279 }, /* Thai_sosala */ + { 0x00000dc9, 25267 }, /* Thai_sorusi */ + { 0x00000dca, 25301 }, /* Thai_sosua */ + { 0x00000dcb, 24522 }, /* Thai_hohip */ + { 0x00000dcc, 24767 }, /* Thai_lochula */ + { 0x00000dcd, 24977 }, /* Thai_oang */ + { 0x00000dce, 24533 }, /* Thai_honokhuk */ + { 0x00000dcf, 24987 }, /* Thai_paiyannoi */ + { 0x00000dd0, 25088 }, /* Thai_saraa */ + { 0x00000dd1, 24828 }, /* Thai_maihanakat */ + { 0x00000dd2, 25099 }, /* Thai_saraaa */ + { 0x00000dd3, 25162 }, /* Thai_saraam */ + { 0x00000dd4, 25185 }, /* Thai_sarai */ + { 0x00000dd5, 25196 }, /* Thai_saraii */ + { 0x00000dd6, 25230 }, /* Thai_saraue */ + { 0x00000dd7, 25242 }, /* Thai_sarauee */ + { 0x00000dd8, 25219 }, /* Thai_sarau */ + { 0x00000dd9, 25255 }, /* Thai_sarauu */ + { 0x00000dda, 25002 }, /* Thai_phinthu */ + { 0x00000dde, 24844 }, /* Thai_maihanakat_maitho */ + { 0x00000ddf, 24399 }, /* Thai_baht */ + { 0x00000de0, 25174 }, /* Thai_sarae */ + { 0x00000de1, 25111 }, /* Thai_saraae */ + { 0x00000de2, 25208 }, /* Thai_sarao */ + { 0x00000de3, 25143 }, /* Thai_saraaimaimuan */ + { 0x00000de4, 25123 }, /* Thai_saraaimaimalai */ + { 0x00000de5, 24628 }, /* Thai_lakkhangyao */ + { 0x00000de6, 24906 }, /* Thai_maiyamok */ + { 0x00000de7, 24867 }, /* Thai_maitaikhu */ + { 0x00000de8, 24817 }, /* Thai_maiek */ + { 0x00000de9, 24882 }, /* Thai_maitho */ + { 0x00000dea, 24894 }, /* Thai_maitri */ + { 0x00000deb, 24800 }, /* Thai_maichattawa */ + { 0x00000dec, 25312 }, /* Thai_thanthakhat */ + { 0x00000ded, 24942 }, /* Thai_nikhahit */ + { 0x00000df0, 24755 }, /* Thai_leksun */ + { 0x00000df1, 24693 }, /* Thai_leknung */ + { 0x00000df2, 24742 }, /* Thai_leksong */ + { 0x00000df3, 24719 }, /* Thai_leksam */ + { 0x00000df4, 24731 }, /* Thai_leksi */ + { 0x00000df5, 24658 }, /* Thai_lekha */ + { 0x00000df6, 24669 }, /* Thai_lekhok */ + { 0x00000df7, 24645 }, /* Thai_lekchet */ + { 0x00000df8, 24706 }, /* Thai_lekpaet */ + { 0x00000df9, 24681 }, /* Thai_lekkao */ + { 0x00000ea1, 15181 }, /* Hangul_Kiyeog */ + { 0x00000ea2, 15726 }, /* Hangul_SsangKiyeog */ + { 0x00000ea3, 15195 }, /* Hangul_KiyeogSios */ + { 0x00000ea4, 15276 }, /* Hangul_Nieun */ + { 0x00000ea5, 15307 }, /* Hangul_NieunJieuj */ + { 0x00000ea6, 15289 }, /* Hangul_NieunHieuh */ + { 0x00000ea7, 14488 }, /* Hangul_Dikeud */ + { 0x00000ea8, 15689 }, /* Hangul_SsangDikeud */ + { 0x00000ea9, 15461 }, /* Hangul_Rieul */ + { 0x00000eaa, 15492 }, /* Hangul_RieulKiyeog */ + { 0x00000eab, 15511 }, /* Hangul_RieulMieum */ + { 0x00000eac, 15548 }, /* Hangul_RieulPieub */ + { 0x00000ead, 15566 }, /* Hangul_RieulSios */ + { 0x00000eae, 15583 }, /* Hangul_RieulTieut */ + { 0x00000eaf, 15529 }, /* Hangul_RieulPhieuf */ + { 0x00000eb0, 15474 }, /* Hangul_RieulHieuh */ + { 0x00000eb1, 15238 }, /* Hangul_Mieum */ + { 0x00000eb2, 15373 }, /* Hangul_Pieub */ + { 0x00000eb3, 15745 }, /* Hangul_SsangPieub */ + { 0x00000eb4, 15386 }, /* Hangul_PieubSios */ + { 0x00000eb5, 15662 }, /* Hangul_Sios */ + { 0x00000eb6, 15763 }, /* Hangul_SsangSios */ + { 0x00000eb7, 14577 }, /* Hangul_Ieung */ + { 0x00000eb8, 15154 }, /* Hangul_Jieuj */ + { 0x00000eb9, 15708 }, /* Hangul_SsangJieuj */ + { 0x00000eba, 14458 }, /* Hangul_Cieuc */ + { 0x00000ebb, 15167 }, /* Hangul_Khieuq */ + { 0x00000ebc, 15883 }, /* Hangul_Tieut */ + { 0x00000ebd, 15359 }, /* Hangul_Phieuf */ + { 0x00000ebe, 14555 }, /* Hangul_Hieuh */ + { 0x00000ebf, 14399 }, /* Hangul_A */ + { 0x00000ec0, 14408 }, /* Hangul_AE */ + { 0x00000ec1, 15957 }, /* Hangul_YA */ + { 0x00000ec2, 15967 }, /* Hangul_YAE */ + { 0x00000ec3, 14522 }, /* Hangul_EO */ + { 0x00000ec4, 14502 }, /* Hangul_E */ + { 0x00000ec5, 15988 }, /* Hangul_YEO */ + { 0x00000ec6, 15978 }, /* Hangul_YE */ + { 0x00000ec7, 15325 }, /* Hangul_O */ + { 0x00000ec8, 15905 }, /* Hangul_WA */ + { 0x00000ec9, 15915 }, /* Hangul_WAE */ + { 0x00000eca, 15334 }, /* Hangul_OE */ + { 0x00000ecb, 16028 }, /* Hangul_YO */ + { 0x00000ecc, 15896 }, /* Hangul_U */ + { 0x00000ecd, 15936 }, /* Hangul_WEO */ + { 0x00000ece, 15926 }, /* Hangul_WE */ + { 0x00000ecf, 15947 }, /* Hangul_WI */ + { 0x00000ed0, 16038 }, /* Hangul_YU */ + { 0x00000ed1, 14532 }, /* Hangul_EU */ + { 0x00000ed2, 16018 }, /* Hangul_YI */ + { 0x00000ed3, 14568 }, /* Hangul_I */ + { 0x00000ed4, 14682 }, /* Hangul_J_Kiyeog */ + { 0x00000ed5, 15052 }, /* Hangul_J_SsangKiyeog */ + { 0x00000ed6, 14698 }, /* Hangul_J_KiyeogSios */ + { 0x00000ed7, 14760 }, /* Hangul_J_Nieun */ + { 0x00000ed8, 14795 }, /* Hangul_J_NieunJieuj */ + { 0x00000ed9, 14775 }, /* Hangul_J_NieunHieuh */ + { 0x00000eda, 14605 }, /* Hangul_J_Dikeud */ + { 0x00000edb, 14882 }, /* Hangul_J_Rieul */ + { 0x00000edc, 14917 }, /* Hangul_J_RieulKiyeog */ + { 0x00000edd, 14938 }, /* Hangul_J_RieulMieum */ + { 0x00000ede, 14979 }, /* Hangul_J_RieulPieub */ + { 0x00000edf, 14999 }, /* Hangul_J_RieulSios */ + { 0x00000ee0, 15018 }, /* Hangul_J_RieulTieut */ + { 0x00000ee1, 14958 }, /* Hangul_J_RieulPhieuf */ + { 0x00000ee2, 14897 }, /* Hangul_J_RieulHieuh */ + { 0x00000ee3, 14745 }, /* Hangul_J_Mieum */ + { 0x00000ee4, 14848 }, /* Hangul_J_Pieub */ + { 0x00000ee5, 14863 }, /* Hangul_J_PieubSios */ + { 0x00000ee6, 15038 }, /* Hangul_J_Sios */ + { 0x00000ee7, 15073 }, /* Hangul_J_SsangSios */ + { 0x00000ee8, 14636 }, /* Hangul_J_Ieung */ + { 0x00000ee9, 14651 }, /* Hangul_J_Jieuj */ + { 0x00000eea, 14590 }, /* Hangul_J_Cieuc */ + { 0x00000eeb, 14666 }, /* Hangul_J_Khieuq */ + { 0x00000eec, 15092 }, /* Hangul_J_Tieut */ + { 0x00000eed, 14832 }, /* Hangul_J_Phieuf */ + { 0x00000eee, 14621 }, /* Hangul_J_Hieuh */ + { 0x00000eef, 15601 }, /* Hangul_RieulYeorinHieuh */ + { 0x00000ef0, 15793 }, /* Hangul_SunkyeongeumMieum */ + { 0x00000ef1, 15844 }, /* Hangul_SunkyeongeumPieub */ + { 0x00000ef2, 15344 }, /* Hangul_PanSios */ + { 0x00000ef3, 15213 }, /* Hangul_KkogjiDalrinIeung */ + { 0x00000ef4, 15818 }, /* Hangul_SunkyeongeumPhieuf */ + { 0x00000ef5, 15999 }, /* Hangul_YeorinHieuh */ + { 0x00000ef6, 14418 }, /* Hangul_AraeA */ + { 0x00000ef7, 14431 }, /* Hangul_AraeAE */ + { 0x00000ef8, 14815 }, /* Hangul_J_PanSios */ + { 0x00000ef9, 14718 }, /* Hangul_J_KkogjiDalrinIeung */ + { 0x00000efa, 15107 }, /* Hangul_J_YeorinHieuh */ + { 0x00000eff, 18797 }, /* Korean_Won */ + { 0x000013bc, 20514 }, /* OE */ + { 0x000013bd, 20517 }, /* oe */ + { 0x000013be, 29155 }, /* Ydiaeresis */ + { 0x000020ac, 12090 }, /* EuroSign */ + { 0x0000fd01, 125 }, /* 3270_Duplicate */ + { 0x0000fd02, 195 }, /* 3270_FieldMark */ + { 0x0000fd03, 343 }, /* 3270_Right2 */ + { 0x0000fd04, 245 }, /* 3270_Left2 */ + { 0x0000fd05, 33 }, /* 3270_BackTab */ + { 0x0000fd06, 151 }, /* 3270_EraseEOF */ + { 0x0000fd07, 165 }, /* 3270_EraseInput */ + { 0x0000fd08, 332 }, /* 3270_Reset */ + { 0x0000fd09, 310 }, /* 3270_Quit */ + { 0x0000fd0a, 256 }, /* 3270_PA1 */ + { 0x0000fd0b, 265 }, /* 3270_PA2 */ + { 0x0000fd0c, 274 }, /* 3270_PA3 */ + { 0x0000fd0d, 376 }, /* 3270_Test */ + { 0x0000fd0e, 23 }, /* 3270_Attn */ + { 0x0000fd0f, 74 }, /* 3270_CursorBlink */ + { 0x0000fd10, 8 }, /* 3270_AltCursor */ + { 0x0000fd11, 231 }, /* 3270_KeyClick */ + { 0x0000fd12, 221 }, /* 3270_Jump */ + { 0x0000fd13, 210 }, /* 3270_Ident */ + { 0x0000fd14, 355 }, /* 3270_Rule */ + { 0x0000fd15, 64 }, /* 3270_Copy */ + { 0x0000fd16, 283 }, /* 3270_Play */ + { 0x0000fd17, 365 }, /* 3270_Setup */ + { 0x0000fd18, 320 }, /* 3270_Record */ + { 0x0000fd19, 46 }, /* 3270_ChangeScreen */ + { 0x0000fd1a, 109 }, /* 3270_DeleteWord */ + { 0x0000fd1b, 181 }, /* 3270_ExSelect */ + { 0x0000fd1c, 91 }, /* 3270_CursorSelect */ + { 0x0000fd1d, 293 }, /* 3270_PrintScreen */ + { 0x0000fd1e, 140 }, /* 3270_Enter */ + { 0x0000fe01, 17781 }, /* ISO_Lock */ + { 0x0000fe02, 17664 }, /* ISO_Level2_Latch */ + { 0x0000fe03, 17714 }, /* ISO_Level3_Shift */ + { 0x0000fe04, 17681 }, /* ISO_Level3_Latch */ + { 0x0000fe05, 17698 }, /* ISO_Level3_Lock */ + { 0x0000fe06, 17569 }, /* ISO_Group_Latch */ + { 0x0000fe07, 17585 }, /* ISO_Group_Lock */ + { 0x0000fe08, 17826 }, /* ISO_Next_Group */ + { 0x0000fe09, 17841 }, /* ISO_Next_Group_Lock */ + { 0x0000fe0a, 17950 }, /* ISO_Prev_Group */ + { 0x0000fe0b, 17965 }, /* ISO_Prev_Group_Lock */ + { 0x0000fe0c, 17532 }, /* ISO_First_Group */ + { 0x0000fe0d, 17548 }, /* ISO_First_Group_Lock */ + { 0x0000fe0e, 17616 }, /* ISO_Last_Group */ + { 0x0000fe0f, 17631 }, /* ISO_Last_Group_Lock */ + { 0x0000fe11, 17764 }, /* ISO_Level5_Shift */ + { 0x0000fe12, 17731 }, /* ISO_Level5_Latch */ + { 0x0000fe13, 17748 }, /* ISO_Level5_Lock */ + { 0x0000fe20, 17651 }, /* ISO_Left_Tab */ + { 0x0000fe21, 17809 }, /* ISO_Move_Line_Up */ + { 0x0000fe22, 17790 }, /* ISO_Move_Line_Down */ + { 0x0000fe23, 17883 }, /* ISO_Partial_Line_Up */ + { 0x0000fe24, 17861 }, /* ISO_Partial_Line_Down */ + { 0x0000fe25, 17903 }, /* ISO_Partial_Space_Left */ + { 0x0000fe26, 17926 }, /* ISO_Partial_Space_Right */ + { 0x0000fe27, 18059 }, /* ISO_Set_Margin_Left */ + { 0x0000fe28, 18079 }, /* ISO_Set_Margin_Right */ + { 0x0000fe29, 18010 }, /* ISO_Release_Margin_Left */ + { 0x0000fe2a, 18034 }, /* ISO_Release_Margin_Right */ + { 0x0000fe2b, 17985 }, /* ISO_Release_Both_Margins */ + { 0x0000fe2c, 17470 }, /* ISO_Fast_Cursor_Left */ + { 0x0000fe2d, 17491 }, /* ISO_Fast_Cursor_Right */ + { 0x0000fe2e, 17513 }, /* ISO_Fast_Cursor_Up */ + { 0x0000fe2f, 17449 }, /* ISO_Fast_Cursor_Down */ + { 0x0000fe30, 17372 }, /* ISO_Continuous_Underline */ + { 0x0000fe31, 17397 }, /* ISO_Discontinuous_Underline */ + { 0x0000fe32, 17425 }, /* ISO_Emphasize */ + { 0x0000fe33, 17354 }, /* ISO_Center_Object */ + { 0x0000fe34, 17439 }, /* ISO_Enter */ + { 0x0000fe50, 10858 }, /* dead_grave */ + { 0x0000fe51, 10531 }, /* dead_acute */ + { 0x0000fe52, 10754 }, /* dead_circumflex */ + { 0x0000fe53, 11097 }, /* dead_tilde */ + { 0x0000fe54, 10980 }, /* dead_macron */ + { 0x0000fe55, 10700 }, /* dead_breve */ + { 0x0000fe56, 10455 }, /* dead_abovedot */ + { 0x0000fe57, 10795 }, /* dead_diaeresis */ + { 0x0000fe58, 10493 }, /* dead_abovering */ + { 0x0000fe59, 10810 }, /* dead_doubleacute */ + { 0x0000fe5a, 10730 }, /* dead_caron */ + { 0x0000fe5b, 10741 }, /* dead_cedilla */ + { 0x0000fe5c, 11006 }, /* dead_ogonek */ + { 0x0000fe5d, 10933 }, /* dead_iota */ + { 0x0000fe5e, 11122 }, /* dead_voiced_sound */ + { 0x0000fe5f, 11046 }, /* dead_semivoiced_sound */ + { 0x0000fe60, 10615 }, /* dead_belowdot */ + { 0x0000fe61, 10880 }, /* dead_hook */ + { 0x0000fe62, 10890 }, /* dead_horn */ + { 0x0000fe63, 11085 }, /* dead_stroke */ + { 0x0000fe64, 10439 }, /* dead_abovecomma */ + { 0x0000fe65, 10469 }, /* dead_abovereversedcomma */ + { 0x0000fe66, 10827 }, /* dead_doublegrave */ + { 0x0000fe67, 10646 }, /* dead_belowring */ + { 0x0000fe68, 10629 }, /* dead_belowmacron */ + { 0x0000fe69, 10558 }, /* dead_belowcircumflex */ + { 0x0000fe6a, 10661 }, /* dead_belowtilde */ + { 0x0000fe6b, 10542 }, /* dead_belowbreve */ + { 0x0000fe6c, 10595 }, /* dead_belowdiaeresis */ + { 0x0000fe6d, 10914 }, /* dead_invertedbreve */ + { 0x0000fe6e, 10579 }, /* dead_belowcomma */ + { 0x0000fe6f, 10770 }, /* dead_currency */ + { 0x0000fe70, 583 }, /* AccessX_Enable */ + { 0x0000fe71, 598 }, /* AccessX_Feedback_Enable */ + { 0x0000fe72, 22342 }, /* RepeatKeys_Enable */ + { 0x0000fe73, 23774 }, /* SlowKeys_Enable */ + { 0x0000fe74, 3564 }, /* BounceKeys_Enable */ + { 0x0000fe75, 23836 }, /* StickyKeys_Enable */ + { 0x0000fe76, 19782 }, /* MouseKeys_Enable */ + { 0x0000fe77, 19759 }, /* MouseKeys_Accel_Enable */ + { 0x0000fe78, 21332 }, /* Overlay1_Enable */ + { 0x0000fe79, 21348 }, /* Overlay2_Enable */ + { 0x0000fe7a, 3309 }, /* AudibleBell_Enable */ + { 0x0000fe80, 10425 }, /* dead_a */ + { 0x0000fe81, 10432 }, /* dead_A */ + { 0x0000fe82, 10844 }, /* dead_e */ + { 0x0000fe83, 10851 }, /* dead_E */ + { 0x0000fe84, 10900 }, /* dead_i */ + { 0x0000fe85, 10907 }, /* dead_I */ + { 0x0000fe86, 10992 }, /* dead_o */ + { 0x0000fe87, 10999 }, /* dead_O */ + { 0x0000fe88, 11108 }, /* dead_u */ + { 0x0000fe89, 11115 }, /* dead_U */ + { 0x0000fe8a, 11068 }, /* dead_small_schwa */ + { 0x0000fe8b, 10711 }, /* dead_capital_schwa */ + { 0x0000fe8c, 10869 }, /* dead_greek */ + { 0x0000fe90, 10967 }, /* dead_lowline */ + { 0x0000fe91, 10508 }, /* dead_aboveverticalline */ + { 0x0000fe92, 10677 }, /* dead_belowverticalline */ + { 0x0000fe93, 10943 }, /* dead_longsolidusoverlay */ + { 0x0000fea0, 8594 }, /* ch */ + { 0x0000fea1, 8597 }, /* Ch */ + { 0x0000fea2, 8600 }, /* CH */ + { 0x0000fea3, 8443 }, /* c_h */ + { 0x0000fea4, 8447 }, /* C_h */ + { 0x0000fea5, 8451 }, /* C_H */ + { 0x0000fed0, 12536 }, /* First_Virtual_Screen */ + { 0x0000fed1, 22080 }, /* Prev_Virtual_Screen */ + { 0x0000fed2, 20028 }, /* Next_Virtual_Screen */ + { 0x0000fed4, 19161 }, /* Last_Virtual_Screen */ + { 0x0000fed5, 24382 }, /* Terminate_Server */ + { 0x0000fee0, 21998 }, /* Pointer_Left */ + { 0x0000fee1, 22011 }, /* Pointer_Right */ + { 0x0000fee2, 22025 }, /* Pointer_Up */ + { 0x0000fee3, 21843 }, /* Pointer_Down */ + { 0x0000fee4, 22036 }, /* Pointer_UpLeft */ + { 0x0000fee5, 22051 }, /* Pointer_UpRight */ + { 0x0000fee6, 21856 }, /* Pointer_DownLeft */ + { 0x0000fee7, 21873 }, /* Pointer_DownRight */ + { 0x0000fee8, 21671 }, /* Pointer_Button_Dflt */ + { 0x0000fee9, 21591 }, /* Pointer_Button1 */ + { 0x0000feea, 21607 }, /* Pointer_Button2 */ + { 0x0000feeb, 21623 }, /* Pointer_Button3 */ + { 0x0000feec, 21639 }, /* Pointer_Button4 */ + { 0x0000feed, 21655 }, /* Pointer_Button5 */ + { 0x0000feee, 21781 }, /* Pointer_DblClick_Dflt */ + { 0x0000feef, 21691 }, /* Pointer_DblClick1 */ + { 0x0000fef0, 21709 }, /* Pointer_DblClick2 */ + { 0x0000fef1, 21727 }, /* Pointer_DblClick3 */ + { 0x0000fef2, 21745 }, /* Pointer_DblClick4 */ + { 0x0000fef3, 21763 }, /* Pointer_DblClick5 */ + { 0x0000fef4, 21961 }, /* Pointer_Drag_Dflt */ + { 0x0000fef5, 21891 }, /* Pointer_Drag1 */ + { 0x0000fef6, 21905 }, /* Pointer_Drag2 */ + { 0x0000fef7, 21919 }, /* Pointer_Drag3 */ + { 0x0000fef8, 21933 }, /* Pointer_Drag4 */ + { 0x0000fef9, 21979 }, /* Pointer_EnableKeys */ + { 0x0000fefa, 21572 }, /* Pointer_Accelerate */ + { 0x0000fefb, 21803 }, /* Pointer_DfltBtnNext */ + { 0x0000fefc, 21823 }, /* Pointer_DfltBtnPrev */ + { 0x0000fefd, 21947 }, /* Pointer_Drag5 */ + { 0x0000ff08, 3362 }, /* BackSpace */ + { 0x0000ff09, 24298 }, /* Tab */ + { 0x0000ff0a, 19434 }, /* Linefeed */ + { 0x0000ff0b, 8633 }, /* Clear */ + { 0x0000ff0d, 22366 }, /* Return */ + { 0x0000ff13, 21481 }, /* Pause */ + { 0x0000ff14, 22699 }, /* Scroll_Lock */ + { 0x0000ff15, 24279 }, /* Sys_Req */ + { 0x0000ff1b, 12057 }, /* Escape */ + { 0x0000ff20, 19811 }, /* Multi_key */ + { 0x0000ff21, 18745 }, /* Kanji */ + { 0x0000ff22, 19802 }, /* Muhenkan */ + { 0x0000ff23, 16614 }, /* Henkan_Mode */ + { 0x0000ff24, 22562 }, /* Romaji */ + { 0x0000ff25, 16635 }, /* Hiragana */ + { 0x0000ff26, 18770 }, /* Katakana */ + { 0x0000ff27, 16644 }, /* Hiragana_Katakana */ + { 0x0000ff28, 29271 }, /* Zenkaku */ + { 0x0000ff29, 16048 }, /* Hankaku */ + { 0x0000ff2a, 29279 }, /* Zenkaku_Hankaku */ + { 0x0000ff2b, 25752 }, /* Touroku */ + { 0x0000ff2c, 19698 }, /* Massyo */ + { 0x0000ff2d, 18351 }, /* Kana_Lock */ + { 0x0000ff2e, 18562 }, /* Kana_Shift */ + { 0x0000ff2f, 11779 }, /* Eisu_Shift */ + { 0x0000ff30, 11790 }, /* Eisu_toggle */ + { 0x0000ff31, 14392 }, /* Hangul */ + { 0x0000ff32, 15780 }, /* Hangul_Start */ + { 0x0000ff33, 14511 }, /* Hangul_End */ + { 0x0000ff34, 14542 }, /* Hangul_Hanja */ + { 0x0000ff35, 15128 }, /* Hangul_Jamo */ + { 0x0000ff36, 15625 }, /* Hangul_Romaja */ + { 0x0000ff37, 8654 }, /* Codeinput */ + { 0x0000ff38, 15140 }, /* Hangul_Jeonja */ + { 0x0000ff39, 14445 }, /* Hangul_Banja */ + { 0x0000ff3a, 15420 }, /* Hangul_PreHanja */ + { 0x0000ff3b, 15403 }, /* Hangul_PostHanja */ + { 0x0000ff3c, 23012 }, /* SingleCandidate */ + { 0x0000ff3d, 19821 }, /* MultipleCandidate */ + { 0x0000ff3e, 22100 }, /* PreviousCandidate */ + { 0x0000ff3f, 15674 }, /* Hangul_Special */ + { 0x0000ff50, 16662 }, /* Home */ + { 0x0000ff51, 19244 }, /* Left */ + { 0x0000ff52, 26384 }, /* Up */ + { 0x0000ff53, 22373 }, /* Right */ + { 0x0000ff54, 11327 }, /* Down */ + { 0x0000ff55, 22124 }, /* Prior */ + { 0x0000ff56, 20023 }, /* Next */ + { 0x0000ff57, 11936 }, /* End */ + { 0x0000ff58, 3404 }, /* Begin */ + { 0x0000ff60, 22727 }, /* Select */ + { 0x0000ff61, 22118 }, /* Print */ + { 0x0000ff62, 12117 }, /* Execute */ + { 0x0000ff63, 17284 }, /* Insert */ + { 0x0000ff65, 26357 }, /* Undo */ + { 0x0000ff66, 22326 }, /* Redo */ + { 0x0000ff67, 19705 }, /* Menu */ + { 0x0000ff68, 12531 }, /* Find */ + { 0x0000ff69, 8489 }, /* Cancel */ + { 0x0000ff6a, 16602 }, /* Help */ + { 0x0000ff6b, 8377 }, /* Break */ + { 0x0000ff7e, 19747 }, /* Mode_switch */ + { 0x0000ff7f, 20169 }, /* Num_Lock */ + { 0x0000ff80, 19074 }, /* KP_Space */ + { 0x0000ff89, 19095 }, /* KP_Tab */ + { 0x0000ff8d, 18931 }, /* KP_Enter */ + { 0x0000ff91, 18949 }, /* KP_F1 */ + { 0x0000ff92, 18955 }, /* KP_F2 */ + { 0x0000ff93, 18961 }, /* KP_F3 */ + { 0x0000ff94, 18967 }, /* KP_F4 */ + { 0x0000ff95, 18973 }, /* KP_Home */ + { 0x0000ff96, 18991 }, /* KP_Left */ + { 0x0000ff97, 19102 }, /* KP_Up */ + { 0x0000ff98, 19052 }, /* KP_Right */ + { 0x0000ff99, 18916 }, /* KP_Down */ + { 0x0000ff9a, 19043 }, /* KP_Prior */ + { 0x0000ff9b, 19011 }, /* KP_Next */ + { 0x0000ff9c, 18924 }, /* KP_End */ + { 0x0000ff9d, 18876 }, /* KP_Begin */ + { 0x0000ff9e, 18981 }, /* KP_Insert */ + { 0x0000ff9f, 18896 }, /* KP_Delete */ + { 0x0000ffaa, 18999 }, /* KP_Multiply */ + { 0x0000ffab, 18858 }, /* KP_Add */ + { 0x0000ffac, 19061 }, /* KP_Separator */ + { 0x0000ffad, 19083 }, /* KP_Subtract */ + { 0x0000ffae, 18885 }, /* KP_Decimal */ + { 0x0000ffaf, 18906 }, /* KP_Divide */ + { 0x0000ffb0, 18808 }, /* KP_0 */ + { 0x0000ffb1, 18813 }, /* KP_1 */ + { 0x0000ffb2, 18818 }, /* KP_2 */ + { 0x0000ffb3, 18823 }, /* KP_3 */ + { 0x0000ffb4, 18828 }, /* KP_4 */ + { 0x0000ffb5, 18833 }, /* KP_5 */ + { 0x0000ffb6, 18838 }, /* KP_6 */ + { 0x0000ffb7, 18843 }, /* KP_7 */ + { 0x0000ffb8, 18848 }, /* KP_8 */ + { 0x0000ffb9, 18853 }, /* KP_9 */ + { 0x0000ffbd, 18940 }, /* KP_Equal */ + { 0x0000ffbe, 12159 }, /* F1 */ + { 0x0000ffbf, 12202 }, /* F2 */ + { 0x0000ffc0, 12245 }, /* F3 */ + { 0x0000ffc1, 12272 }, /* F4 */ + { 0x0000ffc2, 12275 }, /* F5 */ + { 0x0000ffc3, 12278 }, /* F6 */ + { 0x0000ffc4, 12281 }, /* F7 */ + { 0x0000ffc5, 12284 }, /* F8 */ + { 0x0000ffc6, 12287 }, /* F9 */ + { 0x0000ffc7, 12162 }, /* F10 */ + { 0x0000ffc8, 12166 }, /* F11 */ + { 0x0000ffc9, 12170 }, /* F12 */ + { 0x0000ffca, 12174 }, /* F13 */ + { 0x0000ffcb, 12178 }, /* F14 */ + { 0x0000ffcc, 12182 }, /* F15 */ + { 0x0000ffcd, 12186 }, /* F16 */ + { 0x0000ffce, 12190 }, /* F17 */ + { 0x0000ffcf, 12194 }, /* F18 */ + { 0x0000ffd0, 12198 }, /* F19 */ + { 0x0000ffd1, 12205 }, /* F20 */ + { 0x0000ffd2, 12209 }, /* F21 */ + { 0x0000ffd3, 12213 }, /* F22 */ + { 0x0000ffd4, 12217 }, /* F23 */ + { 0x0000ffd5, 12221 }, /* F24 */ + { 0x0000ffd6, 12225 }, /* F25 */ + { 0x0000ffd7, 12229 }, /* F26 */ + { 0x0000ffd8, 12233 }, /* F27 */ + { 0x0000ffd9, 12237 }, /* F28 */ + { 0x0000ffda, 12241 }, /* F29 */ + { 0x0000ffdb, 12248 }, /* F30 */ + { 0x0000ffdc, 12252 }, /* F31 */ + { 0x0000ffdd, 12256 }, /* F32 */ + { 0x0000ffde, 12260 }, /* F33 */ + { 0x0000ffdf, 12264 }, /* F34 */ + { 0x0000ffe0, 12268 }, /* F35 */ + { 0x0000ffe1, 22946 }, /* Shift_L */ + { 0x0000ffe2, 22965 }, /* Shift_R */ + { 0x0000ffe3, 8697 }, /* Control_L */ + { 0x0000ffe4, 8707 }, /* Control_R */ + { 0x0000ffe5, 8496 }, /* Caps_Lock */ + { 0x0000ffe6, 22954 }, /* Shift_Lock */ + { 0x0000ffe7, 19710 }, /* Meta_L */ + { 0x0000ffe8, 19717 }, /* Meta_R */ + { 0x0000ffe9, 880 }, /* Alt_L */ + { 0x0000ffea, 886 }, /* Alt_R */ + { 0x0000ffeb, 24263 }, /* Super_L */ + { 0x0000ffec, 24271 }, /* Super_R */ + { 0x0000ffed, 17046 }, /* Hyper_L */ + { 0x0000ffee, 17054 }, /* Hyper_R */ + { 0x0000fff1, 3642 }, /* braille_dot_1 */ + { 0x0000fff2, 3671 }, /* braille_dot_2 */ + { 0x0000fff3, 3685 }, /* braille_dot_3 */ + { 0x0000fff4, 3699 }, /* braille_dot_4 */ + { 0x0000fff5, 3713 }, /* braille_dot_5 */ + { 0x0000fff6, 3727 }, /* braille_dot_6 */ + { 0x0000fff7, 3741 }, /* braille_dot_7 */ + { 0x0000fff8, 3755 }, /* braille_dot_8 */ + { 0x0000fff9, 3769 }, /* braille_dot_9 */ + { 0x0000fffa, 3656 }, /* braille_dot_10 */ + { 0x0000ffff, 11160 }, /* Delete */ + { 0x00ffffff, 26531 }, /* VoidSymbol */ + { 0x0100012c, 17117 }, /* Ibreve */ + { 0x0100012d, 17124 }, /* ibreve */ + { 0x01000174, 26563 }, /* Wcircumflex */ + { 0x01000175, 26575 }, /* wcircumflex */ + { 0x01000176, 29120 }, /* Ycircumflex */ + { 0x01000177, 29132 }, /* ycircumflex */ + { 0x0100018f, 22649 }, /* SCHWA */ + { 0x0100019f, 20218 }, /* Obarred */ + { 0x010001a0, 20553 }, /* Ohorn */ + { 0x010001a1, 20559 }, /* ohorn */ + { 0x010001af, 25997 }, /* Uhorn */ + { 0x010001b0, 26003 }, /* uhorn */ + { 0x010001b5, 29322 }, /* Zstroke */ + { 0x010001b6, 29330 }, /* zstroke */ + { 0x010001b7, 12147 }, /* EZH */ + { 0x010001d1, 20254 }, /* Ocaron */ + { 0x010001d2, 20261 }, /* ocaron */ + { 0x010001e6, 12703 }, /* Gcaron */ + { 0x010001e7, 12710 }, /* gcaron */ + { 0x01000259, 22655 }, /* schwa */ + { 0x01000275, 20226 }, /* obarred */ + { 0x01000292, 12151 }, /* ezh */ + { 0x01000492, 9236 }, /* Cyrillic_GHE_bar */ + { 0x01000493, 9253 }, /* Cyrillic_ghe_bar */ + { 0x01000496, 10274 }, /* Cyrillic_ZHE_descender */ + { 0x01000497, 10297 }, /* Cyrillic_zhe_descender */ + { 0x0100049a, 9528 }, /* Cyrillic_KA_descender */ + { 0x0100049b, 9550 }, /* Cyrillic_ka_descender */ + { 0x0100049c, 9572 }, /* Cyrillic_KA_vertstroke */ + { 0x0100049d, 9595 }, /* Cyrillic_ka_vertstroke */ + { 0x010004a2, 9118 }, /* Cyrillic_EN_descender */ + { 0x010004a3, 9140 }, /* Cyrillic_en_descender */ + { 0x010004ae, 10036 }, /* Cyrillic_U_straight */ + { 0x010004af, 10056 }, /* Cyrillic_u_straight */ + { 0x010004b0, 10076 }, /* Cyrillic_U_straight_bar */ + { 0x010004b1, 10100 }, /* Cyrillic_u_straight_bar */ + { 0x010004b2, 9294 }, /* Cyrillic_HA_descender */ + { 0x010004b3, 9316 }, /* Cyrillic_ha_descender */ + { 0x010004b6, 8854 }, /* Cyrillic_CHE_descender */ + { 0x010004b7, 8877 }, /* Cyrillic_che_descender */ + { 0x010004b8, 8900 }, /* Cyrillic_CHE_vertstroke */ + { 0x010004b9, 8924 }, /* Cyrillic_che_vertstroke */ + { 0x010004ba, 9832 }, /* Cyrillic_SHHA */ + { 0x010004bb, 9846 }, /* Cyrillic_shha */ + { 0x010004d8, 9746 }, /* Cyrillic_SCHWA */ + { 0x010004d9, 9761 }, /* Cyrillic_schwa */ + { 0x010004e2, 9396 }, /* Cyrillic_I_macron */ + { 0x010004e3, 9414 }, /* Cyrillic_i_macron */ + { 0x010004e8, 9692 }, /* Cyrillic_O_bar */ + { 0x010004e9, 9707 }, /* Cyrillic_o_bar */ + { 0x010004ee, 10000 }, /* Cyrillic_U_macron */ + { 0x010004ef, 10018 }, /* Cyrillic_u_macron */ + { 0x01000531, 2124 }, /* Armenian_AYB */ + { 0x01000532, 2150 }, /* Armenian_BEN */ + { 0x01000533, 2374 }, /* Armenian_GIM */ + { 0x01000534, 2215 }, /* Armenian_DA */ + { 0x01000535, 3164 }, /* Armenian_YECH */ + { 0x01000536, 3210 }, /* Armenian_ZA */ + { 0x01000537, 2265 }, /* Armenian_E */ + { 0x01000538, 2100 }, /* Armenian_AT */ + { 0x01000539, 2964 }, /* Armenian_TO */ + { 0x0100053a, 3234 }, /* Armenian_ZHE */ + { 0x0100053b, 2464 }, /* Armenian_INI */ + { 0x0100053c, 2611 }, /* Armenian_LYUN */ + { 0x0100053d, 2564 }, /* Armenian_KHE */ + { 0x0100053e, 2988 }, /* Armenian_TSA */ + { 0x0100053f, 2538 }, /* Armenian_KEN */ + { 0x01000540, 2424 }, /* Armenian_HO */ + { 0x01000541, 2239 }, /* Armenian_DZA */ + { 0x01000542, 2346 }, /* Armenian_GHAT */ + { 0x01000543, 2936 }, /* Armenian_TCHE */ + { 0x01000544, 2639 }, /* Armenian_MEN */ + { 0x01000545, 2400 }, /* Armenian_HI */ + { 0x01000546, 2665 }, /* Armenian_NU */ + { 0x01000547, 2894 }, /* Armenian_SHA */ + { 0x01000548, 3112 }, /* Armenian_VO */ + { 0x01000549, 2189 }, /* Armenian_CHA */ + { 0x0100054a, 2727 }, /* Armenian_PE */ + { 0x0100054b, 2490 }, /* Armenian_JE */ + { 0x0100054c, 2797 }, /* Armenian_RA */ + { 0x0100054d, 2845 }, /* Armenian_SE */ + { 0x0100054e, 3086 }, /* Armenian_VEV */ + { 0x0100054f, 3040 }, /* Armenian_TYUN */ + { 0x01000550, 2821 }, /* Armenian_RE */ + { 0x01000551, 3014 }, /* Armenian_TSO */ + { 0x01000552, 3136 }, /* Armenian_VYUN */ + { 0x01000553, 2751 }, /* Armenian_PYUR */ + { 0x01000554, 2514 }, /* Armenian_KE */ + { 0x01000555, 2689 }, /* Armenian_O */ + { 0x01000556, 2303 }, /* Armenian_FE */ + { 0x0100055a, 2080 }, /* Armenian_apostrophe */ + { 0x0100055b, 2048 }, /* Armenian_accent */ + { 0x0100055c, 2287 }, /* Armenian_exclam */ + { 0x0100055d, 2869 }, /* Armenian_separation_mark */ + { 0x0100055e, 2779 }, /* Armenian_question */ + { 0x01000561, 2137 }, /* Armenian_ayb */ + { 0x01000562, 2163 }, /* Armenian_ben */ + { 0x01000563, 2387 }, /* Armenian_gim */ + { 0x01000564, 2227 }, /* Armenian_da */ + { 0x01000565, 3178 }, /* Armenian_yech */ + { 0x01000566, 3222 }, /* Armenian_za */ + { 0x01000567, 2276 }, /* Armenian_e */ + { 0x01000568, 2112 }, /* Armenian_at */ + { 0x01000569, 2976 }, /* Armenian_to */ + { 0x0100056a, 3247 }, /* Armenian_zhe */ + { 0x0100056b, 2477 }, /* Armenian_ini */ + { 0x0100056c, 2625 }, /* Armenian_lyun */ + { 0x0100056d, 2577 }, /* Armenian_khe */ + { 0x0100056e, 3001 }, /* Armenian_tsa */ + { 0x0100056f, 2551 }, /* Armenian_ken */ + { 0x01000570, 2436 }, /* Armenian_ho */ + { 0x01000571, 2252 }, /* Armenian_dza */ + { 0x01000572, 2360 }, /* Armenian_ghat */ + { 0x01000573, 2950 }, /* Armenian_tche */ + { 0x01000574, 2652 }, /* Armenian_men */ + { 0x01000575, 2412 }, /* Armenian_hi */ + { 0x01000576, 2677 }, /* Armenian_nu */ + { 0x01000577, 2907 }, /* Armenian_sha */ + { 0x01000578, 3124 }, /* Armenian_vo */ + { 0x01000579, 2202 }, /* Armenian_cha */ + { 0x0100057a, 2739 }, /* Armenian_pe */ + { 0x0100057b, 2502 }, /* Armenian_je */ + { 0x0100057c, 2809 }, /* Armenian_ra */ + { 0x0100057d, 2857 }, /* Armenian_se */ + { 0x0100057e, 3099 }, /* Armenian_vev */ + { 0x0100057f, 3054 }, /* Armenian_tyun */ + { 0x01000580, 2833 }, /* Armenian_re */ + { 0x01000581, 3027 }, /* Armenian_tso */ + { 0x01000582, 3150 }, /* Armenian_vyun */ + { 0x01000583, 2765 }, /* Armenian_pyur */ + { 0x01000584, 2526 }, /* Armenian_ke */ + { 0x01000585, 2700 }, /* Armenian_o */ + { 0x01000586, 2315 }, /* Armenian_fe */ + { 0x01000587, 2590 }, /* Armenian_ligature_ew */ + { 0x01000589, 2327 }, /* Armenian_full_stop */ + { 0x0100058a, 2448 }, /* Armenian_hyphen */ + { 0x01000653, 1580 }, /* Arabic_madda_above */ + { 0x01000654, 1316 }, /* Arabic_hamza_above */ + { 0x01000655, 1335 }, /* Arabic_hamza_below */ + { 0x01000660, 966 }, /* Arabic_0 */ + { 0x01000661, 975 }, /* Arabic_1 */ + { 0x01000662, 984 }, /* Arabic_2 */ + { 0x01000663, 993 }, /* Arabic_3 */ + { 0x01000664, 1002 }, /* Arabic_4 */ + { 0x01000665, 1011 }, /* Arabic_5 */ + { 0x01000666, 1020 }, /* Arabic_6 */ + { 0x01000667, 1029 }, /* Arabic_7 */ + { 0x01000668, 1038 }, /* Arabic_8 */ + { 0x01000669, 1047 }, /* Arabic_9 */ + { 0x0100066a, 1672 }, /* Arabic_percent */ + { 0x01000670, 1821 }, /* Arabic_superscript_alef */ + { 0x01000679, 1951 }, /* Arabic_tteh */ + { 0x0100067e, 1661 }, /* Arabic_peh */ + { 0x01000686, 1885 }, /* Arabic_tcheh */ + { 0x01000688, 1173 }, /* Arabic_ddal */ + { 0x01000691, 1729 }, /* Arabic_rreh */ + { 0x01000698, 1493 }, /* Arabic_jeh */ + { 0x010006a4, 1963 }, /* Arabic_veh */ + { 0x010006a9, 1544 }, /* Arabic_keheh */ + { 0x010006af, 1258 }, /* Arabic_gaf */ + { 0x010006ba, 1642 }, /* Arabic_noon_ghunna */ + { 0x010006be, 1442 }, /* Arabic_heh_doachashmee */ + { 0x010006c1, 1465 }, /* Arabic_heh_goal */ + { 0x010006cc, 12390 }, /* Farsi_yeh */ + { 0x010006d2, 1996 }, /* Arabic_yeh_baree */ + { 0x010006d4, 1242 }, /* Arabic_fullstop */ + { 0x010006f0, 12310 }, /* Farsi_0 */ + { 0x010006f1, 12318 }, /* Farsi_1 */ + { 0x010006f2, 12326 }, /* Farsi_2 */ + { 0x010006f3, 12334 }, /* Farsi_3 */ + { 0x010006f4, 12342 }, /* Farsi_4 */ + { 0x010006f5, 12350 }, /* Farsi_5 */ + { 0x010006f6, 12358 }, /* Farsi_6 */ + { 0x010006f7, 12366 }, /* Farsi_7 */ + { 0x010006f8, 12374 }, /* Farsi_8 */ + { 0x010006f9, 12382 }, /* Farsi_9 */ + { 0x01000d82, 23484 }, /* Sinh_ng */ + { 0x01000d83, 23278 }, /* Sinh_h2 */ + { 0x01000d85, 23047 }, /* Sinh_a */ + { 0x01000d86, 23054 }, /* Sinh_aa */ + { 0x01000d87, 23071 }, /* Sinh_ae */ + { 0x01000d88, 23088 }, /* Sinh_aee */ + { 0x01000d89, 23294 }, /* Sinh_i */ + { 0x01000d8a, 23309 }, /* Sinh_ii */ + { 0x01000d8b, 23695 }, /* Sinh_u */ + { 0x01000d8c, 23710 }, /* Sinh_uu */ + { 0x01000d8d, 23594 }, /* Sinh_ri */ + { 0x01000d8e, 23602 }, /* Sinh_rii */ + { 0x01000d8f, 23403 }, /* Sinh_lu */ + { 0x01000d90, 23420 }, /* Sinh_luu */ + { 0x01000d91, 23221 }, /* Sinh_e */ + { 0x01000d92, 23236 }, /* Sinh_ee */ + { 0x01000d93, 23107 }, /* Sinh_ai */ + { 0x01000d94, 23537 }, /* Sinh_o */ + { 0x01000d95, 23552 }, /* Sinh_oo */ + { 0x01000d96, 23132 }, /* Sinh_au */ + { 0x01000d9a, 23353 }, /* Sinh_ka */ + { 0x01000d9b, 23361 }, /* Sinh_kha */ + { 0x01000d9c, 23261 }, /* Sinh_ga */ + { 0x01000d9d, 23269 }, /* Sinh_gha */ + { 0x01000d9e, 23492 }, /* Sinh_ng2 */ + { 0x01000d9f, 23501 }, /* Sinh_nga */ + { 0x01000da0, 23166 }, /* Sinh_ca */ + { 0x01000da1, 23174 }, /* Sinh_cha */ + { 0x01000da2, 23326 }, /* Sinh_ja */ + { 0x01000da3, 23334 }, /* Sinh_jha */ + { 0x01000da4, 23528 }, /* Sinh_nya */ + { 0x01000da5, 23343 }, /* Sinh_jnya */ + { 0x01000da6, 23510 }, /* Sinh_nja */ + { 0x01000da7, 23676 }, /* Sinh_tta */ + { 0x01000da8, 23685 }, /* Sinh_ttha */ + { 0x01000da9, 23183 }, /* Sinh_dda */ + { 0x01000daa, 23192 }, /* Sinh_ddha */ + { 0x01000dab, 23519 }, /* Sinh_nna */ + { 0x01000dac, 23464 }, /* Sinh_ndda */ + { 0x01000dad, 23657 }, /* Sinh_tha */ + { 0x01000dae, 23666 }, /* Sinh_thha */ + { 0x01000daf, 23202 }, /* Sinh_dha */ + { 0x01000db0, 23211 }, /* Sinh_dhha */ + { 0x01000db1, 23456 }, /* Sinh_na */ + { 0x01000db3, 23474 }, /* Sinh_ndha */ + { 0x01000db4, 23569 }, /* Sinh_pa */ + { 0x01000db5, 23577 }, /* Sinh_pha */ + { 0x01000db6, 23149 }, /* Sinh_ba */ + { 0x01000db7, 23157 }, /* Sinh_bha */ + { 0x01000db8, 23439 }, /* Sinh_ma */ + { 0x01000db9, 23447 }, /* Sinh_mba */ + { 0x01000dba, 23735 }, /* Sinh_ya */ + { 0x01000dbb, 23586 }, /* Sinh_ra */ + { 0x01000dbd, 23386 }, /* Sinh_la */ + { 0x01000dc0, 23727 }, /* Sinh_va */ + { 0x01000dc1, 23638 }, /* Sinh_sha */ + { 0x01000dc2, 23647 }, /* Sinh_ssha */ + { 0x01000dc3, 23630 }, /* Sinh_sa */ + { 0x01000dc4, 23286 }, /* Sinh_ha */ + { 0x01000dc5, 23394 }, /* Sinh_lla */ + { 0x01000dc6, 23253 }, /* Sinh_fa */ + { 0x01000dca, 23124 }, /* Sinh_al */ + { 0x01000dcf, 23062 }, /* Sinh_aa2 */ + { 0x01000dd0, 23079 }, /* Sinh_ae2 */ + { 0x01000dd1, 23097 }, /* Sinh_aee2 */ + { 0x01000dd2, 23301 }, /* Sinh_i2 */ + { 0x01000dd3, 23317 }, /* Sinh_ii2 */ + { 0x01000dd4, 23702 }, /* Sinh_u2 */ + { 0x01000dd6, 23718 }, /* Sinh_uu2 */ + { 0x01000dd8, 23611 }, /* Sinh_ru2 */ + { 0x01000dd9, 23228 }, /* Sinh_e2 */ + { 0x01000dda, 23244 }, /* Sinh_ee2 */ + { 0x01000ddb, 23115 }, /* Sinh_ai2 */ + { 0x01000ddc, 23544 }, /* Sinh_o2 */ + { 0x01000ddd, 23560 }, /* Sinh_oo2 */ + { 0x01000dde, 23140 }, /* Sinh_au2 */ + { 0x01000ddf, 23411 }, /* Sinh_lu2 */ + { 0x01000df2, 23620 }, /* Sinh_ruu2 */ + { 0x01000df3, 23429 }, /* Sinh_luu2 */ + { 0x01000df4, 23370 }, /* Sinh_kunddaliya */ + { 0x010010d0, 12759 }, /* Georgian_an */ + { 0x010010d1, 12771 }, /* Georgian_ban */ + { 0x010010d2, 12875 }, /* Georgian_gan */ + { 0x010010d3, 12838 }, /* Georgian_don */ + { 0x010010d4, 12851 }, /* Georgian_en */ + { 0x010010d5, 13201 }, /* Georgian_vin */ + { 0x010010d6, 13239 }, /* Georgian_zen */ + { 0x010010d7, 13163 }, /* Georgian_tan */ + { 0x010010d8, 12966 }, /* Georgian_in */ + { 0x010010d9, 13005 }, /* Georgian_kan */ + { 0x010010da, 13032 }, /* Georgian_las */ + { 0x010010db, 13045 }, /* Georgian_man */ + { 0x010010dc, 13058 }, /* Georgian_nar */ + { 0x010010dd, 13071 }, /* Georgian_on */ + { 0x010010de, 13083 }, /* Georgian_par */ + { 0x010010df, 13252 }, /* Georgian_zhar */ + { 0x010010e0, 13123 }, /* Georgian_rae */ + { 0x010010e1, 13136 }, /* Georgian_san */ + { 0x010010e2, 13176 }, /* Georgian_tar */ + { 0x010010e3, 13189 }, /* Georgian_un */ + { 0x010010e4, 13096 }, /* Georgian_phar */ + { 0x010010e5, 13018 }, /* Georgian_khar */ + { 0x010010e6, 12888 }, /* Georgian_ghan */ + { 0x010010e7, 13110 }, /* Georgian_qar */ + { 0x010010e8, 13149 }, /* Georgian_shin */ + { 0x010010e9, 12811 }, /* Georgian_chin */ + { 0x010010ea, 12784 }, /* Georgian_can */ + { 0x010010eb, 12992 }, /* Georgian_jil */ + { 0x010010ec, 12825 }, /* Georgian_cil */ + { 0x010010ed, 12797 }, /* Georgian_char */ + { 0x010010ee, 13226 }, /* Georgian_xan */ + { 0x010010ef, 12978 }, /* Georgian_jhan */ + { 0x010010f0, 12902 }, /* Georgian_hae */ + { 0x010010f1, 12928 }, /* Georgian_he */ + { 0x010010f2, 12940 }, /* Georgian_hie */ + { 0x010010f3, 13214 }, /* Georgian_we */ + { 0x010010f4, 12915 }, /* Georgian_har */ + { 0x010010f5, 12953 }, /* Georgian_hoe */ + { 0x010010f6, 12863 }, /* Georgian_fi */ + { 0x01001e02, 3332 }, /* Babovedot */ + { 0x01001e03, 3342 }, /* babovedot */ + { 0x01001e0a, 10324 }, /* Dabovedot */ + { 0x01001e0b, 10334 }, /* dabovedot */ + { 0x01001e1e, 12290 }, /* Fabovedot */ + { 0x01001e1f, 12300 }, /* fabovedot */ + { 0x01001e36, 19192 }, /* Lbelowdot */ + { 0x01001e37, 19202 }, /* lbelowdot */ + { 0x01001e40, 19537 }, /* Mabovedot */ + { 0x01001e41, 19547 }, /* mabovedot */ + { 0x01001e56, 21377 }, /* Pabovedot */ + { 0x01001e57, 21387 }, /* pabovedot */ + { 0x01001e60, 22583 }, /* Sabovedot */ + { 0x01001e61, 22593 }, /* sabovedot */ + { 0x01001e6a, 24302 }, /* Tabovedot */ + { 0x01001e6b, 24312 }, /* tabovedot */ + { 0x01001e80, 26609 }, /* Wgrave */ + { 0x01001e81, 26616 }, /* wgrave */ + { 0x01001e82, 26549 }, /* Wacute */ + { 0x01001e83, 26556 }, /* wacute */ + { 0x01001e84, 26587 }, /* Wdiaeresis */ + { 0x01001e85, 26598 }, /* wdiaeresis */ + { 0x01001e8a, 26635 }, /* Xabovedot */ + { 0x01001e8b, 26645 }, /* xabovedot */ + { 0x01001ea0, 416 }, /* Abelowdot */ + { 0x01001ea1, 426 }, /* abelowdot */ + { 0x01001ea2, 868 }, /* Ahook */ + { 0x01001ea3, 874 }, /* ahook */ + { 0x01001ea4, 646 }, /* Acircumflexacute */ + { 0x01001ea5, 663 }, /* acircumflexacute */ + { 0x01001ea6, 720 }, /* Acircumflexgrave */ + { 0x01001ea7, 737 }, /* acircumflexgrave */ + { 0x01001ea8, 754 }, /* Acircumflexhook */ + { 0x01001ea9, 770 }, /* acircumflexhook */ + { 0x01001eaa, 786 }, /* Acircumflextilde */ + { 0x01001eab, 803 }, /* acircumflextilde */ + { 0x01001eac, 680 }, /* Acircumflexbelowdot */ + { 0x01001ead, 700 }, /* acircumflexbelowdot */ + { 0x01001eae, 459 }, /* Abreveacute */ + { 0x01001eaf, 471 }, /* abreveacute */ + { 0x01001eb0, 513 }, /* Abrevegrave */ + { 0x01001eb1, 525 }, /* abrevegrave */ + { 0x01001eb2, 537 }, /* Abrevehook */ + { 0x01001eb3, 548 }, /* abrevehook */ + { 0x01001eb4, 559 }, /* Abrevetilde */ + { 0x01001eb5, 571 }, /* abrevetilde */ + { 0x01001eb6, 483 }, /* Abrevebelowdot */ + { 0x01001eb7, 498 }, /* abrevebelowdot */ + { 0x01001eb8, 11462 }, /* Ebelowdot */ + { 0x01001eb9, 11472 }, /* ebelowdot */ + { 0x01001eba, 11738 }, /* Ehook */ + { 0x01001ebb, 11744 }, /* ehook */ + { 0x01001ebc, 12076 }, /* Etilde */ + { 0x01001ebd, 12083 }, /* etilde */ + { 0x01001ebe, 11520 }, /* Ecircumflexacute */ + { 0x01001ebf, 11537 }, /* ecircumflexacute */ + { 0x01001ec0, 11594 }, /* Ecircumflexgrave */ + { 0x01001ec1, 11611 }, /* ecircumflexgrave */ + { 0x01001ec2, 11628 }, /* Ecircumflexhook */ + { 0x01001ec3, 11644 }, /* ecircumflexhook */ + { 0x01001ec4, 11660 }, /* Ecircumflextilde */ + { 0x01001ec5, 11677 }, /* ecircumflextilde */ + { 0x01001ec6, 11554 }, /* Ecircumflexbelowdot */ + { 0x01001ec7, 11574 }, /* ecircumflexbelowdot */ + { 0x01001ec8, 17219 }, /* Ihook */ + { 0x01001ec9, 17225 }, /* ihook */ + { 0x01001eca, 17097 }, /* Ibelowdot */ + { 0x01001ecb, 17107 }, /* ibelowdot */ + { 0x01001ecc, 20234 }, /* Obelowdot */ + { 0x01001ecd, 20244 }, /* obelowdot */ + { 0x01001ece, 20541 }, /* Ohook */ + { 0x01001ecf, 20547 }, /* ohook */ + { 0x01001ed0, 20292 }, /* Ocircumflexacute */ + { 0x01001ed1, 20309 }, /* ocircumflexacute */ + { 0x01001ed2, 20366 }, /* Ocircumflexgrave */ + { 0x01001ed3, 20383 }, /* ocircumflexgrave */ + { 0x01001ed4, 20400 }, /* Ocircumflexhook */ + { 0x01001ed5, 20416 }, /* ocircumflexhook */ + { 0x01001ed6, 20432 }, /* Ocircumflextilde */ + { 0x01001ed7, 20449 }, /* ocircumflextilde */ + { 0x01001ed8, 20326 }, /* Ocircumflexbelowdot */ + { 0x01001ed9, 20346 }, /* ocircumflexbelowdot */ + { 0x01001eda, 20565 }, /* Ohornacute */ + { 0x01001edb, 20576 }, /* ohornacute */ + { 0x01001edc, 20615 }, /* Ohorngrave */ + { 0x01001edd, 20626 }, /* ohorngrave */ + { 0x01001ede, 20637 }, /* Ohornhook */ + { 0x01001edf, 20647 }, /* ohornhook */ + { 0x01001ee0, 20657 }, /* Ohorntilde */ + { 0x01001ee1, 20668 }, /* ohorntilde */ + { 0x01001ee2, 20587 }, /* Ohornbelowdot */ + { 0x01001ee3, 20601 }, /* ohornbelowdot */ + { 0x01001ee4, 25865 }, /* Ubelowdot */ + { 0x01001ee5, 25875 }, /* ubelowdot */ + { 0x01001ee6, 25985 }, /* Uhook */ + { 0x01001ee7, 25991 }, /* uhook */ + { 0x01001ee8, 26009 }, /* Uhornacute */ + { 0x01001ee9, 26020 }, /* uhornacute */ + { 0x01001eea, 26059 }, /* Uhorngrave */ + { 0x01001eeb, 26070 }, /* uhorngrave */ + { 0x01001eec, 26081 }, /* Uhornhook */ + { 0x01001eed, 26091 }, /* uhornhook */ + { 0x01001eee, 26101 }, /* Uhorntilde */ + { 0x01001eef, 26112 }, /* uhorntilde */ + { 0x01001ef0, 26031 }, /* Uhornbelowdot */ + { 0x01001ef1, 26045 }, /* uhornbelowdot */ + { 0x01001ef2, 29170 }, /* Ygrave */ + { 0x01001ef3, 29177 }, /* ygrave */ + { 0x01001ef4, 29100 }, /* Ybelowdot */ + { 0x01001ef5, 29110 }, /* ybelowdot */ + { 0x01001ef6, 29184 }, /* Yhook */ + { 0x01001ef7, 29190 }, /* yhook */ + { 0x01001ef8, 29196 }, /* Ytilde */ + { 0x01001ef9, 29203 }, /* ytilde */ + { 0x01002070, 29309 }, /* zerosuperior */ + { 0x01002074, 12632 }, /* foursuperior */ + { 0x01002075, 12594 }, /* fivesuperior */ + { 0x01002076, 23756 }, /* sixsuperior */ + { 0x01002077, 22932 }, /* sevensuperior */ + { 0x01002078, 11765 }, /* eightsuperior */ + { 0x01002079, 20062 }, /* ninesuperior */ + { 0x01002080, 29295 }, /* zerosubscript */ + { 0x01002081, 20742 }, /* onesubscript */ + { 0x01002082, 25812 }, /* twosubscript */ + { 0x01002083, 25556 }, /* threesubscript */ + { 0x01002084, 12618 }, /* foursubscript */ + { 0x01002085, 12580 }, /* fivesubscript */ + { 0x01002086, 23743 }, /* sixsubscript */ + { 0x01002087, 22917 }, /* sevensubscript */ + { 0x01002088, 11750 }, /* eightsubscript */ + { 0x01002089, 20048 }, /* ninesubscript */ + { 0x010020a0, 11694 }, /* EcuSign */ + { 0x010020a1, 8670 }, /* ColonSign */ + { 0x010020a2, 8744 }, /* CruzeiroSign */ + { 0x010020a3, 12416 }, /* FFrancSign */ + { 0x010020a4, 19448 }, /* LiraSign */ + { 0x010020a5, 19724 }, /* MillSign */ + { 0x010020a6, 19967 }, /* NairaSign */ + { 0x010020a7, 21526 }, /* PesetaSign */ + { 0x010020a8, 22569 }, /* RupeeSign */ + { 0x010020a9, 26623 }, /* WonSign */ + { 0x010020aa, 20009 }, /* NewSheqelSign */ + { 0x010020ab, 11258 }, /* DongSign */ + { 0x01002202, 21446 }, /* partdifferential */ + { 0x01002205, 11919 }, /* emptyset */ + { 0x01002208, 11802 }, /* elementof */ + { 0x01002209, 20112 }, /* notelementof */ + { 0x0100220b, 8686 }, /* containsas */ + { 0x0100221a, 23809 }, /* squareroot */ + { 0x0100221b, 8757 }, /* cuberoot */ + { 0x0100221c, 12645 }, /* fourthroot */ + { 0x0100222c, 11232 }, /* dintegral */ + { 0x0100222d, 25585 }, /* tintegral */ + { 0x01002235, 3396 }, /* because */ + { 0x01002247, 20100 }, /* notapproxeq */ + { 0x01002248, 945 }, /* approxeq */ + { 0x01002262, 20134 }, /* notidentical */ + { 0x01002263, 23854 }, /* stricteq */ + { 0x01002800, 3628 }, /* braille_blank */ + { 0x01002801, 3783 }, /* braille_dots_1 */ + { 0x01002802, 6151 }, /* braille_dots_2 */ + { 0x01002803, 3798 }, /* braille_dots_12 */ + { 0x01002804, 7303 }, /* braille_dots_3 */ + { 0x01002805, 5014 }, /* braille_dots_13 */ + { 0x01002806, 6166 }, /* braille_dots_23 */ + { 0x01002807, 3814 }, /* braille_dots_123 */ + { 0x01002808, 7863 }, /* braille_dots_4 */ + { 0x01002809, 5606 }, /* braille_dots_14 */ + { 0x0100280a, 6758 }, /* braille_dots_24 */ + { 0x0100280b, 4438 }, /* braille_dots_124 */ + { 0x0100280c, 7318 }, /* braille_dots_34 */ + { 0x0100280d, 5030 }, /* braille_dots_134 */ + { 0x0100280e, 6182 }, /* braille_dots_234 */ + { 0x0100280f, 3831 }, /* braille_dots_1234 */ + { 0x01002810, 8135 }, /* braille_dots_5 */ + { 0x01002811, 5894 }, /* braille_dots_15 */ + { 0x01002812, 7046 }, /* braille_dots_25 */ + { 0x01002813, 4742 }, /* braille_dots_125 */ + { 0x01002814, 7606 }, /* braille_dots_35 */ + { 0x01002815, 5334 }, /* braille_dots_135 */ + { 0x01002816, 6486 }, /* braille_dots_235 */ + { 0x01002817, 4151 }, /* braille_dots_1235 */ + { 0x01002818, 7878 }, /* braille_dots_45 */ + { 0x01002819, 5622 }, /* braille_dots_145 */ + { 0x0100281a, 6774 }, /* braille_dots_245 */ + { 0x0100281b, 4455 }, /* braille_dots_1245 */ + { 0x0100281c, 7334 }, /* braille_dots_345 */ + { 0x0100281d, 5047 }, /* braille_dots_1345 */ + { 0x0100281e, 6199 }, /* braille_dots_2345 */ + { 0x0100281f, 3849 }, /* braille_dots_12345 */ + { 0x01002820, 8267 }, /* braille_dots_6 */ + { 0x01002821, 6034 }, /* braille_dots_16 */ + { 0x01002822, 7186 }, /* braille_dots_26 */ + { 0x01002823, 4890 }, /* braille_dots_126 */ + { 0x01002824, 7746 }, /* braille_dots_36 */ + { 0x01002825, 5482 }, /* braille_dots_136 */ + { 0x01002826, 6634 }, /* braille_dots_236 */ + { 0x01002827, 4307 }, /* braille_dots_1236 */ + { 0x01002828, 8018 }, /* braille_dots_46 */ + { 0x01002829, 5770 }, /* braille_dots_146 */ + { 0x0100282a, 6922 }, /* braille_dots_246 */ + { 0x0100282b, 4611 }, /* braille_dots_1246 */ + { 0x0100282c, 7482 }, /* braille_dots_346 */ + { 0x0100282d, 5203 }, /* braille_dots_1346 */ + { 0x0100282e, 6355 }, /* braille_dots_2346 */ + { 0x0100282f, 4013 }, /* braille_dots_12346 */ + { 0x01002830, 8150 }, /* braille_dots_56 */ + { 0x01002831, 5910 }, /* braille_dots_156 */ + { 0x01002832, 7062 }, /* braille_dots_256 */ + { 0x01002833, 4759 }, /* braille_dots_1256 */ + { 0x01002834, 7622 }, /* braille_dots_356 */ + { 0x01002835, 5351 }, /* braille_dots_1356 */ + { 0x01002836, 6503 }, /* braille_dots_2356 */ + { 0x01002837, 4169 }, /* braille_dots_12356 */ + { 0x01002838, 7894 }, /* braille_dots_456 */ + { 0x01002839, 5639 }, /* braille_dots_1456 */ + { 0x0100283a, 6791 }, /* braille_dots_2456 */ + { 0x0100283b, 4473 }, /* braille_dots_12456 */ + { 0x0100283c, 7351 }, /* braille_dots_3456 */ + { 0x0100283d, 5065 }, /* braille_dots_13456 */ + { 0x0100283e, 6217 }, /* braille_dots_23456 */ + { 0x0100283f, 3868 }, /* braille_dots_123456 */ + { 0x01002840, 8331 }, /* braille_dots_7 */ + { 0x01002841, 6102 }, /* braille_dots_17 */ + { 0x01002842, 7254 }, /* braille_dots_27 */ + { 0x01002843, 4962 }, /* braille_dots_127 */ + { 0x01002844, 7814 }, /* braille_dots_37 */ + { 0x01002845, 5554 }, /* braille_dots_137 */ + { 0x01002846, 6706 }, /* braille_dots_237 */ + { 0x01002847, 4383 }, /* braille_dots_1237 */ + { 0x01002848, 8086 }, /* braille_dots_47 */ + { 0x01002849, 5842 }, /* braille_dots_147 */ + { 0x0100284a, 6994 }, /* braille_dots_247 */ + { 0x0100284b, 4687 }, /* braille_dots_1247 */ + { 0x0100284c, 7554 }, /* braille_dots_347 */ + { 0x0100284d, 5279 }, /* braille_dots_1347 */ + { 0x0100284e, 6431 }, /* braille_dots_2347 */ + { 0x0100284f, 4093 }, /* braille_dots_12347 */ + { 0x01002850, 8218 }, /* braille_dots_57 */ + { 0x01002851, 5982 }, /* braille_dots_157 */ + { 0x01002852, 7134 }, /* braille_dots_257 */ + { 0x01002853, 4835 }, /* braille_dots_1257 */ + { 0x01002854, 7694 }, /* braille_dots_357 */ + { 0x01002855, 5427 }, /* braille_dots_1357 */ + { 0x01002856, 6579 }, /* braille_dots_2357 */ + { 0x01002857, 4249 }, /* braille_dots_12357 */ + { 0x01002858, 7966 }, /* braille_dots_457 */ + { 0x01002859, 5715 }, /* braille_dots_1457 */ + { 0x0100285a, 6867 }, /* braille_dots_2457 */ + { 0x0100285b, 4553 }, /* braille_dots_12457 */ + { 0x0100285c, 7427 }, /* braille_dots_3457 */ + { 0x0100285d, 5145 }, /* braille_dots_13457 */ + { 0x0100285e, 6297 }, /* braille_dots_23457 */ + { 0x0100285f, 3952 }, /* braille_dots_123457 */ + { 0x01002860, 8282 }, /* braille_dots_67 */ + { 0x01002861, 6050 }, /* braille_dots_167 */ + { 0x01002862, 7202 }, /* braille_dots_267 */ + { 0x01002863, 4907 }, /* braille_dots_1267 */ + { 0x01002864, 7762 }, /* braille_dots_367 */ + { 0x01002865, 5499 }, /* braille_dots_1367 */ + { 0x01002866, 6651 }, /* braille_dots_2367 */ + { 0x01002867, 4325 }, /* braille_dots_12367 */ + { 0x01002868, 8034 }, /* braille_dots_467 */ + { 0x01002869, 5787 }, /* braille_dots_1467 */ + { 0x0100286a, 6939 }, /* braille_dots_2467 */ + { 0x0100286b, 4629 }, /* braille_dots_12467 */ + { 0x0100286c, 7499 }, /* braille_dots_3467 */ + { 0x0100286d, 5221 }, /* braille_dots_13467 */ + { 0x0100286e, 6373 }, /* braille_dots_23467 */ + { 0x0100286f, 4032 }, /* braille_dots_123467 */ + { 0x01002870, 8166 }, /* braille_dots_567 */ + { 0x01002871, 5927 }, /* braille_dots_1567 */ + { 0x01002872, 7079 }, /* braille_dots_2567 */ + { 0x01002873, 4777 }, /* braille_dots_12567 */ + { 0x01002874, 7639 }, /* braille_dots_3567 */ + { 0x01002875, 5369 }, /* braille_dots_13567 */ + { 0x01002876, 6521 }, /* braille_dots_23567 */ + { 0x01002877, 4188 }, /* braille_dots_123567 */ + { 0x01002878, 7911 }, /* braille_dots_4567 */ + { 0x01002879, 5657 }, /* braille_dots_14567 */ + { 0x0100287a, 6809 }, /* braille_dots_24567 */ + { 0x0100287b, 4492 }, /* braille_dots_124567 */ + { 0x0100287c, 7369 }, /* braille_dots_34567 */ + { 0x0100287d, 5084 }, /* braille_dots_134567 */ + { 0x0100287e, 6236 }, /* braille_dots_234567 */ + { 0x0100287f, 3888 }, /* braille_dots_1234567 */ + { 0x01002880, 8362 }, /* braille_dots_8 */ + { 0x01002881, 6135 }, /* braille_dots_18 */ + { 0x01002882, 7287 }, /* braille_dots_28 */ + { 0x01002883, 4997 }, /* braille_dots_128 */ + { 0x01002884, 7847 }, /* braille_dots_38 */ + { 0x01002885, 5589 }, /* braille_dots_138 */ + { 0x01002886, 6741 }, /* braille_dots_238 */ + { 0x01002887, 4420 }, /* braille_dots_1238 */ + { 0x01002888, 8119 }, /* braille_dots_48 */ + { 0x01002889, 5877 }, /* braille_dots_148 */ + { 0x0100288a, 7029 }, /* braille_dots_248 */ + { 0x0100288b, 4724 }, /* braille_dots_1248 */ + { 0x0100288c, 7589 }, /* braille_dots_348 */ + { 0x0100288d, 5316 }, /* braille_dots_1348 */ + { 0x0100288e, 6468 }, /* braille_dots_2348 */ + { 0x0100288f, 4132 }, /* braille_dots_12348 */ + { 0x01002890, 8251 }, /* braille_dots_58 */ + { 0x01002891, 6017 }, /* braille_dots_158 */ + { 0x01002892, 7169 }, /* braille_dots_258 */ + { 0x01002893, 4872 }, /* braille_dots_1258 */ + { 0x01002894, 7729 }, /* braille_dots_358 */ + { 0x01002895, 5464 }, /* braille_dots_1358 */ + { 0x01002896, 6616 }, /* braille_dots_2358 */ + { 0x01002897, 4288 }, /* braille_dots_12358 */ + { 0x01002898, 8001 }, /* braille_dots_458 */ + { 0x01002899, 5752 }, /* braille_dots_1458 */ + { 0x0100289a, 6904 }, /* braille_dots_2458 */ + { 0x0100289b, 4592 }, /* braille_dots_12458 */ + { 0x0100289c, 7464 }, /* braille_dots_3458 */ + { 0x0100289d, 5184 }, /* braille_dots_13458 */ + { 0x0100289e, 6336 }, /* braille_dots_23458 */ + { 0x0100289f, 3993 }, /* braille_dots_123458 */ + { 0x010028a0, 8315 }, /* braille_dots_68 */ + { 0x010028a1, 6085 }, /* braille_dots_168 */ + { 0x010028a2, 7237 }, /* braille_dots_268 */ + { 0x010028a3, 4944 }, /* braille_dots_1268 */ + { 0x010028a4, 7797 }, /* braille_dots_368 */ + { 0x010028a5, 5536 }, /* braille_dots_1368 */ + { 0x010028a6, 6688 }, /* braille_dots_2368 */ + { 0x010028a7, 4364 }, /* braille_dots_12368 */ + { 0x010028a8, 8069 }, /* braille_dots_468 */ + { 0x010028a9, 5824 }, /* braille_dots_1468 */ + { 0x010028aa, 6976 }, /* braille_dots_2468 */ + { 0x010028ab, 4668 }, /* braille_dots_12468 */ + { 0x010028ac, 7536 }, /* braille_dots_3468 */ + { 0x010028ad, 5260 }, /* braille_dots_13468 */ + { 0x010028ae, 6412 }, /* braille_dots_23468 */ + { 0x010028af, 4073 }, /* braille_dots_123468 */ + { 0x010028b0, 8201 }, /* braille_dots_568 */ + { 0x010028b1, 5964 }, /* braille_dots_1568 */ + { 0x010028b2, 7116 }, /* braille_dots_2568 */ + { 0x010028b3, 4816 }, /* braille_dots_12568 */ + { 0x010028b4, 7676 }, /* braille_dots_3568 */ + { 0x010028b5, 5408 }, /* braille_dots_13568 */ + { 0x010028b6, 6560 }, /* braille_dots_23568 */ + { 0x010028b7, 4229 }, /* braille_dots_123568 */ + { 0x010028b8, 7948 }, /* braille_dots_4568 */ + { 0x010028b9, 5696 }, /* braille_dots_14568 */ + { 0x010028ba, 6848 }, /* braille_dots_24568 */ + { 0x010028bb, 4533 }, /* braille_dots_124568 */ + { 0x010028bc, 7408 }, /* braille_dots_34568 */ + { 0x010028bd, 5125 }, /* braille_dots_134568 */ + { 0x010028be, 6277 }, /* braille_dots_234568 */ + { 0x010028bf, 3931 }, /* braille_dots_1234568 */ + { 0x010028c0, 8346 }, /* braille_dots_78 */ + { 0x010028c1, 6118 }, /* braille_dots_178 */ + { 0x010028c2, 7270 }, /* braille_dots_278 */ + { 0x010028c3, 4979 }, /* braille_dots_1278 */ + { 0x010028c4, 7830 }, /* braille_dots_378 */ + { 0x010028c5, 5571 }, /* braille_dots_1378 */ + { 0x010028c6, 6723 }, /* braille_dots_2378 */ + { 0x010028c7, 4401 }, /* braille_dots_12378 */ + { 0x010028c8, 8102 }, /* braille_dots_478 */ + { 0x010028c9, 5859 }, /* braille_dots_1478 */ + { 0x010028ca, 7011 }, /* braille_dots_2478 */ + { 0x010028cb, 4705 }, /* braille_dots_12478 */ + { 0x010028cc, 7571 }, /* braille_dots_3478 */ + { 0x010028cd, 5297 }, /* braille_dots_13478 */ + { 0x010028ce, 6449 }, /* braille_dots_23478 */ + { 0x010028cf, 4112 }, /* braille_dots_123478 */ + { 0x010028d0, 8234 }, /* braille_dots_578 */ + { 0x010028d1, 5999 }, /* braille_dots_1578 */ + { 0x010028d2, 7151 }, /* braille_dots_2578 */ + { 0x010028d3, 4853 }, /* braille_dots_12578 */ + { 0x010028d4, 7711 }, /* braille_dots_3578 */ + { 0x010028d5, 5445 }, /* braille_dots_13578 */ + { 0x010028d6, 6597 }, /* braille_dots_23578 */ + { 0x010028d7, 4268 }, /* braille_dots_123578 */ + { 0x010028d8, 7983 }, /* braille_dots_4578 */ + { 0x010028d9, 5733 }, /* braille_dots_14578 */ + { 0x010028da, 6885 }, /* braille_dots_24578 */ + { 0x010028db, 4572 }, /* braille_dots_124578 */ + { 0x010028dc, 7445 }, /* braille_dots_34578 */ + { 0x010028dd, 5164 }, /* braille_dots_134578 */ + { 0x010028de, 6316 }, /* braille_dots_234578 */ + { 0x010028df, 3972 }, /* braille_dots_1234578 */ + { 0x010028e0, 8298 }, /* braille_dots_678 */ + { 0x010028e1, 6067 }, /* braille_dots_1678 */ + { 0x010028e2, 7219 }, /* braille_dots_2678 */ + { 0x010028e3, 4925 }, /* braille_dots_12678 */ + { 0x010028e4, 7779 }, /* braille_dots_3678 */ + { 0x010028e5, 5517 }, /* braille_dots_13678 */ + { 0x010028e6, 6669 }, /* braille_dots_23678 */ + { 0x010028e7, 4344 }, /* braille_dots_123678 */ + { 0x010028e8, 8051 }, /* braille_dots_4678 */ + { 0x010028e9, 5805 }, /* braille_dots_14678 */ + { 0x010028ea, 6957 }, /* braille_dots_24678 */ + { 0x010028eb, 4648 }, /* braille_dots_124678 */ + { 0x010028ec, 7517 }, /* braille_dots_34678 */ + { 0x010028ed, 5240 }, /* braille_dots_134678 */ + { 0x010028ee, 6392 }, /* braille_dots_234678 */ + { 0x010028ef, 4052 }, /* braille_dots_1234678 */ + { 0x010028f0, 8183 }, /* braille_dots_5678 */ + { 0x010028f1, 5945 }, /* braille_dots_15678 */ + { 0x010028f2, 7097 }, /* braille_dots_25678 */ + { 0x010028f3, 4796 }, /* braille_dots_125678 */ + { 0x010028f4, 7657 }, /* braille_dots_35678 */ + { 0x010028f5, 5388 }, /* braille_dots_135678 */ + { 0x010028f6, 6540 }, /* braille_dots_235678 */ + { 0x010028f7, 4208 }, /* braille_dots_1235678 */ + { 0x010028f8, 7929 }, /* braille_dots_45678 */ + { 0x010028f9, 5676 }, /* braille_dots_145678 */ + { 0x010028fa, 6828 }, /* braille_dots_245678 */ + { 0x010028fb, 4512 }, /* braille_dots_1245678 */ + { 0x010028fc, 7388 }, /* braille_dots_345678 */ + { 0x010028fd, 5104 }, /* braille_dots_1345678 */ + { 0x010028fe, 6256 }, /* braille_dots_2345678 */ + { 0x010028ff, 3909 }, /* braille_dots_12345678 */ + { 0x100000a8, 16910 }, /* hpmute_acute */ + { 0x100000a9, 16977 }, /* hpmute_grave */ + { 0x100000aa, 16923 }, /* hpmute_asciicircum */ + { 0x100000ab, 16960 }, /* hpmute_diaeresis */ + { 0x100000ac, 16942 }, /* hpmute_asciitilde */ + { 0x100000af, 16867 }, /* hplira */ + { 0x100000be, 16813 }, /* hpguilder */ + { 0x100000ee, 17014 }, /* hpYdiaeresis */ + { 0x100000f6, 16874 }, /* hplongminus */ + { 0x100000fc, 16767 }, /* hpblock */ + { 0x1000fe22, 10414 }, /* Ddiaeresis */ + { 0x1000fe27, 10344 }, /* Dacute_accent */ + { 0x1000fe2c, 10379 }, /* Dcedilla_accent */ + { 0x1000fe5e, 10395 }, /* Dcircumflex_accent */ + { 0x1000fe60, 11189 }, /* Dgrave_accent */ + { 0x1000fe7e, 11417 }, /* Dtilde */ + { 0x1000feb0, 11388 }, /* Dring_accent */ + { 0x1000ff00, 11380 }, /* DRemove */ + { 0x1000ff48, 16886 }, /* hpModelock1 */ + { 0x1000ff49, 16898 }, /* hpModelock2 */ + { 0x1000ff6c, 16990 }, /* hpReset */ + { 0x1000ff6d, 16998 }, /* hpSystem */ + { 0x1000ff6e, 17007 }, /* hpUser */ + { 0x1000ff6f, 16775 }, /* hpClearLine */ + { 0x1000ff70, 16836 }, /* hpInsertLine */ + { 0x1000ff71, 16800 }, /* hpDeleteLine */ + { 0x1000ff72, 16823 }, /* hpInsertChar */ + { 0x1000ff73, 16787 }, /* hpDeleteChar */ + { 0x1000ff74, 16757 }, /* hpBackTab */ + { 0x1000ff75, 16854 }, /* hpKP_BackTab */ + { 0x1000ff76, 12125 }, /* Ext16bit_L */ + { 0x1000ff77, 12136 }, /* Ext16bit_R */ + { 0x1004ff02, 20956 }, /* osfCopy */ + { 0x1004ff03, 20964 }, /* osfCut */ + { 0x1004ff04, 21163 }, /* osfPaste */ + { 0x1004ff07, 20900 }, /* osfBackTab */ + { 0x1004ff08, 20887 }, /* osfBackSpace */ + { 0x1004ff0b, 20947 }, /* osfClear */ + { 0x1004ff1b, 21026 }, /* osfEscape */ + { 0x1004ff31, 20876 }, /* osfAddMode */ + { 0x1004ff32, 21197 }, /* osfPrimaryPaste */ + { 0x1004ff33, 21213 }, /* osfQuickPaste */ + { 0x1004ff40, 21128 }, /* osfPageLeft */ + { 0x1004ff41, 21153 }, /* osfPageUp */ + { 0x1004ff42, 21116 }, /* osfPageDown */ + { 0x1004ff43, 21140 }, /* osfPageRight */ + { 0x1004ff44, 20864 }, /* osfActivate */ + { 0x1004ff45, 21080 }, /* osfMenuBar */ + { 0x1004ff51, 21064 }, /* osfLeft */ + { 0x1004ff52, 21290 }, /* osfUp */ + { 0x1004ff53, 21250 }, /* osfRight */ + { 0x1004ff54, 20996 }, /* osfDown */ + { 0x1004ff57, 21015 }, /* osfEndLine */ + { 0x1004ff58, 20924 }, /* osfBeginLine */ + { 0x1004ff59, 21004 }, /* osfEndData */ + { 0x1004ff5a, 20911 }, /* osfBeginData */ + { 0x1004ff5b, 21185 }, /* osfPrevMenu */ + { 0x1004ff5c, 21104 }, /* osfNextMenu */ + { 0x1004ff5d, 21172 }, /* osfPrevField */ + { 0x1004ff5e, 21091 }, /* osfNextField */ + { 0x1004ff60, 21259 }, /* osfSelect */ + { 0x1004ff63, 21054 }, /* osfInsert */ + { 0x1004ff65, 21282 }, /* osfUndo */ + { 0x1004ff67, 21072 }, /* osfMenu */ + { 0x1004ff69, 20937 }, /* osfCancel */ + { 0x1004ff6a, 21046 }, /* osfHelp */ + { 0x1004ff71, 21269 }, /* osfSelectAll */ + { 0x1004ff72, 20981 }, /* osfDeselectAll */ + { 0x1004ff73, 21227 }, /* osfReselect */ + { 0x1004ff74, 21036 }, /* osfExtend */ + { 0x1004ff78, 21239 }, /* osfRestore */ + { 0x1004ffff, 20971 }, /* osfDelete */ + { 0x1005ff00, 24032 }, /* SunFA_Grave */ + { 0x1005ff01, 24003 }, /* SunFA_Circum */ + { 0x1005ff02, 24044 }, /* SunFA_Tilde */ + { 0x1005ff03, 23977 }, /* SunFA_Acute */ + { 0x1005ff04, 24016 }, /* SunFA_Diaeresis */ + { 0x1005ff05, 23989 }, /* SunFA_Cedilla */ + { 0x1005ff10, 23963 }, /* SunF36 */ + { 0x1005ff11, 23970 }, /* SunF37 */ + { 0x1005ff60, 24180 }, /* SunSys_Req */ + { 0x1005ff70, 24163 }, /* SunProps */ + { 0x1005ff71, 24064 }, /* SunFront */ + { 0x1005ff72, 23948 }, /* SunCopy */ + { 0x1005ff73, 24073 }, /* SunOpen */ + { 0x1005ff74, 24103 }, /* SunPaste */ + { 0x1005ff75, 23956 }, /* SunCut */ + { 0x1005ff76, 24112 }, /* SunPowerSwitch */ + { 0x1005ff77, 23884 }, /* SunAudioLowerVolume */ + { 0x1005ff78, 23904 }, /* SunAudioMute */ + { 0x1005ff79, 23917 }, /* SunAudioRaiseVolume */ + { 0x1005ff7a, 24199 }, /* SunVideoDegauss */ + { 0x1005ff7b, 24215 }, /* SunVideoLowerBrightness */ + { 0x1005ff7c, 24239 }, /* SunVideoRaiseBrightness */ + { 0x1005ff7d, 24127 }, /* SunPowerSwitchShift */ + { 0x1008fe01, 28552 }, /* XF86Switch_VT_1 */ + { 0x1008fe02, 28619 }, /* XF86Switch_VT_2 */ + { 0x1008fe03, 28635 }, /* XF86Switch_VT_3 */ + { 0x1008fe04, 28651 }, /* XF86Switch_VT_4 */ + { 0x1008fe05, 28667 }, /* XF86Switch_VT_5 */ + { 0x1008fe06, 28683 }, /* XF86Switch_VT_6 */ + { 0x1008fe07, 28699 }, /* XF86Switch_VT_7 */ + { 0x1008fe08, 28715 }, /* XF86Switch_VT_8 */ + { 0x1008fe09, 28731 }, /* XF86Switch_VT_9 */ + { 0x1008fe0a, 28568 }, /* XF86Switch_VT_10 */ + { 0x1008fe0b, 28585 }, /* XF86Switch_VT_11 */ + { 0x1008fe0c, 28602 }, /* XF86Switch_VT_12 */ + { 0x1008fe20, 28878 }, /* XF86Ungrab */ + { 0x1008fe21, 27151 }, /* XF86ClearGrab */ + { 0x1008fe22, 28029 }, /* XF86Next_VMode */ + { 0x1008fe23, 28151 }, /* XF86Prev_VMode */ + { 0x1008fe24, 27786 }, /* XF86LogWindowTree */ + { 0x1008fe25, 27759 }, /* XF86LogGrabInfo */ + { 0x1008ff01, 27897 }, /* XF86ModeLock */ + { 0x1008ff02, 27955 }, /* XF86MonBrightnessUp */ + { 0x1008ff03, 27933 }, /* XF86MonBrightnessDown */ + { 0x1008ff04, 27522 }, /* XF86KbdLightOnOff */ + { 0x1008ff05, 27502 }, /* XF86KbdBrightnessUp */ + { 0x1008ff06, 27480 }, /* XF86KbdBrightnessDown */ + { 0x1008ff07, 27910 }, /* XF86MonBrightnessCycle */ + { 0x1008ff10, 28484 }, /* XF86Standby */ + { 0x1008ff11, 26749 }, /* XF86AudioLowerVolume */ + { 0x1008ff12, 26802 }, /* XF86AudioMute */ + { 0x1008ff13, 26889 }, /* XF86AudioRaiseVolume */ + { 0x1008ff14, 26845 }, /* XF86AudioPlay */ + { 0x1008ff15, 26978 }, /* XF86AudioStop */ + { 0x1008ff16, 26875 }, /* XF86AudioPrev */ + { 0x1008ff17, 26816 }, /* XF86AudioNext */ + { 0x1008ff18, 27443 }, /* XF86HomePage */ + { 0x1008ff19, 27804 }, /* XF86Mail */ + { 0x1008ff1a, 28496 }, /* XF86Start */ + { 0x1008ff1b, 28408 }, /* XF86Search */ + { 0x1008ff1c, 26930 }, /* XF86AudioRecord */ + { 0x1008ff1d, 27106 }, /* XF86Calculator */ + { 0x1008ff1e, 27852 }, /* XF86Memo */ + { 0x1008ff1f, 28782 }, /* XF86ToDoList */ + { 0x1008ff20, 27121 }, /* XF86Calendar */ + { 0x1008ff21, 28124 }, /* XF86PowerDown */ + { 0x1008ff22, 27189 }, /* XF86ContrastAdjust */ + { 0x1008ff23, 28255 }, /* XF86RockerUp */ + { 0x1008ff24, 28224 }, /* XF86RockerDown */ + { 0x1008ff25, 28239 }, /* XF86RockerEnter */ + { 0x1008ff26, 27001 }, /* XF86Back */ + { 0x1008ff27, 27333 }, /* XF86Forward */ + { 0x1008ff28, 28506 }, /* XF86Stop */ + { 0x1008ff29, 28180 }, /* XF86Refresh */ + { 0x1008ff2a, 28138 }, /* XF86PowerOff */ + { 0x1008ff2b, 28966 }, /* XF86WakeUp */ + { 0x1008ff2c, 27274 }, /* XF86Eject */ + { 0x1008ff2d, 28348 }, /* XF86ScreenSaver */ + { 0x1008ff2e, 29031 }, /* XF86WWW */ + { 0x1008ff2f, 28448 }, /* XF86Sleep */ + { 0x1008ff30, 27307 }, /* XF86Favorites */ + { 0x1008ff31, 26830 }, /* XF86AudioPause */ + { 0x1008ff32, 26770 }, /* XF86AudioMedia */ + { 0x1008ff33, 27985 }, /* XF86MyComputer */ + { 0x1008ff34, 28932 }, /* XF86VendorHome */ + { 0x1008ff35, 27745 }, /* XF86LightBulb */ + { 0x1008ff36, 28439 }, /* XF86Shop */ + { 0x1008ff37, 27431 }, /* XF86History */ + { 0x1008ff38, 28068 }, /* XF86OpenURL */ + { 0x1008ff39, 26655 }, /* XF86AddFavorite */ + { 0x1008ff3a, 27456 }, /* XF86HotLinks */ + { 0x1008ff3b, 27070 }, /* XF86BrightnessAdjust */ + { 0x1008ff3c, 27321 }, /* XF86Finance */ + { 0x1008ff3d, 27175 }, /* XF86Community */ + { 0x1008ff3e, 26962 }, /* XF86AudioRewind */ + { 0x1008ff3f, 27010 }, /* XF86BackForward */ + { 0x1008ff40, 27553 }, /* XF86Launch0 */ + { 0x1008ff41, 27565 }, /* XF86Launch1 */ + { 0x1008ff42, 27577 }, /* XF86Launch2 */ + { 0x1008ff43, 27589 }, /* XF86Launch3 */ + { 0x1008ff44, 27601 }, /* XF86Launch4 */ + { 0x1008ff45, 27613 }, /* XF86Launch5 */ + { 0x1008ff46, 27625 }, /* XF86Launch6 */ + { 0x1008ff47, 27637 }, /* XF86Launch7 */ + { 0x1008ff48, 27649 }, /* XF86Launch8 */ + { 0x1008ff49, 27661 }, /* XF86Launch9 */ + { 0x1008ff4a, 27673 }, /* XF86LaunchA */ + { 0x1008ff4b, 27685 }, /* XF86LaunchB */ + { 0x1008ff4c, 27697 }, /* XF86LaunchC */ + { 0x1008ff4d, 27709 }, /* XF86LaunchD */ + { 0x1008ff4e, 27721 }, /* XF86LaunchE */ + { 0x1008ff4f, 27733 }, /* XF86LaunchF */ + { 0x1008ff50, 26671 }, /* XF86ApplicationLeft */ + { 0x1008ff51, 26691 }, /* XF86ApplicationRight */ + { 0x1008ff52, 27061 }, /* XF86Book */ + { 0x1008ff53, 27134 }, /* XF86CD */ + { 0x1008ff54, 27091 }, /* XF86Calculater */ + { 0x1008ff55, 27141 }, /* XF86Clear */ + { 0x1008ff56, 27165 }, /* XF86Close */ + { 0x1008ff57, 27208 }, /* XF86Copy */ + { 0x1008ff58, 27217 }, /* XF86Cut */ + { 0x1008ff59, 27240 }, /* XF86Display */ + { 0x1008ff5a, 27266 }, /* XF86DOS */ + { 0x1008ff5b, 27252 }, /* XF86Documents */ + { 0x1008ff5c, 27284 }, /* XF86Excel */ + { 0x1008ff5d, 27294 }, /* XF86Explorer */ + { 0x1008ff5e, 27391 }, /* XF86Game */ + { 0x1008ff5f, 27400 }, /* XF86Go */ + { 0x1008ff60, 27469 }, /* XF86iTouch */ + { 0x1008ff61, 27775 }, /* XF86LogOff */ + { 0x1008ff62, 27829 }, /* XF86Market */ + { 0x1008ff63, 27840 }, /* XF86Meeting */ + { 0x1008ff65, 27861 }, /* XF86MenuKB */ + { 0x1008ff66, 27872 }, /* XF86MenuPB */ + { 0x1008ff67, 28000 }, /* XF86MySites */ + { 0x1008ff68, 28012 }, /* XF86New */ + { 0x1008ff69, 28020 }, /* XF86News */ + { 0x1008ff6a, 28044 }, /* XF86OfficeHome */ + { 0x1008ff6b, 28059 }, /* XF86Open */ + { 0x1008ff6c, 28080 }, /* XF86Option */ + { 0x1008ff6d, 28091 }, /* XF86Paste */ + { 0x1008ff6e, 28101 }, /* XF86Phone */ + { 0x1008ff70, 28166 }, /* XF86Q */ + { 0x1008ff72, 28203 }, /* XF86Reply */ + { 0x1008ff73, 28192 }, /* XF86Reload */ + { 0x1008ff74, 28268 }, /* XF86RotateWindows */ + { 0x1008ff75, 28324 }, /* XF86RotationPB */ + { 0x1008ff76, 28286 }, /* XF86RotationKB */ + { 0x1008ff77, 28339 }, /* XF86Save */ + { 0x1008ff78, 28395 }, /* XF86ScrollUp */ + { 0x1008ff79, 28380 }, /* XF86ScrollDown */ + { 0x1008ff7a, 28364 }, /* XF86ScrollClick */ + { 0x1008ff7b, 28430 }, /* XF86Send */ + { 0x1008ff7c, 28458 }, /* XF86Spell */ + { 0x1008ff7d, 28468 }, /* XF86SplitScreen */ + { 0x1008ff7e, 28528 }, /* XF86Support */ + { 0x1008ff7f, 28747 }, /* XF86TaskPane */ + { 0x1008ff80, 28760 }, /* XF86Terminal */ + { 0x1008ff81, 28795 }, /* XF86Tools */ + { 0x1008ff82, 28867 }, /* XF86Travel */ + { 0x1008ff84, 28913 }, /* XF86UserPB */ + { 0x1008ff85, 28889 }, /* XF86User1KB */ + { 0x1008ff86, 28901 }, /* XF86User2KB */ + { 0x1008ff87, 28947 }, /* XF86Video */ + { 0x1008ff88, 28988 }, /* XF86WheelButton */ + { 0x1008ff89, 29013 }, /* XF86Word */ + { 0x1008ff8a, 29039 }, /* XF86Xfer */ + { 0x1008ff8b, 29059 }, /* XF86ZoomIn */ + { 0x1008ff8c, 29070 }, /* XF86ZoomOut */ + { 0x1008ff8d, 26992 }, /* XF86Away */ + { 0x1008ff8e, 27883 }, /* XF86Messenger */ + { 0x1008ff8f, 28977 }, /* XF86WebCam */ + { 0x1008ff90, 27813 }, /* XF86MailForward */ + { 0x1008ff91, 28111 }, /* XF86Pictures */ + { 0x1008ff92, 27975 }, /* XF86Music */ + { 0x1008ff93, 27026 }, /* XF86Battery */ + { 0x1008ff94, 27047 }, /* XF86Bluetooth */ + { 0x1008ff95, 29004 }, /* XF86WLAN */ + { 0x1008ff96, 28924 }, /* XF86UWB */ + { 0x1008ff97, 26732 }, /* XF86AudioForward */ + { 0x1008ff98, 26946 }, /* XF86AudioRepeat */ + { 0x1008ff99, 26910 }, /* XF86AudioRandomPlay */ + { 0x1008ff9a, 28515 }, /* XF86Subtitle */ + { 0x1008ff9b, 26712 }, /* XF86AudioCycleTrack */ + { 0x1008ff9c, 27225 }, /* XF86CycleAngle */ + { 0x1008ff9d, 27345 }, /* XF86FrameBack */ + { 0x1008ff9e, 27359 }, /* XF86FrameForward */ + { 0x1008ff9f, 28773 }, /* XF86Time */ + { 0x1008ffa0, 28419 }, /* XF86Select */ + { 0x1008ffa1, 28957 }, /* XF86View */ + { 0x1008ffa2, 28805 }, /* XF86TopMenu */ + { 0x1008ffa3, 28172 }, /* XF86Red */ + { 0x1008ffa4, 27407 }, /* XF86Green */ + { 0x1008ffa5, 29048 }, /* XF86Yellow */ + { 0x1008ffa6, 27038 }, /* XF86Blue */ + { 0x1008ffa7, 28540 }, /* XF86Suspend */ + { 0x1008ffa8, 27417 }, /* XF86Hibernate */ + { 0x1008ffa9, 28848 }, /* XF86TouchpadToggle */ + { 0x1008ffb0, 28833 }, /* XF86TouchpadOn */ + { 0x1008ffb1, 28817 }, /* XF86TouchpadOff */ + { 0x1008ffb2, 26785 }, /* XF86AudioMicMute */ + { 0x1008ffb3, 27540 }, /* XF86Keyboard */ + { 0x1008ffb4, 29022 }, /* XF86WWAN */ + { 0x1008ffb5, 28213 }, /* XF86RFKill */ + { 0x1008ffb6, 26859 }, /* XF86AudioPreset */ + { 0x1008ffb7, 28301 }, /* XF86RotationLockToggle */ + { 0x1008ffb8, 27376 }, /* XF86FullScreen */ +}; diff --git a/src/registry.c b/src/registry.c new file mode 100644 index 0000000..d3d95f5 --- /dev/null +++ b/src/registry.c @@ -0,0 +1,1198 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <libxml/parser.h> + +#include "xkbcommon/xkbregistry.h" +#include "utils.h" +#include "util-list.h" + +struct rxkb_object; + +typedef void (*destroy_func_t)(struct rxkb_object *object); + +/** + * All our objects are refcounted and are linked to iterate through them. + * Abstract those bits away into a shared parent class so we can generate + * most of the functions through macros. + */ +struct rxkb_object { + struct rxkb_object *parent; + uint32_t refcount; + struct list link; + destroy_func_t destroy; +}; + +struct rxkb_iso639_code { + struct rxkb_object base; + char *code; +}; + +struct rxkb_iso3166_code { + struct rxkb_object base; + char *code; +}; + +enum context_state { + CONTEXT_NEW, + CONTEXT_PARSED, + CONTEXT_FAILED, +}; + +struct rxkb_context { + struct rxkb_object base; + enum context_state context_state; + + bool load_extra_rules_files; + + struct list models; /* list of struct rxkb_models */ + struct list layouts; /* list of struct rxkb_layouts */ + struct list option_groups; /* list of struct rxkb_option_group */ + + darray(char *) includes; + + + ATTR_PRINTF(3, 0) void (*log_fn)(struct rxkb_context *ctx, + enum rxkb_log_level level, + const char *fmt, va_list args); + enum rxkb_log_level log_level; + + void *userdata; +}; + +struct rxkb_model { + struct rxkb_object base; + + char *name; + char *vendor; + char *description; + enum rxkb_popularity popularity; +}; + +struct rxkb_layout { + struct rxkb_object base; + + char *name; + char *brief; + char *description; + char *variant; + enum rxkb_popularity popularity; + + struct list iso639s; /* list of struct rxkb_iso639_code */ + struct list iso3166s; /* list of struct rxkb_iso3166_code */ +}; + +struct rxkb_option_group { + struct rxkb_object base; + + bool allow_multiple; + struct list options; /* list of struct rxkb_options */ + char *name; + char *description; + enum rxkb_popularity popularity; +}; + +struct rxkb_option { + struct rxkb_object base; + + char *name; + char *brief; + char *description; + enum rxkb_popularity popularity; +}; + +static bool +parse(struct rxkb_context *ctx, const char *path, + enum rxkb_popularity popularity); + +static void +rxkb_log(struct rxkb_context *ctx, enum rxkb_log_level level, + const char *fmt, ...) +{ + va_list args; + + if (ctx->log_level < level) + return; + + va_start(args, fmt); + ctx->log_fn(ctx, level, fmt, args); + va_end(args); +} + +/* + * The format is not part of the argument list in order to avoid the + * "ISO C99 requires rest arguments to be used" warning when only the + * format is supplied without arguments. Not supplying it would still + * result in an error, though. + */ +#define log_dbg(ctx, ...) \ + rxkb_log((ctx), RXKB_LOG_LEVEL_DEBUG, __VA_ARGS__) +#define log_info(ctx, ...) \ + rxkb_log((ctx), RXKB_LOG_LEVEL_INFO, __VA_ARGS__) +#define log_warn(ctx, ...) \ + rxkb_log((ctx), RXKB_LOG_LEVEL_WARNING, __VA_ARGS__) +#define log_err(ctx, ...) \ + rxkb_log((ctx), RXKB_LOG_LEVEL_ERROR, __VA_ARGS__) +#define log_wsgo(ctx, ...) \ + rxkb_log((ctx), RXKB_LOG_LEVEL_CRITICAL, __VA_ARGS__) + + +#define DECLARE_REF_UNREF_FOR_TYPE(type_) \ +XKB_EXPORT struct type_ * type_##_ref(struct type_ *object) { \ + rxkb_object_ref(&object->base); \ + return object; \ +} \ +XKB_EXPORT struct type_ * type_##_unref(struct type_ *object) { \ + if (!object) return NULL; \ + return rxkb_object_unref(&object->base); \ +} + +#define DECLARE_CREATE_FOR_TYPE(type_) \ +static inline struct type_ * type_##_create(struct rxkb_object *parent) { \ + struct type_ *t = calloc(1, sizeof *t); \ + if (t) \ + rxkb_object_init(&t->base, parent, (destroy_func_t)type_##_destroy); \ + return t; \ +} + +#define DECLARE_TYPED_GETTER_FOR_TYPE(type_, field_, rtype_) \ +XKB_EXPORT rtype_ type_##_get_##field_(struct type_ *object) { \ + return object->field_; \ +} + +#define DECLARE_GETTER_FOR_TYPE(type_, field_) \ + DECLARE_TYPED_GETTER_FOR_TYPE(type_, field_, const char*) + +#define DECLARE_FIRST_NEXT_FOR_TYPE(type_, parent_type_, parent_field_) \ +XKB_EXPORT struct type_ * type_##_first(struct parent_type_ *parent) { \ + struct type_ *o = NULL; \ + if (!list_empty(&parent->parent_field_)) \ + o = list_first_entry(&parent->parent_field_, o, base.link); \ + return o; \ +} \ +XKB_EXPORT struct type_ * \ +type_##_next(struct type_ *o) \ +{ \ + struct parent_type_ *parent; \ + struct type_ *next; \ + parent = container_of(o->base.parent, struct parent_type_, base); \ + next = list_first_entry(&o->base.link, o, base.link); \ + if (list_is_last(&parent->parent_field_, &o->base.link)) \ + return NULL; \ + return next; \ +} + +static void +rxkb_object_init(struct rxkb_object *object, struct rxkb_object *parent, destroy_func_t destroy) +{ + object->refcount = 1; + object->destroy = destroy; + object->parent = parent; + list_init(&object->link); +} + +static void +rxkb_object_destroy(struct rxkb_object *object) +{ + if (object->destroy) + object->destroy(object); + list_remove(&object->link); + free(object); +} + +static void * +rxkb_object_ref(struct rxkb_object *object) +{ + assert(object->refcount >= 1); + ++object->refcount; + return object; +} + +static void * +rxkb_object_unref(struct rxkb_object *object) +{ + assert(object->refcount >= 1); + if (--object->refcount == 0) + rxkb_object_destroy(object); + return NULL; +} + +static void +rxkb_iso639_code_destroy(struct rxkb_iso639_code *code) +{ + free(code->code); +} + +XKB_EXPORT struct rxkb_iso639_code * +rxkb_layout_get_iso639_first(struct rxkb_layout *layout) +{ + struct rxkb_iso639_code *code = NULL; + + if (!list_empty(&layout->iso639s)) + code = list_first_entry(&layout->iso639s, code, base.link); + + return code; +} + +XKB_EXPORT struct rxkb_iso639_code * +rxkb_iso639_code_next(struct rxkb_iso639_code *code) +{ + struct rxkb_iso639_code *next = NULL; + struct rxkb_layout *layout; + + layout = container_of(code->base.parent, struct rxkb_layout, base); + + if (list_is_last(&layout->iso639s, &code->base.link)) + return NULL; + + next = list_first_entry(&code->base.link, code, base.link); + + return next; +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_iso639_code); +DECLARE_CREATE_FOR_TYPE(rxkb_iso639_code); +DECLARE_GETTER_FOR_TYPE(rxkb_iso639_code, code); + +static void +rxkb_iso3166_code_destroy(struct rxkb_iso3166_code *code) +{ + free(code->code); +} + +XKB_EXPORT struct rxkb_iso3166_code * +rxkb_layout_get_iso3166_first(struct rxkb_layout *layout) +{ + struct rxkb_iso3166_code *code = NULL; + + if (!list_empty(&layout->iso3166s)) + code = list_first_entry(&layout->iso3166s, code, base.link); + + return code; +} + +XKB_EXPORT struct rxkb_iso3166_code * +rxkb_iso3166_code_next(struct rxkb_iso3166_code *code) +{ + struct rxkb_iso3166_code *next = NULL; + struct rxkb_layout *layout; + + layout = container_of(code->base.parent, struct rxkb_layout, base); + + if (list_is_last(&layout->iso3166s, &code->base.link)) + return NULL; + + next = list_first_entry(&code->base.link, code, base.link); + + return next; +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_iso3166_code); +DECLARE_CREATE_FOR_TYPE(rxkb_iso3166_code); +DECLARE_GETTER_FOR_TYPE(rxkb_iso3166_code, code); + +static void +rxkb_option_destroy(struct rxkb_option *o) +{ + free(o->name); + free(o->brief); + free(o->description); +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_option); +DECLARE_CREATE_FOR_TYPE(rxkb_option); +DECLARE_GETTER_FOR_TYPE(rxkb_option, name); +DECLARE_GETTER_FOR_TYPE(rxkb_option, brief); +DECLARE_GETTER_FOR_TYPE(rxkb_option, description); +DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_option, popularity, enum rxkb_popularity); +DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_option, rxkb_option_group, options); + +static void +rxkb_layout_destroy(struct rxkb_layout *l) +{ + struct rxkb_iso639_code *iso639, *tmp_639; + struct rxkb_iso3166_code *iso3166, *tmp_3166; + + free(l->name); + free(l->brief); + free(l->description); + free(l->variant); + + list_for_each_safe(iso639, tmp_639, &l->iso639s, base.link) { + rxkb_iso639_code_unref(iso639); + } + list_for_each_safe(iso3166, tmp_3166, &l->iso3166s, base.link) { + rxkb_iso3166_code_unref(iso3166); + } +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_layout); +DECLARE_CREATE_FOR_TYPE(rxkb_layout); +DECLARE_GETTER_FOR_TYPE(rxkb_layout, name); +DECLARE_GETTER_FOR_TYPE(rxkb_layout, brief); +DECLARE_GETTER_FOR_TYPE(rxkb_layout, description); +DECLARE_GETTER_FOR_TYPE(rxkb_layout, variant); +DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_layout, popularity, enum rxkb_popularity); +DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_layout, rxkb_context, layouts); + +static void +rxkb_model_destroy(struct rxkb_model *m) +{ + free(m->name); + free(m->vendor); + free(m->description); +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_model); +DECLARE_CREATE_FOR_TYPE(rxkb_model); +DECLARE_GETTER_FOR_TYPE(rxkb_model, name); +DECLARE_GETTER_FOR_TYPE(rxkb_model, vendor); +DECLARE_GETTER_FOR_TYPE(rxkb_model, description); +DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_model, popularity, enum rxkb_popularity); +DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_model, rxkb_context, models); + +static void +rxkb_option_group_destroy(struct rxkb_option_group *og) +{ + struct rxkb_option *o, *otmp; + + free(og->name); + free(og->description); + + list_for_each_safe(o, otmp, &og->options, base.link) { + rxkb_option_unref(o); + } +} + +XKB_EXPORT bool +rxkb_option_group_allows_multiple(struct rxkb_option_group *g) +{ + return g->allow_multiple; +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_option_group); +DECLARE_CREATE_FOR_TYPE(rxkb_option_group); +DECLARE_GETTER_FOR_TYPE(rxkb_option_group, name); +DECLARE_GETTER_FOR_TYPE(rxkb_option_group, description); +DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_option_group, popularity, enum rxkb_popularity); +DECLARE_FIRST_NEXT_FOR_TYPE(rxkb_option_group, rxkb_context, option_groups); + +static void +rxkb_context_destroy(struct rxkb_context *ctx) +{ + struct rxkb_model *m, *mtmp; + struct rxkb_layout *l, *ltmp; + struct rxkb_option_group *og, *ogtmp; + char **path; + + list_for_each_safe(m, mtmp, &ctx->models, base.link) + rxkb_model_unref(m); + assert(list_empty(&ctx->models)); + + list_for_each_safe(l, ltmp, &ctx->layouts, base.link) + rxkb_layout_unref(l); + assert(list_empty(&ctx->layouts)); + + list_for_each_safe(og, ogtmp, &ctx->option_groups, base.link) + rxkb_option_group_unref(og); + assert(list_empty(&ctx->option_groups)); + + darray_foreach(path, ctx->includes) + free(*path); + darray_free(ctx->includes); + + assert(darray_empty(ctx->includes)); +} + +DECLARE_REF_UNREF_FOR_TYPE(rxkb_context); +DECLARE_CREATE_FOR_TYPE(rxkb_context); +DECLARE_TYPED_GETTER_FOR_TYPE(rxkb_context, log_level, enum rxkb_log_level); + +XKB_EXPORT void +rxkb_context_set_log_level(struct rxkb_context *ctx, + enum rxkb_log_level level) +{ + ctx->log_level = level; +} + +static const char * +log_level_to_prefix(enum rxkb_log_level level) +{ + switch (level) { + case RXKB_LOG_LEVEL_DEBUG: + return "xkbregistry: DEBUG: "; + case RXKB_LOG_LEVEL_INFO: + return "xkbregistry: INFO: "; + case RXKB_LOG_LEVEL_WARNING: + return "xkbregistry: WARNING: "; + case RXKB_LOG_LEVEL_ERROR: + return "xkbregistry: ERROR: "; + case RXKB_LOG_LEVEL_CRITICAL: + return "xkbregistry: CRITICAL: "; + default: + return NULL; + } +} + +ATTR_PRINTF(3, 0) static void +default_log_fn(struct rxkb_context *ctx, enum rxkb_log_level level, + const char *fmt, va_list args) +{ + const char *prefix = log_level_to_prefix(level); + + if (prefix) + fprintf(stderr, "%s", prefix); + vfprintf(stderr, fmt, args); +} + +static enum rxkb_log_level +log_level(const char *level) { + char *endptr; + enum rxkb_log_level lvl; + + errno = 0; + lvl = strtol(level, &endptr, 10); + if (errno == 0 && (endptr[0] == '\0' || is_space(endptr[0]))) + return lvl; + if (istreq_prefix("crit", level)) + return RXKB_LOG_LEVEL_CRITICAL; + if (istreq_prefix("err", level)) + return RXKB_LOG_LEVEL_ERROR; + if (istreq_prefix("warn", level)) + return RXKB_LOG_LEVEL_WARNING; + if (istreq_prefix("info", level)) + return RXKB_LOG_LEVEL_INFO; + if (istreq_prefix("debug", level) || istreq_prefix("dbg", level)) + return RXKB_LOG_LEVEL_DEBUG; + + return RXKB_LOG_LEVEL_ERROR; +} + +XKB_EXPORT struct rxkb_context * +rxkb_context_new(enum rxkb_context_flags flags) +{ + struct rxkb_context *ctx = rxkb_context_create(NULL); + const char *env; + + if (!ctx) + return NULL; + + ctx->context_state = CONTEXT_NEW; + ctx->load_extra_rules_files = flags & RXKB_CONTEXT_LOAD_EXOTIC_RULES; + ctx->log_fn = default_log_fn; + ctx->log_level = RXKB_LOG_LEVEL_ERROR; + + /* Environment overwrites defaults. */ + env = secure_getenv("RXKB_LOG_LEVEL"); + if (env) + rxkb_context_set_log_level(ctx, log_level(env)); + + list_init(&ctx->models); + list_init(&ctx->layouts); + list_init(&ctx->option_groups); + + if (!(flags & RXKB_CONTEXT_NO_DEFAULT_INCLUDES) && + !rxkb_context_include_path_append_default(ctx)) { + rxkb_context_unref(ctx); + return NULL; + } + + return ctx; +} + +XKB_EXPORT void +rxkb_context_set_log_fn(struct rxkb_context *ctx, + void (*log_fn)(struct rxkb_context *ctx, + enum rxkb_log_level level, + const char *fmt, va_list args)) +{ + ctx->log_fn = (log_fn ? log_fn : default_log_fn); +} + +XKB_EXPORT bool +rxkb_context_include_path_append(struct rxkb_context *ctx, const char *path) +{ + struct stat stat_buf; + int err; + char *tmp = NULL; + char rules[PATH_MAX]; + + if (ctx->context_state != CONTEXT_NEW) { + log_err(ctx, "include paths can only be appended to a new context\n"); + return false; + } + + tmp = strdup(path); + if (!tmp) + goto err; + + err = stat(path, &stat_buf); + if (err != 0) + goto err; + if (!S_ISDIR(stat_buf.st_mode)) + goto err; + + if (!check_eaccess(path, R_OK | X_OK)) + goto err; + + /* Pre-filter for the 99.9% case - if we can't assemble the default ruleset + * path, complain here instead of during parsing later. The niche cases + * where this is the wrong behaviour aren't worth worrying about. + */ + if (!snprintf_safe(rules, sizeof(rules), "%s/rules/%s.xml", + path, DEFAULT_XKB_RULES)) + goto err; + + darray_append(ctx->includes, tmp); + + return true; + +err: + free(tmp); + return false; +} + +XKB_EXPORT bool +rxkb_context_include_path_append_default(struct rxkb_context *ctx) +{ + const char *home, *xdg, *root, *extra; + char *user_path; + bool ret = false; + + if (ctx->context_state != CONTEXT_NEW) { + log_err(ctx, "include paths can only be appended to a new context\n"); + return false; + } + + home = secure_getenv("HOME"); + + xdg = secure_getenv("XDG_CONFIG_HOME"); + if (xdg != NULL) { + user_path = asprintf_safe("%s/xkb", xdg); + if (user_path) { + ret |= rxkb_context_include_path_append(ctx, user_path); + free(user_path); + } + } else if (home != NULL) { + /* XDG_CONFIG_HOME fallback is $HOME/.config/ */ + user_path = asprintf_safe("%s/.config/xkb", home); + if (user_path) { + ret |= rxkb_context_include_path_append(ctx, user_path); + free(user_path); + } + } + + if (home != NULL) { + user_path = asprintf_safe("%s/.xkb", home); + if (user_path) { + ret |= rxkb_context_include_path_append(ctx, user_path); + free(user_path); + } + } + + extra = secure_getenv("XKB_CONFIG_EXTRA_PATH"); + if (extra != NULL) + ret |= rxkb_context_include_path_append(ctx, extra); + else + ret |= rxkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_EXTRA_PATH); + + root = secure_getenv("XKB_CONFIG_ROOT"); + if (root != NULL) + ret |= rxkb_context_include_path_append(ctx, root); + else + ret |= rxkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_ROOT); + + return ret; +} + +XKB_EXPORT bool +rxkb_context_parse_default_ruleset(struct rxkb_context *ctx) +{ + return rxkb_context_parse(ctx, DEFAULT_XKB_RULES); +} + +XKB_EXPORT bool +rxkb_context_parse(struct rxkb_context *ctx, const char *ruleset) +{ + char **path; + bool success = false; + + if (ctx->context_state != CONTEXT_NEW) { + log_err(ctx, "parse must only be called on a new context\n"); + return false; + } + + darray_foreach_reverse(path, ctx->includes) { + char rules[PATH_MAX]; + + if (snprintf_safe(rules, sizeof(rules), "%s/rules/%s.xml", + *path, ruleset)) { + log_dbg(ctx, "Parsing %s\n", rules); + if (parse(ctx, rules, RXKB_POPULARITY_STANDARD)) + success = true; + } + + if (ctx->load_extra_rules_files && + snprintf_safe(rules, sizeof(rules), "%s/rules/%s.extras.xml", + *path, ruleset)) { + log_dbg(ctx, "Parsing %s\n", rules); + if (parse(ctx, rules, RXKB_POPULARITY_EXOTIC)) + success = true; + } + } + + ctx->context_state = success ? CONTEXT_PARSED : CONTEXT_FAILED; + + return success; +} + + +XKB_EXPORT void +rxkb_context_set_user_data(struct rxkb_context *ctx, void *userdata) +{ + ctx->userdata = userdata; +} + +XKB_EXPORT void * +rxkb_context_get_user_data(struct rxkb_context *ctx) +{ + return ctx->userdata; +} + +static inline bool +is_node(xmlNode *node, const char *name) +{ + return node->type == XML_ELEMENT_NODE && + xmlStrEqual(node->name, (const xmlChar*)name); +} + +/* return a copy of the text content from the first text node of this node */ +static char * +extract_text(xmlNode *node) +{ + xmlNode *n; + + for (n = node->children; n; n = n->next) { + if (n->type == XML_TEXT_NODE) + return (char *)xmlStrdup(n->content); + } + return NULL; +} + +static bool +parse_config_item(struct rxkb_context *ctx, + xmlNode *parent, + char **name, + char **description, + char **brief, + char **vendor) +{ + xmlNode *node = NULL; + xmlNode *ci = NULL; + + for (ci = parent->children; ci; ci = ci->next) { + if (is_node(ci, "configItem")) { + *name = NULL; + *description = NULL; + *brief = NULL; + *vendor = NULL; + + for (node = ci->children; node; node = node->next) { + if (is_node(node, "name")) + *name = extract_text(node); + else if (is_node(node, "description")) + *description = extract_text(node); + else if (is_node(node, "shortDescription")) + *brief = extract_text(node); + else if (is_node(node, "vendor")) + *vendor = extract_text(node); + /* Note: the DTD allows for vendor + brief but models only use + * vendor and everything else only uses shortDescription */ + } + + if (!*name || !strlen(*name)) { + log_err(ctx, "xml:%d: missing required element 'name'\n", + ci->line); + return false; + } + + return true; /* only one configItem allowed in the dtd */ + } + } + + return false; +} + +static void +parse_model(struct rxkb_context *ctx, xmlNode *model, + enum rxkb_popularity popularity) +{ + char *name, *description, *brief, *vendor; + + if (parse_config_item(ctx, model, &name, &description, &brief, &vendor)) { + struct rxkb_model *m; + + list_for_each(m, &ctx->models, base.link) { + if (streq(m->name, name)) { + free(name); + free(description); + free(brief); + free(vendor); + return; + } + } + + /* new model */ + m = rxkb_model_create(&ctx->base); + m->name = name; + m->description = description; + m->vendor = vendor; + m->popularity = popularity; + list_append(&ctx->models, &m->base.link); + } +} + +static void +parse_model_list(struct rxkb_context *ctx, xmlNode *model_list, + enum rxkb_popularity popularity) +{ + xmlNode *node = NULL; + + for (node = model_list->children; node; node = node->next) { + if (is_node(node, "model")) + parse_model(ctx, node, popularity); + } +} + +static void +parse_language_list(xmlNode *language_list, struct rxkb_layout *layout) +{ + xmlNode *node = NULL; + struct rxkb_iso639_code *code; + + for (node = language_list->children; node; node = node->next) { + if (is_node(node, "iso639Id")) { + char *str = extract_text(node); + struct rxkb_object *parent; + + parent = &layout->base; + code = rxkb_iso639_code_create(parent); + code->code = str; + list_append(&layout->iso639s, &code->base.link); + } + } +} + +static void +parse_country_list(xmlNode *country_list, struct rxkb_layout *layout) +{ + xmlNode *node = NULL; + struct rxkb_iso3166_code *code; + + for (node = country_list->children; node; node = node->next) { + if (is_node(node, "iso3166Id")) { + char *str = extract_text(node); + struct rxkb_object *parent; + + parent = &layout->base; + code = rxkb_iso3166_code_create(parent); + code->code = str; + list_append(&layout->iso3166s, &code->base.link); + } + } +} + +static void +parse_variant(struct rxkb_context *ctx, struct rxkb_layout *l, + xmlNode *variant, enum rxkb_popularity popularity) +{ + xmlNode *ci; + char *name, *description, *brief, *vendor; + + if (parse_config_item(ctx, variant, &name, &description, &brief, &vendor)) { + struct rxkb_layout *v; + bool exists = false; + + list_for_each(v, &ctx->layouts, base.link) { + if (streq(v->name, name) && streq(v->name, l->name)) { + exists = true; + break; + } + } + + if (!exists) { + v = rxkb_layout_create(&ctx->base); + list_init(&v->iso639s); + list_init(&v->iso3166s); + v->name = strdup(l->name); + v->variant = name; + v->description = description; + v->brief = brief; + v->popularity = popularity; + list_append(&ctx->layouts, &v->base.link); + + for (ci = variant->children; ci; ci = ci->next) { + xmlNode *node; + + if (!is_node(ci, "configItem")) + continue; + + for (node = ci->children; node; node = node->next) { + if (is_node(node, "languageList")) + parse_language_list(node, v); + if (is_node(node, "countryList")) + parse_country_list(node, v); + } + } + } else { + free(name); + free(description); + free(brief); + free(vendor); + } + } +} + +static void +parse_variant_list(struct rxkb_context *ctx, struct rxkb_layout *l, + xmlNode *variant_list, enum rxkb_popularity popularity) +{ + xmlNode *node = NULL; + + for (node = variant_list->children; node; node = node->next) { + if (is_node(node, "variant")) + parse_variant(ctx, l, node, popularity); + } +} + +static void +parse_layout(struct rxkb_context *ctx, xmlNode *layout, + enum rxkb_popularity popularity) +{ + char *name, *description, *brief, *vendor; + struct rxkb_layout *l; + xmlNode *node = NULL; + bool exists = false; + + if (!parse_config_item(ctx, layout, &name, &description, &brief, &vendor)) + return; + + list_for_each(l, &ctx->layouts, base.link) { + if (streq(l->name, name) && l->variant == NULL) { + exists = true; + break; + } + } + + if (!exists) { + l = rxkb_layout_create(&ctx->base); + list_init(&l->iso639s); + list_init(&l->iso3166s); + l->name = name; + l->variant = NULL; + l->description = description; + l->brief = brief; + l->popularity = popularity; + list_append(&ctx->layouts, &l->base.link); + } else { + free(name); + free(description); + free(brief); + free(vendor); + } + + for (node = layout->children; node; node = node->next) { + if (is_node(node, "variantList")) { + parse_variant_list(ctx, l, node, popularity); + } + if (!exists && is_node(node, "configItem")) { + xmlNode *ll; + for (ll = node->children; ll; ll = ll->next) { + if (is_node(ll, "languageList")) + parse_language_list(ll, l); + if (is_node(ll, "countryList")) + parse_country_list(ll, l); + } + } + } +} + +static void +parse_layout_list(struct rxkb_context *ctx, xmlNode *layout_list, + enum rxkb_popularity popularity) +{ + xmlNode *node = NULL; + + for (node = layout_list->children; node; node = node->next) { + if (is_node(node, "layout")) + parse_layout(ctx, node, popularity); + } +} + +static void +parse_option(struct rxkb_context *ctx, struct rxkb_option_group *group, + xmlNode *option, enum rxkb_popularity popularity) +{ + char *name, *description, *brief, *vendor; + + if (parse_config_item(ctx, option, &name, &description, &brief, &vendor)) { + struct rxkb_option *o; + + list_for_each(o, &group->options, base.link) { + if (streq(o->name, name)) { + free(name); + free(description); + free(brief); + free(vendor); + return; + } + } + + o = rxkb_option_create(&group->base); + o->name = name; + o->description = description; + o->popularity = popularity; + list_append(&group->options, &o->base.link); + } +} + +static void +parse_group(struct rxkb_context *ctx, xmlNode *group, + enum rxkb_popularity popularity) +{ + char *name, *description, *brief, *vendor; + struct rxkb_option_group *g; + xmlNode *node = NULL; + xmlChar *multiple; + bool exists = false; + + if (!parse_config_item(ctx, group, &name, &description, &brief, &vendor)) + return; + + list_for_each(g, &ctx->option_groups, base.link) { + if (streq(g->name, name)) { + exists = true; + break; + } + } + + if (!exists) { + g = rxkb_option_group_create(&ctx->base); + g->name = name; + g->description = description; + g->popularity = popularity; + + multiple = xmlGetProp(group, (const xmlChar*)"allowMultipleSelection"); + if (multiple && xmlStrEqual(multiple, (const xmlChar*)"true")) + g->allow_multiple = true; + xmlFree(multiple); + + list_init(&g->options); + list_append(&ctx->option_groups, &g->base.link); + } else { + free(name); + free(description); + free(brief); + free(vendor); + } + + for (node = group->children; node; node = node->next) { + if (is_node(node, "option")) + parse_option(ctx, g, node, popularity); + } +} + +static void +parse_option_list(struct rxkb_context *ctx, xmlNode *option_list, + enum rxkb_popularity popularity) +{ + xmlNode *node = NULL; + + for (node = option_list->children; node; node = node->next) { + if (is_node(node, "group")) + parse_group(ctx, node, popularity); + } +} + +static void +parse_rules_xml(struct rxkb_context *ctx, xmlNode *root, + enum rxkb_popularity popularity) +{ + xmlNode *node = NULL; + + for (node = root->children; node; node = node->next) { + if (is_node(node, "modelList")) + parse_model_list(ctx, node, popularity); + else if (is_node(node, "layoutList")) + parse_layout_list(ctx, node, popularity); + else if (is_node(node, "optionList")) + parse_option_list(ctx, node, popularity); + } +} + +static void +ATTR_PRINTF(2, 0) +xml_error_func(void *ctx, const char *msg, ...) +{ + static char buf[PATH_MAX]; + static int slen = 0; + va_list args; + int rc; + + /* libxml2 prints IO errors from bad includes paths by + * calling the error function once per word. So we get to + * re-assemble the message here and print it when we get + * the line break. My enthusiasm about this is indescribable. + */ + va_start(args, msg); + rc = vsnprintf(&buf[slen], sizeof(buf) - slen, msg, args); + va_end(args); + + /* This shouldn't really happen */ + if (rc < 0) { + log_err(ctx, "+++ out of cheese error. redo from start +++\n"); + slen = 0; + memset(buf, 0, sizeof(buf)); + return; + } + + slen += rc; + if (slen >= (int)sizeof(buf)) { + /* truncated, let's flush this */ + buf[sizeof(buf) - 1] = '\n'; + slen = sizeof(buf); + } + + /* We're assuming here that the last character is \n. */ + if (buf[slen - 1] == '\n') { + log_err(ctx, "%s", buf); + memset(buf, 0, sizeof(buf)); + slen = 0; + } +} + +static bool +validate(struct rxkb_context *ctx, xmlDoc *doc) +{ + bool success = false; + xmlValidCtxt *dtdvalid = NULL; + xmlDtd *dtd = NULL; + xmlParserInputBufferPtr buf = NULL; + /* This is a modified version of the xkeyboard-config xkb.dtd. That one + * requires modelList, layoutList and optionList, we + * allow for any of those to be missing. + */ + const char dtdstr[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!ELEMENT xkbConfigRegistry (modelList?, layoutList?, optionList?)>\n" + "<!ATTLIST xkbConfigRegistry version CDATA \"1.1\">\n" + "<!ELEMENT modelList (model*)>\n" + "<!ELEMENT model (configItem)>\n" + "<!ELEMENT layoutList (layout*)>\n" + "<!ELEMENT layout (configItem, variantList?)>\n" + "<!ELEMENT optionList (group*)>\n" + "<!ELEMENT variantList (variant*)>\n" + "<!ELEMENT variant (configItem)>\n" + "<!ELEMENT group (configItem, option*)>\n" + "<!ATTLIST group allowMultipleSelection (true|false) \"false\">\n" + "<!ELEMENT option (configItem)>\n" + "<!ELEMENT configItem (name, shortDescription?, description?, vendor?, countryList?, languageList?, hwList?)>\n" + "<!ATTLIST configItem popularity (standard|exotic) \"standard\">\n" + "<!ELEMENT name (#PCDATA)>\n" + "<!ELEMENT shortDescription (#PCDATA)>\n" + "<!ELEMENT description (#PCDATA)>\n" + "<!ELEMENT vendor (#PCDATA)>\n" + "<!ELEMENT countryList (iso3166Id+)>\n" + "<!ELEMENT iso3166Id (#PCDATA)>\n" + "<!ELEMENT languageList (iso639Id+)>\n" + "<!ELEMENT iso639Id (#PCDATA)>\n" + "<!ELEMENT hwList (hwId+)>\n" + "<!ELEMENT hwId (#PCDATA)>\n"; + + /* Note: do not use xmlParserInputBufferCreateStatic, it generates random + * DTD validity errors for unknown reasons */ + buf = xmlParserInputBufferCreateMem(dtdstr, sizeof(dtdstr), + XML_CHAR_ENCODING_UTF8); + if (!buf) + return false; + + dtd = xmlIOParseDTD(NULL, buf, XML_CHAR_ENCODING_UTF8); + if (!dtd) { + log_err(ctx, "Failed to load DTD\n"); + return false; + } + + dtdvalid = xmlNewValidCtxt(); + if (xmlValidateDtd(dtdvalid, doc, dtd)) + success = true; + + if (dtd) + xmlFreeDtd(dtd); + if (dtdvalid) + xmlFreeValidCtxt(dtdvalid); + + return success; +} + +static bool +parse(struct rxkb_context *ctx, const char *path, + enum rxkb_popularity popularity) +{ + bool success = false; + xmlDoc *doc = NULL; + xmlNode *root = NULL; + + if (!check_eaccess(path, R_OK)) + return false; + + LIBXML_TEST_VERSION + + xmlSetGenericErrorFunc(ctx, xml_error_func); + + doc = xmlParseFile(path); + if (!doc) + return false; + + if (!validate(ctx, doc)) { + log_err(ctx, "XML error: failed to validate document at %s\n", path); + goto error; + } + + root = xmlDocGetRootElement(doc); + parse_rules_xml(ctx, root, popularity); + + success = true; +error: + xmlFreeDoc(doc); + xmlCleanupParser(); + + return success; +} diff --git a/src/scanner-utils.h b/src/scanner-utils.h new file mode 100644 index 0000000..dbf4eaa --- /dev/null +++ b/src/scanner-utils.h @@ -0,0 +1,195 @@ +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef XKBCOMP_SCANNER_UTILS_H +#define XKBCOMP_SCANNER_UTILS_H + +/* Point to some substring in the file; used to avoid copying. */ +struct sval { + const char *start; + unsigned int len; +}; +typedef darray(struct sval) darray_sval; + +static inline bool +svaleq(struct sval s1, struct sval s2) +{ + return s1.len == s2.len && memcmp(s1.start, s2.start, s1.len) == 0; +} + +static inline bool +svaleq_prefix(struct sval s1, struct sval s2) +{ + return s1.len <= s2.len && memcmp(s1.start, s2.start, s1.len) == 0; +} + +struct scanner { + const char *s; + size_t pos; + size_t len; + char buf[1024]; + size_t buf_pos; + size_t line, column; + /* The line/column of the start of the current token. */ + size_t token_line, token_column; + const char *file_name; + struct xkb_context *ctx; + void *priv; +}; + +#define scanner_log(scanner, level, fmt, ...) \ + xkb_log((scanner)->ctx, (level), 0, \ + "%s:%zu:%zu: " fmt "\n", \ + (scanner)->file_name, \ + (scanner)->token_line, (scanner)->token_column, ##__VA_ARGS__) + +#define scanner_err(scanner, fmt, ...) \ + scanner_log(scanner, XKB_LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define scanner_warn(scanner, fmt, ...) \ + scanner_log(scanner, XKB_LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__) + +static inline void +scanner_init(struct scanner *s, struct xkb_context *ctx, + const char *string, size_t len, const char *file_name, + void *priv) +{ + s->s = string; + s->len = len; + s->pos = 0; + s->line = s->column = 1; + s->token_line = s->token_column = 1; + s->file_name = file_name; + s->ctx = ctx; + s->priv = priv; +} + +static inline char +peek(struct scanner *s) +{ + if (unlikely(s->pos >= s->len)) + return '\0'; + return s->s[s->pos]; +} + +static inline bool +eof(struct scanner *s) +{ + return s->pos >= s->len; +} + +static inline bool +eol(struct scanner *s) +{ + return peek(s) == '\n'; +} + +static inline void +skip_to_eol(struct scanner *s) +{ + const char *nl = memchr(s->s + s->pos, '\n', s->len - s->pos); + const size_t new_pos = nl ? (size_t) (nl - s->s) : s->len; + s->column += new_pos - s->pos; + s->pos = new_pos; +} + +static inline char +next(struct scanner *s) +{ + if (unlikely(eof(s))) + return '\0'; + if (unlikely(eol(s))) { + s->line++; + s->column = 1; + } + else { + s->column++; + } + return s->s[s->pos++]; +} + +static inline bool +chr(struct scanner *s, char ch) +{ + if (likely(peek(s) != ch)) + return false; + s->pos++; s->column++; + return true; +} + +static inline bool +str(struct scanner *s, const char *string, size_t len) +{ + if (s->len - s->pos < len) + return false; + if (memcmp(s->s + s->pos, string, len) != 0) + return false; + s->pos += len; s->column += len; + return true; +} + +#define lit(s, literal) str(s, literal, sizeof(literal) - 1) + +static inline bool +buf_append(struct scanner *s, char ch) +{ + if (s->buf_pos + 1 >= sizeof(s->buf)) + return false; + s->buf[s->buf_pos++] = ch; + return true; +} + +static inline bool +buf_appends(struct scanner *s, const char *str) +{ + int ret; + ret = snprintf(s->buf + s->buf_pos, sizeof(s->buf) - s->buf_pos, "%s", str); + if (ret < 0 || (size_t) ret >= sizeof(s->buf) - s->buf_pos) + return false; + s->buf_pos += ret; + return true; +} + +static inline bool +oct(struct scanner *s, uint8_t *out) +{ + int i; + for (i = 0, *out = 0; peek(s) >= '0' && peek(s) <= '7' && i < 3; i++) + *out = *out * 8 + next(s) - '0'; + return i > 0; +} + +static inline bool +hex(struct scanner *s, uint8_t *out) +{ + int i; + for (i = 0, *out = 0; is_xdigit(peek(s)) && i < 2; i++) { + const char c = next(s); + const char offset = (c >= '0' && c <= '9' ? '0' : + c >= 'a' && c <= 'f' ? 'a' - 10 : 'A' - 10); + *out = *out * 16 + c - offset; + } + return i > 0; +} + +#endif diff --git a/src/state.c b/src/state.c new file mode 100644 index 0000000..b269e6d --- /dev/null +++ b/src/state.c @@ -0,0 +1,1437 @@ +/************************************************************ + * Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +/* + * This is a bastardised version of xkbActions.c from the X server which + * does not support, for the moment: + * - AccessX sticky/debounce/etc (will come later) + * - pointer keys (may come later) + * - key redirects (unlikely) + * - messages (very unlikely) + */ + +#include "config.h" + +#include "keymap.h" +#include "keysym.h" +#include "utf8.h" + +struct xkb_filter { + union xkb_action action; + const struct xkb_key *key; + uint32_t priv; + bool (*func)(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction); + int refcnt; +}; + +struct state_components { + /* These may be negative, because of -1 group actions. */ + int32_t base_group; /**< depressed */ + int32_t latched_group; + int32_t locked_group; + xkb_layout_index_t group; /**< effective */ + + xkb_mod_mask_t base_mods; /**< depressed */ + xkb_mod_mask_t latched_mods; + xkb_mod_mask_t locked_mods; + xkb_mod_mask_t mods; /**< effective */ + + xkb_led_mask_t leds; +}; + +struct xkb_state { + /* + * Before updating the state, we keep a copy of just this struct. This + * allows us to report which components of the state have changed. + */ + struct state_components components; + + /* + * At each event, we accumulate all the needed modifications to the base + * modifiers, and apply them at the end. These keep track of this state. + */ + xkb_mod_mask_t set_mods; + xkb_mod_mask_t clear_mods; + + /* + * We mustn't clear a base modifier if there's another depressed key + * which affects it, e.g. given this sequence + * < Left Shift down, Right Shift down, Left Shift Up > + * the modifier should still be set. This keeps the count. + */ + int16_t mod_key_count[XKB_MAX_MODS]; + + int refcnt; + darray(struct xkb_filter) filters; + struct xkb_keymap *keymap; +}; + +static const struct xkb_key_type_entry * +get_entry_for_mods(const struct xkb_key_type *type, xkb_mod_mask_t mods) +{ + for (unsigned i = 0; i < type->num_entries; i++) + if (entry_is_active(&type->entries[i]) && + type->entries[i].mods.mask == mods) + return &type->entries[i]; + return NULL; +} + +static const struct xkb_key_type_entry * +get_entry_for_key_state(struct xkb_state *state, const struct xkb_key *key, + xkb_layout_index_t group) +{ + const struct xkb_key_type *type = key->groups[group].type; + xkb_mod_mask_t active_mods = state->components.mods & type->mods.mask; + return get_entry_for_mods(type, active_mods); +} + +/** + * Returns the level to use for the given key and state, or + * XKB_LEVEL_INVALID. + */ +XKB_EXPORT xkb_level_index_t +xkb_state_key_get_level(struct xkb_state *state, xkb_keycode_t kc, + xkb_layout_index_t layout) +{ + const struct xkb_key *key = XkbKey(state->keymap, kc); + const struct xkb_key_type_entry *entry; + + if (!key || layout >= key->num_groups) + return XKB_LEVEL_INVALID; + + /* If we don't find an explicit match the default is 0. */ + entry = get_entry_for_key_state(state, key, layout); + if (!entry) + return 0; + + return entry->level; +} + +xkb_layout_index_t +XkbWrapGroupIntoRange(int32_t group, + xkb_layout_index_t num_groups, + enum xkb_range_exceed_type out_of_range_group_action, + xkb_layout_index_t out_of_range_group_number) +{ + if (num_groups == 0) + return XKB_LAYOUT_INVALID; + + if (group >= 0 && (xkb_layout_index_t) group < num_groups) + return group; + + switch (out_of_range_group_action) { + case RANGE_REDIRECT: + if (out_of_range_group_number >= num_groups) + return 0; + return out_of_range_group_number; + + case RANGE_SATURATE: + if (group < 0) + return 0; + else + return num_groups - 1; + + case RANGE_WRAP: + default: + /* + * C99 says a negative dividend in a modulo operation always + * gives a negative result. + */ + if (group < 0) + return ((int) num_groups + (group % (int) num_groups)); + else + return group % num_groups; + } +} + +/** + * Returns the layout to use for the given key and state, taking + * wrapping/clamping/etc into account, or XKB_LAYOUT_INVALID. + */ +XKB_EXPORT xkb_layout_index_t +xkb_state_key_get_layout(struct xkb_state *state, xkb_keycode_t kc) +{ + const struct xkb_key *key = XkbKey(state->keymap, kc); + + if (!key) + return XKB_LAYOUT_INVALID; + + return XkbWrapGroupIntoRange(state->components.group, key->num_groups, + key->out_of_range_group_action, + key->out_of_range_group_number); +} + +static const union xkb_action * +xkb_key_get_action(struct xkb_state *state, const struct xkb_key *key) +{ + static const union xkb_action dummy = { .type = ACTION_TYPE_NONE }; + + xkb_layout_index_t layout; + xkb_level_index_t level; + + layout = xkb_state_key_get_layout(state, key->keycode); + if (layout == XKB_LAYOUT_INVALID) + return &dummy; + + level = xkb_state_key_get_level(state, key->keycode, layout); + if (level == XKB_LEVEL_INVALID) + return &dummy; + + return &key->groups[layout].levels[level].action; +} + +static struct xkb_filter * +xkb_filter_new(struct xkb_state *state) +{ + struct xkb_filter *filter = NULL, *iter; + + darray_foreach(iter, state->filters) { + if (iter->func) + continue; + filter = iter; + break; + } + + if (!filter) { + darray_resize0(state->filters, darray_size(state->filters) + 1); + filter = &darray_item(state->filters, darray_size(state->filters) -1); + } + + filter->refcnt = 1; + return filter; +} + +/***====================================================================***/ + +enum xkb_filter_result { + /* + * The event is consumed by the filters. + * + * An event is always processed by all filters, but any filter can + * prevent it from being processed further by consuming it. + */ + XKB_FILTER_CONSUME, + /* + * The event may continue to be processed as far as this filter is + * concerned. + */ + XKB_FILTER_CONTINUE, +}; + +static void +xkb_filter_group_set_new(struct xkb_state *state, struct xkb_filter *filter) +{ + filter->priv = state->components.base_group; + if (filter->action.group.flags & ACTION_ABSOLUTE_SWITCH) + state->components.base_group = filter->action.group.group; + else + state->components.base_group += filter->action.group.group; +} + +static bool +xkb_filter_group_set_func(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + if (key != filter->key) { + filter->action.group.flags &= ~ACTION_LOCK_CLEAR; + return XKB_FILTER_CONTINUE; + } + + if (direction == XKB_KEY_DOWN) { + filter->refcnt++; + return XKB_FILTER_CONSUME; + } + else if (--filter->refcnt > 0) { + return XKB_FILTER_CONSUME; + } + + state->components.base_group = filter->priv; + + if (filter->action.group.flags & ACTION_LOCK_CLEAR) + state->components.locked_group = 0; + + filter->func = NULL; + return XKB_FILTER_CONTINUE; +} + +static void +xkb_filter_group_lock_new(struct xkb_state *state, struct xkb_filter *filter) +{ + if (filter->action.group.flags & ACTION_ABSOLUTE_SWITCH) + state->components.locked_group = filter->action.group.group; + else + state->components.locked_group += filter->action.group.group; +} + +static bool +xkb_filter_group_lock_func(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + if (key != filter->key) + return XKB_FILTER_CONTINUE; + + if (direction == XKB_KEY_DOWN) { + filter->refcnt++; + return XKB_FILTER_CONSUME; + } + if (--filter->refcnt > 0) + return XKB_FILTER_CONSUME; + + filter->func = NULL; + return XKB_FILTER_CONTINUE; +} + +static void +xkb_filter_mod_set_new(struct xkb_state *state, struct xkb_filter *filter) +{ + state->set_mods = filter->action.mods.mods.mask; +} + +static bool +xkb_filter_mod_set_func(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + if (key != filter->key) { + filter->action.mods.flags &= ~ACTION_LOCK_CLEAR; + return XKB_FILTER_CONTINUE; + } + + if (direction == XKB_KEY_DOWN) { + filter->refcnt++; + return XKB_FILTER_CONSUME; + } + else if (--filter->refcnt > 0) { + return XKB_FILTER_CONSUME; + } + + state->clear_mods = filter->action.mods.mods.mask; + if (filter->action.mods.flags & ACTION_LOCK_CLEAR) + state->components.locked_mods &= ~filter->action.mods.mods.mask; + + filter->func = NULL; + return XKB_FILTER_CONTINUE; +} + +static void +xkb_filter_mod_lock_new(struct xkb_state *state, struct xkb_filter *filter) +{ + filter->priv = (state->components.locked_mods & + filter->action.mods.mods.mask); + state->set_mods |= filter->action.mods.mods.mask; + if (!(filter->action.mods.flags & ACTION_LOCK_NO_LOCK)) + state->components.locked_mods |= filter->action.mods.mods.mask; +} + +static bool +xkb_filter_mod_lock_func(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + if (key != filter->key) + return XKB_FILTER_CONTINUE; + + if (direction == XKB_KEY_DOWN) { + filter->refcnt++; + return XKB_FILTER_CONSUME; + } + if (--filter->refcnt > 0) + return XKB_FILTER_CONSUME; + + state->clear_mods |= filter->action.mods.mods.mask; + if (!(filter->action.mods.flags & ACTION_LOCK_NO_UNLOCK)) + state->components.locked_mods &= ~filter->priv; + + filter->func = NULL; + return XKB_FILTER_CONTINUE; +} + +enum xkb_key_latch_state { + NO_LATCH, + LATCH_KEY_DOWN, + LATCH_PENDING, +}; + +static bool +xkb_action_breaks_latch(const union xkb_action *action) +{ + switch (action->type) { + case ACTION_TYPE_NONE: + case ACTION_TYPE_PTR_BUTTON: + case ACTION_TYPE_PTR_LOCK: + case ACTION_TYPE_CTRL_SET: + case ACTION_TYPE_CTRL_LOCK: + case ACTION_TYPE_SWITCH_VT: + case ACTION_TYPE_TERMINATE: + return true; + default: + return false; + } +} + +static void +xkb_filter_mod_latch_new(struct xkb_state *state, struct xkb_filter *filter) +{ + filter->priv = LATCH_KEY_DOWN; + state->set_mods = filter->action.mods.mods.mask; +} + +static bool +xkb_filter_mod_latch_func(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + enum xkb_key_latch_state latch = filter->priv; + + if (direction == XKB_KEY_DOWN && latch == LATCH_PENDING) { + /* If this is a new keypress and we're awaiting our single latched + * keypress, then either break the latch if any random key is pressed, + * or promote it to a lock or plain base set if it's the same + * modifier. */ + const union xkb_action *action = xkb_key_get_action(state, key); + if (action->type == ACTION_TYPE_MOD_LATCH && + action->mods.flags == filter->action.mods.flags && + action->mods.mods.mask == filter->action.mods.mods.mask) { + filter->action = *action; + if (filter->action.mods.flags & ACTION_LATCH_TO_LOCK) { + filter->action.type = ACTION_TYPE_MOD_LOCK; + filter->func = xkb_filter_mod_lock_func; + state->components.locked_mods |= filter->action.mods.mods.mask; + } + else { + filter->action.type = ACTION_TYPE_MOD_SET; + filter->func = xkb_filter_mod_set_func; + state->set_mods = filter->action.mods.mods.mask; + } + filter->key = key; + state->components.latched_mods &= ~filter->action.mods.mods.mask; + /* XXX beep beep! */ + return XKB_FILTER_CONSUME; + } + else if (xkb_action_breaks_latch(action)) { + /* XXX: This may be totally broken, we might need to break the + * latch in the next run after this press? */ + state->components.latched_mods &= ~filter->action.mods.mods.mask; + filter->func = NULL; + return XKB_FILTER_CONTINUE; + } + } + else if (direction == XKB_KEY_UP && key == filter->key) { + /* Our key got released. If we've set it to clear locks, and we + * currently have the same modifiers locked, then release them and + * don't actually latch. Else we've actually hit the latching + * stage, so set PENDING and move our modifier from base to + * latched. */ + if (latch == NO_LATCH || + ((filter->action.mods.flags & ACTION_LOCK_CLEAR) && + (state->components.locked_mods & filter->action.mods.mods.mask) == + filter->action.mods.mods.mask)) { + /* XXX: We might be a bit overenthusiastic about clearing + * mods other filters have set here? */ + if (latch == LATCH_PENDING) + state->components.latched_mods &= + ~filter->action.mods.mods.mask; + else + state->clear_mods = filter->action.mods.mods.mask; + state->components.locked_mods &= ~filter->action.mods.mods.mask; + filter->func = NULL; + } + else { + latch = LATCH_PENDING; + state->clear_mods = filter->action.mods.mods.mask; + state->components.latched_mods |= filter->action.mods.mods.mask; + /* XXX beep beep! */ + } + } + else if (direction == XKB_KEY_DOWN && latch == LATCH_KEY_DOWN) { + /* Someone's pressed another key while we've still got the latching + * key held down, so keep the base modifier state active (from + * xkb_filter_mod_latch_new), but don't trip the latch, just clear + * it as soon as the modifier gets released. */ + latch = NO_LATCH; + } + + filter->priv = latch; + + return XKB_FILTER_CONTINUE; +} + +static const struct { + void (*new)(struct xkb_state *state, struct xkb_filter *filter); + bool (*func)(struct xkb_state *state, struct xkb_filter *filter, + const struct xkb_key *key, enum xkb_key_direction direction); +} filter_action_funcs[_ACTION_TYPE_NUM_ENTRIES] = { + [ACTION_TYPE_MOD_SET] = { xkb_filter_mod_set_new, + xkb_filter_mod_set_func }, + [ACTION_TYPE_MOD_LATCH] = { xkb_filter_mod_latch_new, + xkb_filter_mod_latch_func }, + [ACTION_TYPE_MOD_LOCK] = { xkb_filter_mod_lock_new, + xkb_filter_mod_lock_func }, + [ACTION_TYPE_GROUP_SET] = { xkb_filter_group_set_new, + xkb_filter_group_set_func }, + [ACTION_TYPE_GROUP_LOCK] = { xkb_filter_group_lock_new, + xkb_filter_group_lock_func }, +}; + +/** + * Applies any relevant filters to the key, first from the list of filters + * that are currently active, then if no filter has claimed the key, possibly + * apply a new filter from the key action. + */ +static void +xkb_filter_apply_all(struct xkb_state *state, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + struct xkb_filter *filter; + const union xkb_action *action; + bool consumed; + + /* First run through all the currently active filters and see if any of + * them have consumed this event. */ + consumed = false; + darray_foreach(filter, state->filters) { + if (!filter->func) + continue; + + if (filter->func(state, filter, key, direction) == XKB_FILTER_CONSUME) + consumed = true; + } + if (consumed || direction == XKB_KEY_UP) + return; + + action = xkb_key_get_action(state, key); + + /* + * It's possible for the keymap to set action->type explicitly, like so: + * interpret XF86_Next_VMode { + * action = Private(type=0x86, data="+VMode"); + * }; + * We don't handle those. + */ + if (action->type >= _ACTION_TYPE_NUM_ENTRIES) + return; + + if (!filter_action_funcs[action->type].new) + return; + + filter = xkb_filter_new(state); + filter->key = key; + filter->func = filter_action_funcs[action->type].func; + filter->action = *action; + filter_action_funcs[action->type].new(state, filter); +} + +XKB_EXPORT struct xkb_state * +xkb_state_new(struct xkb_keymap *keymap) +{ + struct xkb_state *ret; + + ret = calloc(sizeof(*ret), 1); + if (!ret) + return NULL; + + ret->refcnt = 1; + ret->keymap = xkb_keymap_ref(keymap); + + return ret; +} + +XKB_EXPORT struct xkb_state * +xkb_state_ref(struct xkb_state *state) +{ + state->refcnt++; + return state; +} + +XKB_EXPORT void +xkb_state_unref(struct xkb_state *state) +{ + if (!state || --state->refcnt > 0) + return; + + xkb_keymap_unref(state->keymap); + darray_free(state->filters); + free(state); +} + +XKB_EXPORT struct xkb_keymap * +xkb_state_get_keymap(struct xkb_state *state) +{ + return state->keymap; +} + +/** + * Update the LED state to match the rest of the xkb_state. + */ +static void +xkb_state_led_update_all(struct xkb_state *state) +{ + xkb_led_index_t idx; + const struct xkb_led *led; + + state->components.leds = 0; + + xkb_leds_enumerate(idx, led, state->keymap) { + xkb_mod_mask_t mod_mask = 0; + xkb_layout_mask_t group_mask = 0; + + if (led->which_mods != 0 && led->mods.mask != 0) { + if (led->which_mods & XKB_STATE_MODS_EFFECTIVE) + mod_mask |= state->components.mods; + if (led->which_mods & XKB_STATE_MODS_DEPRESSED) + mod_mask |= state->components.base_mods; + if (led->which_mods & XKB_STATE_MODS_LATCHED) + mod_mask |= state->components.latched_mods; + if (led->which_mods & XKB_STATE_MODS_LOCKED) + mod_mask |= state->components.locked_mods; + + if (led->mods.mask & mod_mask) { + state->components.leds |= (1u << idx); + continue; + } + } + + if (led->which_groups != 0 && led->groups != 0) { + if (led->which_groups & XKB_STATE_LAYOUT_EFFECTIVE) + group_mask |= (1u << state->components.group); + if (led->which_groups & XKB_STATE_LAYOUT_DEPRESSED) + group_mask |= (1u << state->components.base_group); + if (led->which_groups & XKB_STATE_LAYOUT_LATCHED) + group_mask |= (1u << state->components.latched_group); + if (led->which_groups & XKB_STATE_LAYOUT_LOCKED) + group_mask |= (1u << state->components.locked_group); + + if (led->groups & group_mask) { + state->components.leds |= (1u << idx); + continue; + } + } + + if (led->ctrls & state->keymap->enabled_ctrls) { + state->components.leds |= (1u << idx); + continue; + } + } +} + +/** + * Calculates the derived state (effective mods/group and LEDs) from an + * up-to-date xkb_state. + */ +static void +xkb_state_update_derived(struct xkb_state *state) +{ + xkb_layout_index_t wrapped; + + state->components.mods = (state->components.base_mods | + state->components.latched_mods | + state->components.locked_mods); + + /* TODO: Use groups_wrap control instead of always RANGE_WRAP. */ + + wrapped = XkbWrapGroupIntoRange(state->components.locked_group, + state->keymap->num_groups, + RANGE_WRAP, 0); + state->components.locked_group = + (wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped); + + wrapped = XkbWrapGroupIntoRange(state->components.base_group + + state->components.latched_group + + state->components.locked_group, + state->keymap->num_groups, + RANGE_WRAP, 0); + state->components.group = + (wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped); + + xkb_state_led_update_all(state); +} + +static enum xkb_state_component +get_state_component_changes(const struct state_components *a, + const struct state_components *b) +{ + xkb_mod_mask_t mask = 0; + + if (a->group != b->group) + mask |= XKB_STATE_LAYOUT_EFFECTIVE; + if (a->base_group != b->base_group) + mask |= XKB_STATE_LAYOUT_DEPRESSED; + if (a->latched_group != b->latched_group) + mask |= XKB_STATE_LAYOUT_LATCHED; + if (a->locked_group != b->locked_group) + mask |= XKB_STATE_LAYOUT_LOCKED; + if (a->mods != b->mods) + mask |= XKB_STATE_MODS_EFFECTIVE; + if (a->base_mods != b->base_mods) + mask |= XKB_STATE_MODS_DEPRESSED; + if (a->latched_mods != b->latched_mods) + mask |= XKB_STATE_MODS_LATCHED; + if (a->locked_mods != b->locked_mods) + mask |= XKB_STATE_MODS_LOCKED; + if (a->leds != b->leds) + mask |= XKB_STATE_LEDS; + + return mask; +} + +/** + * Given a particular key event, updates the state structure to reflect the + * new modifiers. + */ +XKB_EXPORT enum xkb_state_component +xkb_state_update_key(struct xkb_state *state, xkb_keycode_t kc, + enum xkb_key_direction direction) +{ + xkb_mod_index_t i; + xkb_mod_mask_t bit; + struct state_components prev_components; + const struct xkb_key *key = XkbKey(state->keymap, kc); + + if (!key) + return 0; + + prev_components = state->components; + + state->set_mods = 0; + state->clear_mods = 0; + + xkb_filter_apply_all(state, key, direction); + + for (i = 0, bit = 1; state->set_mods; i++, bit <<= 1) { + if (state->set_mods & bit) { + state->mod_key_count[i]++; + state->components.base_mods |= bit; + state->set_mods &= ~bit; + } + } + + for (i = 0, bit = 1; state->clear_mods; i++, bit <<= 1) { + if (state->clear_mods & bit) { + state->mod_key_count[i]--; + if (state->mod_key_count[i] <= 0) { + state->components.base_mods &= ~bit; + state->mod_key_count[i] = 0; + } + state->clear_mods &= ~bit; + } + } + + xkb_state_update_derived(state); + + return get_state_component_changes(&prev_components, &state->components); +} + +/** + * Updates the state from a set of explicit masks as gained from + * xkb_state_serialize_mods and xkb_state_serialize_groups. As noted in the + * documentation for these functions in xkbcommon.h, this round-trip is + * lossy, and should only be used to update a slave state mirroring the + * master, e.g. in a client/server window system. + */ +XKB_EXPORT enum xkb_state_component +xkb_state_update_mask(struct xkb_state *state, + xkb_mod_mask_t base_mods, + xkb_mod_mask_t latched_mods, + xkb_mod_mask_t locked_mods, + xkb_layout_index_t base_group, + xkb_layout_index_t latched_group, + xkb_layout_index_t locked_group) +{ + struct state_components prev_components; + xkb_mod_mask_t mask; + + prev_components = state->components; + + /* Only include modifiers which exist in the keymap. */ + mask = (xkb_mod_mask_t) ((1ull << xkb_keymap_num_mods(state->keymap)) - 1u); + + state->components.base_mods = base_mods & mask; + state->components.latched_mods = latched_mods & mask; + state->components.locked_mods = locked_mods & mask; + + /* Make sure the mods are fully resolved - since we get arbitrary + * input, they might not be. + * + * It might seem more reasonable to do this only for components.mods + * in xkb_state_update_derived(), rather than for each component + * seperately. That would allow to distinguish between "really" + * depressed mods (would be in MODS_DEPRESSED) and indirectly + * depressed to to a mapping (would only be in MODS_EFFECTIVE). + * However, the traditional behavior of xkb_state_update_key() is that + * if a vmod is depressed, its mappings are depressed with it; so we're + * expected to do the same here. Also, LEDs (usually) look if a real + * mod is locked, not just effective; otherwise it won't be lit. + * + * We OR here because mod_mask_get_effective() drops vmods. */ + state->components.base_mods |= + mod_mask_get_effective(state->keymap, state->components.base_mods); + state->components.latched_mods |= + mod_mask_get_effective(state->keymap, state->components.latched_mods); + state->components.locked_mods |= + mod_mask_get_effective(state->keymap, state->components.locked_mods); + + state->components.base_group = base_group; + state->components.latched_group = latched_group; + state->components.locked_group = locked_group; + + xkb_state_update_derived(state); + + return get_state_component_changes(&prev_components, &state->components); +} + +/** + * Provides the symbols to use for the given key and state. Returns the + * number of symbols pointed to in syms_out. + */ +XKB_EXPORT int +xkb_state_key_get_syms(struct xkb_state *state, xkb_keycode_t kc, + const xkb_keysym_t **syms_out) +{ + xkb_layout_index_t layout; + xkb_level_index_t level; + + layout = xkb_state_key_get_layout(state, kc); + if (layout == XKB_LAYOUT_INVALID) + goto err; + + level = xkb_state_key_get_level(state, kc, layout); + if (level == XKB_LEVEL_INVALID) + goto err; + + return xkb_keymap_key_get_syms_by_level(state->keymap, kc, layout, level, + syms_out); + +err: + *syms_out = NULL; + return 0; +} + +/* + * https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Lock_Modifier + */ +static bool +should_do_caps_transformation(struct xkb_state *state, xkb_keycode_t kc) +{ + xkb_mod_index_t caps = + xkb_keymap_mod_get_index(state->keymap, XKB_MOD_NAME_CAPS); + + return + xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) > 0 && + xkb_state_mod_index_is_consumed(state, kc, caps) == 0; +} + +/* + * https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Control_Modifier + */ +static bool +should_do_ctrl_transformation(struct xkb_state *state, xkb_keycode_t kc) +{ + xkb_mod_index_t ctrl = + xkb_keymap_mod_get_index(state->keymap, XKB_MOD_NAME_CTRL); + + return + xkb_state_mod_index_is_active(state, ctrl, XKB_STATE_MODS_EFFECTIVE) > 0 && + xkb_state_mod_index_is_consumed(state, kc, ctrl) == 0; +} + +/* Verbatim from libX11:src/xkb/XKBBind.c */ +static char +XkbToControl(char ch) +{ + char c = ch; + + if ((c >= '@' && c < '\177') || c == ' ') + c &= 0x1F; + else if (c == '2') + c = '\000'; + else if (c >= '3' && c <= '7') + c -= ('3' - '\033'); + else if (c == '8') + c = '\177'; + else if (c == '/') + c = '_' & 0x1F; + return c; +} + +/** + * Provides either exactly one symbol, or XKB_KEY_NoSymbol. + */ +XKB_EXPORT xkb_keysym_t +xkb_state_key_get_one_sym(struct xkb_state *state, xkb_keycode_t kc) +{ + const xkb_keysym_t *syms; + xkb_keysym_t sym; + int num_syms; + + num_syms = xkb_state_key_get_syms(state, kc, &syms); + if (num_syms != 1) + return XKB_KEY_NoSymbol; + + sym = syms[0]; + + if (should_do_caps_transformation(state, kc)) + sym = xkb_keysym_to_upper(sym); + + return sym; +} + +/* + * The caps and ctrl transformations require some special handling, + * so we cannot simply use xkb_state_get_one_sym() for them. + * In particular, if Control is set, we must try very hard to find + * some layout in which the keysym is ASCII and thus can be (maybe) + * converted to a control character. libX11 allows to disable this + * behavior with the XkbLC_ControlFallback (see XkbSetXlibControls(3)), + * but it is enabled by default, yippee. + */ +static xkb_keysym_t +get_one_sym_for_string(struct xkb_state *state, xkb_keycode_t kc) +{ + xkb_level_index_t level; + xkb_layout_index_t layout, num_layouts; + const xkb_keysym_t *syms; + int nsyms; + xkb_keysym_t sym; + + layout = xkb_state_key_get_layout(state, kc); + num_layouts = xkb_keymap_num_layouts_for_key(state->keymap, kc); + level = xkb_state_key_get_level(state, kc, layout); + if (layout == XKB_LAYOUT_INVALID || num_layouts == 0 || + level == XKB_LEVEL_INVALID) + return XKB_KEY_NoSymbol; + + nsyms = xkb_keymap_key_get_syms_by_level(state->keymap, kc, + layout, level, &syms); + if (nsyms != 1) + return XKB_KEY_NoSymbol; + sym = syms[0]; + + if (should_do_ctrl_transformation(state, kc) && sym > 127u) { + for (xkb_layout_index_t i = 0; i < num_layouts; i++) { + level = xkb_state_key_get_level(state, kc, i); + if (level == XKB_LEVEL_INVALID) + continue; + + nsyms = xkb_keymap_key_get_syms_by_level(state->keymap, kc, + i, level, &syms); + if (nsyms == 1 && syms[0] <= 127u) { + sym = syms[0]; + break; + } + } + } + + if (should_do_caps_transformation(state, kc)) { + sym = xkb_keysym_to_upper(sym); + } + + return sym; +} + +XKB_EXPORT int +xkb_state_key_get_utf8(struct xkb_state *state, xkb_keycode_t kc, + char *buffer, size_t size) +{ + xkb_keysym_t sym; + const xkb_keysym_t *syms; + int nsyms; + int offset; + char tmp[7]; + + sym = get_one_sym_for_string(state, kc); + if (sym != XKB_KEY_NoSymbol) { + nsyms = 1; syms = &sym; + } + else { + nsyms = xkb_state_key_get_syms(state, kc, &syms); + } + + /* Make sure not to truncate in the middle of a UTF-8 sequence. */ + offset = 0; + for (int i = 0; i < nsyms; i++) { + int ret = xkb_keysym_to_utf8(syms[i], tmp, sizeof(tmp)); + if (ret <= 0) + goto err_bad; + + ret--; + if ((size_t) (offset + ret) <= size) + memcpy(buffer + offset, tmp, ret); + offset += ret; + } + + if ((size_t) offset >= size) + goto err_trunc; + buffer[offset] = '\0'; + + if (!is_valid_utf8(buffer, offset)) + goto err_bad; + + if (offset == 1 && (unsigned int) buffer[0] <= 127u && + should_do_ctrl_transformation(state, kc)) + buffer[0] = XkbToControl(buffer[0]); + + return offset; + +err_trunc: + if (size > 0) + buffer[size - 1] = '\0'; + return offset; + +err_bad: + if (size > 0) + buffer[0] = '\0'; + return 0; +} + +XKB_EXPORT uint32_t +xkb_state_key_get_utf32(struct xkb_state *state, xkb_keycode_t kc) +{ + xkb_keysym_t sym; + uint32_t cp; + + sym = get_one_sym_for_string(state, kc); + cp = xkb_keysym_to_utf32(sym); + + if (cp <= 127u && should_do_ctrl_transformation(state, kc)) + cp = (uint32_t) XkbToControl((char) cp); + + return cp; +} + +/** + * Serialises the requested modifier state into an xkb_mod_mask_t, with all + * the same disclaimers as in xkb_state_update_mask. + */ +XKB_EXPORT xkb_mod_mask_t +xkb_state_serialize_mods(struct xkb_state *state, + enum xkb_state_component type) +{ + xkb_mod_mask_t ret = 0; + + if (type & XKB_STATE_MODS_EFFECTIVE) + return state->components.mods; + + if (type & XKB_STATE_MODS_DEPRESSED) + ret |= state->components.base_mods; + if (type & XKB_STATE_MODS_LATCHED) + ret |= state->components.latched_mods; + if (type & XKB_STATE_MODS_LOCKED) + ret |= state->components.locked_mods; + + return ret; +} + +/** + * Serialises the requested group state, with all the same disclaimers as + * in xkb_state_update_mask. + */ +XKB_EXPORT xkb_layout_index_t +xkb_state_serialize_layout(struct xkb_state *state, + enum xkb_state_component type) +{ + xkb_layout_index_t ret = 0; + + if (type & XKB_STATE_LAYOUT_EFFECTIVE) + return state->components.group; + + if (type & XKB_STATE_LAYOUT_DEPRESSED) + ret += state->components.base_group; + if (type & XKB_STATE_LAYOUT_LATCHED) + ret += state->components.latched_group; + if (type & XKB_STATE_LAYOUT_LOCKED) + ret += state->components.locked_group; + + return ret; +} + +/** + * Gets a modifier mask and returns the resolved effective mask; this + * is needed because some modifiers can also map to other modifiers, e.g. + * the "NumLock" modifier usually also sets the "Mod2" modifier. + */ +xkb_mod_mask_t +mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods) +{ + const struct xkb_mod *mod; + xkb_mod_index_t i; + xkb_mod_mask_t mask; + + /* The effective mask is only real mods for now. */ + mask = mods & MOD_REAL_MASK_ALL; + + xkb_mods_enumerate(i, mod, &keymap->mods) + if (mods & (1u << i)) + mask |= mod->mapping; + + return mask; +} + +/** + * Returns 1 if the given modifier is active with the specified type(s), 0 if + * not, or -1 if the modifier is invalid. + */ +XKB_EXPORT int +xkb_state_mod_index_is_active(struct xkb_state *state, + xkb_mod_index_t idx, + enum xkb_state_component type) +{ + if (idx >= xkb_keymap_num_mods(state->keymap)) + return -1; + + return !!(xkb_state_serialize_mods(state, type) & (1u << idx)); +} + +/** + * Helper function for xkb_state_mod_indices_are_active and + * xkb_state_mod_names_are_active. + */ +static bool +match_mod_masks(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + xkb_mod_mask_t wanted) +{ + xkb_mod_mask_t active = xkb_state_serialize_mods(state, type); + + if (!(match & XKB_STATE_MATCH_NON_EXCLUSIVE) && (active & ~wanted)) + return false; + + if (match & XKB_STATE_MATCH_ANY) + return active & wanted; + + return (active & wanted) == wanted; +} + +/** + * Returns 1 if the modifiers are active with the specified type(s), 0 if + * not, or -1 if any of the modifiers are invalid. + */ +XKB_EXPORT int +xkb_state_mod_indices_are_active(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + ...) +{ + va_list ap; + xkb_mod_mask_t wanted = 0; + int ret = 0; + xkb_mod_index_t num_mods = xkb_keymap_num_mods(state->keymap); + + va_start(ap, match); + while (1) { + xkb_mod_index_t idx = va_arg(ap, xkb_mod_index_t); + if (idx == XKB_MOD_INVALID) + break; + if (idx >= num_mods) { + ret = -1; + break; + } + wanted |= (1u << idx); + } + va_end(ap); + + if (ret == -1) + return ret; + + return match_mod_masks(state, type, match, wanted); +} + +/** + * Returns 1 if the given modifier is active with the specified type(s), 0 if + * not, or -1 if the modifier is invalid. + */ +XKB_EXPORT int +xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, + enum xkb_state_component type) +{ + xkb_mod_index_t idx = xkb_keymap_mod_get_index(state->keymap, name); + + if (idx == XKB_MOD_INVALID) + return -1; + + return xkb_state_mod_index_is_active(state, idx, type); +} + +/** + * Returns 1 if the modifiers are active with the specified type(s), 0 if + * not, or -1 if any of the modifiers are invalid. + */ +XKB_EXPORT ATTR_NULL_SENTINEL int +xkb_state_mod_names_are_active(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + ...) +{ + va_list ap; + xkb_mod_mask_t wanted = 0; + int ret = 0; + + va_start(ap, match); + while (1) { + xkb_mod_index_t idx; + const char *str = va_arg(ap, const char *); + if (str == NULL) + break; + idx = xkb_keymap_mod_get_index(state->keymap, str); + if (idx == XKB_MOD_INVALID) { + ret = -1; + break; + } + wanted |= (1u << idx); + } + va_end(ap); + + if (ret == -1) + return ret; + + return match_mod_masks(state, type, match, wanted); +} + +/** + * Returns 1 if the given group is active with the specified type(s), 0 if + * not, or -1 if the group is invalid. + */ +XKB_EXPORT int +xkb_state_layout_index_is_active(struct xkb_state *state, + xkb_layout_index_t idx, + enum xkb_state_component type) +{ + int ret = 0; + + if (idx >= state->keymap->num_groups) + return -1; + + if (type & XKB_STATE_LAYOUT_EFFECTIVE) + ret |= (state->components.group == idx); + if (type & XKB_STATE_LAYOUT_DEPRESSED) + ret |= (state->components.base_group == (int32_t) idx); + if (type & XKB_STATE_LAYOUT_LATCHED) + ret |= (state->components.latched_group == (int32_t) idx); + if (type & XKB_STATE_LAYOUT_LOCKED) + ret |= (state->components.locked_group == (int32_t) idx); + + return ret; +} + +/** + * Returns 1 if the given modifier is active with the specified type(s), 0 if + * not, or -1 if the modifier is invalid. + */ +XKB_EXPORT int +xkb_state_layout_name_is_active(struct xkb_state *state, const char *name, + enum xkb_state_component type) +{ + xkb_layout_index_t idx = xkb_keymap_layout_get_index(state->keymap, name); + + if (idx == XKB_LAYOUT_INVALID) + return -1; + + return xkb_state_layout_index_is_active(state, idx, type); +} + +/** + * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid. + */ +XKB_EXPORT int +xkb_state_led_index_is_active(struct xkb_state *state, xkb_led_index_t idx) +{ + if (idx >= state->keymap->num_leds || + state->keymap->leds[idx].name == XKB_ATOM_NONE) + return -1; + + return !!(state->components.leds & (1u << idx)); +} + +/** + * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid. + */ +XKB_EXPORT int +xkb_state_led_name_is_active(struct xkb_state *state, const char *name) +{ + xkb_led_index_t idx = xkb_keymap_led_get_index(state->keymap, name); + + if (idx == XKB_LED_INVALID) + return -1; + + return xkb_state_led_index_is_active(state, idx); +} + +/** + * See: + * - XkbTranslateKeyCode(3), mod_rtrn return value, from libX11. + * - MyEnhancedXkbTranslateKeyCode(), a modification of the above, from GTK+. + */ +static xkb_mod_mask_t +key_get_consumed(struct xkb_state *state, const struct xkb_key *key, + enum xkb_consumed_mode mode) +{ + const struct xkb_key_type *type; + const struct xkb_key_type_entry *matching_entry; + xkb_mod_mask_t preserve = 0; + xkb_layout_index_t group; + xkb_mod_mask_t consumed = 0; + + group = xkb_state_key_get_layout(state, key->keycode); + if (group == XKB_LAYOUT_INVALID) + return 0; + + type = key->groups[group].type; + + matching_entry = get_entry_for_key_state(state, key, group); + if (matching_entry) + preserve = matching_entry->preserve.mask; + + switch (mode) { + case XKB_CONSUMED_MODE_XKB: + consumed = type->mods.mask; + break; + + case XKB_CONSUMED_MODE_GTK: { + const struct xkb_key_type_entry *no_mods_entry; + xkb_level_index_t no_mods_leveli; + const struct xkb_level *no_mods_level, *level; + + no_mods_entry = get_entry_for_mods(type, 0); + no_mods_leveli = no_mods_entry ? no_mods_entry->level : 0; + no_mods_level = &key->groups[group].levels[no_mods_leveli]; + + for (unsigned i = 0; i < type->num_entries; i++) { + const struct xkb_key_type_entry *entry = &type->entries[i]; + if (!entry_is_active(entry)) + continue; + + level = &key->groups[group].levels[entry->level]; + if (XkbLevelsSameSyms(level, no_mods_level)) + continue; + + if (entry == matching_entry || one_bit_set(entry->mods.mask)) + consumed |= entry->mods.mask & ~entry->preserve.mask; + } + break; + } + } + + return consumed & ~preserve; +} + +XKB_EXPORT int +xkb_state_mod_index_is_consumed2(struct xkb_state *state, xkb_keycode_t kc, + xkb_mod_index_t idx, + enum xkb_consumed_mode mode) +{ + const struct xkb_key *key = XkbKey(state->keymap, kc); + + if (!key || idx >= xkb_keymap_num_mods(state->keymap)) + return -1; + + return !!((1u << idx) & key_get_consumed(state, key, mode)); +} + +XKB_EXPORT int +xkb_state_mod_index_is_consumed(struct xkb_state *state, xkb_keycode_t kc, + xkb_mod_index_t idx) +{ + return xkb_state_mod_index_is_consumed2(state, kc, idx, + XKB_CONSUMED_MODE_XKB); +} + +XKB_EXPORT xkb_mod_mask_t +xkb_state_mod_mask_remove_consumed(struct xkb_state *state, xkb_keycode_t kc, + xkb_mod_mask_t mask) +{ + const struct xkb_key *key = XkbKey(state->keymap, kc); + + if (!key) + return 0; + + return mask & ~key_get_consumed(state, key, XKB_CONSUMED_MODE_XKB); +} + +XKB_EXPORT xkb_mod_mask_t +xkb_state_key_get_consumed_mods2(struct xkb_state *state, xkb_keycode_t kc, + enum xkb_consumed_mode mode) +{ + const struct xkb_key *key; + + switch (mode) { + case XKB_CONSUMED_MODE_XKB: + case XKB_CONSUMED_MODE_GTK: + break; + default: + log_err_func(state->keymap->ctx, + "unrecognized consumed modifiers mode: %d\n", mode); + return 0; + } + + key = XkbKey(state->keymap, kc); + if (!key) + return 0; + + return key_get_consumed(state, key, mode); +} + +XKB_EXPORT xkb_mod_mask_t +xkb_state_key_get_consumed_mods(struct xkb_state *state, xkb_keycode_t kc) +{ + return xkb_state_key_get_consumed_mods2(state, kc, XKB_CONSUMED_MODE_XKB); +} diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..60edb03 --- /dev/null +++ b/src/text.c @@ -0,0 +1,348 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#include "config.h" + +#include "keymap.h" +#include "text.h" + +bool +LookupString(const LookupEntry tab[], const char *string, + unsigned int *value_rtrn) +{ + if (!string) + return false; + + for (const LookupEntry *entry = tab; entry->name; entry++) { + if (istreq(entry->name, string)) { + *value_rtrn = entry->value; + return true; + } + } + + return false; +} + +const char * +LookupValue(const LookupEntry tab[], unsigned int value) +{ + for (const LookupEntry *entry = tab; entry->name; entry++) + if (entry->value == value) + return entry->name; + + return NULL; +} + +const LookupEntry ctrlMaskNames[] = { + { "RepeatKeys", CONTROL_REPEAT }, + { "Repeat", CONTROL_REPEAT }, + { "AutoRepeat", CONTROL_REPEAT }, + { "SlowKeys", CONTROL_SLOW }, + { "BounceKeys", CONTROL_DEBOUNCE }, + { "StickyKeys", CONTROL_STICKY }, + { "MouseKeys", CONTROL_MOUSEKEYS }, + { "MouseKeysAccel", CONTROL_MOUSEKEYS_ACCEL }, + { "AccessXKeys", CONTROL_AX }, + { "AccessXTimeout", CONTROL_AX_TIMEOUT }, + { "AccessXFeedback", CONTROL_AX_FEEDBACK }, + { "AudibleBell", CONTROL_BELL }, + { "IgnoreGroupLock", CONTROL_IGNORE_GROUP_LOCK }, + { "all", CONTROL_ALL }, + { "none", 0 }, + { "Overlay1", 0 }, + { "Overlay2", 0 }, + { NULL, 0 } +}; + +const LookupEntry modComponentMaskNames[] = { + { "base", XKB_STATE_MODS_DEPRESSED }, + { "latched", XKB_STATE_MODS_LATCHED }, + { "locked", XKB_STATE_MODS_LOCKED }, + { "effective", XKB_STATE_MODS_EFFECTIVE }, + { "compat", XKB_STATE_MODS_EFFECTIVE }, + { "any", XKB_STATE_MODS_EFFECTIVE }, + { "none", 0 }, + { NULL, 0 } +}; + +const LookupEntry groupComponentMaskNames[] = { + { "base", XKB_STATE_LAYOUT_DEPRESSED }, + { "latched", XKB_STATE_LAYOUT_LATCHED }, + { "locked", XKB_STATE_LAYOUT_LOCKED }, + { "effective", XKB_STATE_LAYOUT_EFFECTIVE }, + { "any", XKB_STATE_LAYOUT_EFFECTIVE }, + { "none", 0 }, + { NULL, 0 } +}; + +const LookupEntry groupMaskNames[] = { + { "Group1", 0x01 }, + { "Group2", 0x02 }, + { "Group3", 0x04 }, + { "Group4", 0x08 }, + { "Group5", 0x10 }, + { "Group6", 0x20 }, + { "Group7", 0x40 }, + { "Group8", 0x80 }, + { "none", 0x00 }, + { "all", 0xff }, + { NULL, 0 } +}; + +const LookupEntry groupNames[] = { + { "Group1", 1 }, + { "Group2", 2 }, + { "Group3", 3 }, + { "Group4", 4 }, + { "Group5", 5 }, + { "Group6", 6 }, + { "Group7", 7 }, + { "Group8", 8 }, + { NULL, 0 } +}; + +const LookupEntry levelNames[] = { + { "Level1", 1 }, + { "Level2", 2 }, + { "Level3", 3 }, + { "Level4", 4 }, + { "Level5", 5 }, + { "Level6", 6 }, + { "Level7", 7 }, + { "Level8", 8 }, + { NULL, 0 } +}; + +const LookupEntry buttonNames[] = { + { "Button1", 1 }, + { "Button2", 2 }, + { "Button3", 3 }, + { "Button4", 4 }, + { "Button5", 5 }, + { "default", 0 }, + { NULL, 0 } +}; + +const LookupEntry useModMapValueNames[] = { + { "LevelOne", 1 }, + { "Level1", 1 }, + { "AnyLevel", 0 }, + { "any", 0 }, + { NULL, 0 } +}; + +const LookupEntry actionTypeNames[] = { + { "NoAction", ACTION_TYPE_NONE }, + { "SetMods", ACTION_TYPE_MOD_SET }, + { "LatchMods", ACTION_TYPE_MOD_LATCH }, + { "LockMods", ACTION_TYPE_MOD_LOCK }, + { "SetGroup", ACTION_TYPE_GROUP_SET }, + { "LatchGroup", ACTION_TYPE_GROUP_LATCH }, + { "LockGroup", ACTION_TYPE_GROUP_LOCK }, + { "MovePtr", ACTION_TYPE_PTR_MOVE }, + { "MovePointer", ACTION_TYPE_PTR_MOVE }, + { "PtrBtn", ACTION_TYPE_PTR_BUTTON }, + { "PointerButton", ACTION_TYPE_PTR_BUTTON }, + { "LockPtrBtn", ACTION_TYPE_PTR_LOCK }, + { "LockPtrButton", ACTION_TYPE_PTR_LOCK }, + { "LockPointerButton", ACTION_TYPE_PTR_LOCK }, + { "LockPointerBtn", ACTION_TYPE_PTR_LOCK }, + { "SetPtrDflt", ACTION_TYPE_PTR_DEFAULT }, + { "SetPointerDefault", ACTION_TYPE_PTR_DEFAULT }, + { "Terminate", ACTION_TYPE_TERMINATE }, + { "TerminateServer", ACTION_TYPE_TERMINATE }, + { "SwitchScreen", ACTION_TYPE_SWITCH_VT }, + { "SetControls", ACTION_TYPE_CTRL_SET }, + { "LockControls", ACTION_TYPE_CTRL_LOCK }, + { "Private", ACTION_TYPE_PRIVATE }, + /* deprecated actions below here - unused */ + { "RedirectKey", ACTION_TYPE_NONE }, + { "Redirect", ACTION_TYPE_NONE }, + { "ISOLock", ACTION_TYPE_NONE }, + { "ActionMessage", ACTION_TYPE_NONE }, + { "MessageAction", ACTION_TYPE_NONE }, + { "Message", ACTION_TYPE_NONE }, + { "DeviceBtn", ACTION_TYPE_NONE }, + { "DevBtn", ACTION_TYPE_NONE }, + { "DevButton", ACTION_TYPE_NONE }, + { "DeviceButton", ACTION_TYPE_NONE }, + { "LockDeviceBtn", ACTION_TYPE_NONE }, + { "LockDevBtn", ACTION_TYPE_NONE }, + { "LockDevButton", ACTION_TYPE_NONE }, + { "LockDeviceButton", ACTION_TYPE_NONE }, + { "DeviceValuator", ACTION_TYPE_NONE }, + { "DevVal", ACTION_TYPE_NONE }, + { "DeviceVal", ACTION_TYPE_NONE }, + { "DevValuator", ACTION_TYPE_NONE }, + { NULL, 0 }, +}; + +const LookupEntry symInterpretMatchMaskNames[] = { + { "NoneOf", MATCH_NONE }, + { "AnyOfOrNone", MATCH_ANY_OR_NONE }, + { "AnyOf", MATCH_ANY }, + { "AllOf", MATCH_ALL }, + { "Exactly", MATCH_EXACTLY }, + { NULL, 0 }, +}; + +const char * +ModIndexText(struct xkb_context *ctx, const struct xkb_mod_set *mods, + xkb_mod_index_t ndx) +{ + if (ndx == XKB_MOD_INVALID) + return "none"; + + if (ndx >= mods->num_mods) + return NULL; + + return xkb_atom_text(ctx, mods->mods[ndx].name); +} + +const char * +ActionTypeText(enum xkb_action_type type) +{ + const char *name = LookupValue(actionTypeNames, type); + return name ? name : "Private"; +} + +const char * +KeysymText(struct xkb_context *ctx, xkb_keysym_t sym) +{ + char *buffer = xkb_context_get_buffer(ctx, 64); + xkb_keysym_get_name(sym, buffer, 64); + return buffer; +} + +const char * +KeyNameText(struct xkb_context *ctx, xkb_atom_t name) +{ + const char *sname = xkb_atom_text(ctx, name); + size_t len = strlen_safe(sname) + 3; + char *buf = xkb_context_get_buffer(ctx, len); + snprintf(buf, len, "<%s>", strempty(sname)); + return buf; +} + +const char * +SIMatchText(enum xkb_match_operation type) +{ + return LookupValue(symInterpretMatchMaskNames, type); +} + +const char * +ModMaskText(struct xkb_context *ctx, const struct xkb_mod_set *mods, + xkb_mod_mask_t mask) +{ + char buf[1024] = {0}; + size_t pos = 0; + xkb_mod_index_t i; + const struct xkb_mod *mod; + + if (mask == 0) + return "none"; + + if (mask == MOD_REAL_MASK_ALL) + return "all"; + + xkb_mods_enumerate(i, mod, mods) { + int ret; + + if (!(mask & (1u << i))) + continue; + + ret = snprintf(buf + pos, sizeof(buf) - pos, "%s%s", + pos == 0 ? "" : "+", + xkb_atom_text(ctx, mod->name)); + if (ret <= 0 || pos + ret >= sizeof(buf)) + break; + else + pos += ret; + } + + return strcpy(xkb_context_get_buffer(ctx, pos + 1), buf); +} + +const char * +LedStateMaskText(struct xkb_context *ctx, enum xkb_state_component mask) +{ + char buf[1024]; + size_t pos = 0; + + if (mask == 0) + return "0"; + + for (unsigned i = 0; mask; i++) { + int ret; + + if (!(mask & (1u << i))) + continue; + + mask &= ~(1u << i); + + ret = snprintf(buf + pos, sizeof(buf) - pos, "%s%s", + pos == 0 ? "" : "+", + LookupValue(modComponentMaskNames, 1u << i)); + if (ret <= 0 || pos + ret >= sizeof(buf)) + break; + else + pos += ret; + } + + return strcpy(xkb_context_get_buffer(ctx, pos + 1), buf); +} + +const char * +ControlMaskText(struct xkb_context *ctx, enum xkb_action_controls mask) +{ + char buf[1024]; + size_t pos = 0; + + if (mask == 0) + return "none"; + + if (mask == CONTROL_ALL) + return "all"; + + for (unsigned i = 0; mask; i++) { + int ret; + + if (!(mask & (1u << i))) + continue; + + mask &= ~(1u << i); + + ret = snprintf(buf + pos, sizeof(buf) - pos, "%s%s", + pos == 0 ? "" : "+", + LookupValue(ctrlMaskNames, 1u << i)); + if (ret <= 0 || pos + ret >= sizeof(buf)) + break; + else + pos += ret; + } + + return strcpy(xkb_context_get_buffer(ctx, pos + 1), buf); +} diff --git a/src/text.h b/src/text.h new file mode 100644 index 0000000..584dea6 --- /dev/null +++ b/src/text.h @@ -0,0 +1,76 @@ +/* + * Copyright © 2009 Dan Nicholson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TEXT_H +#define TEXT_H + +typedef struct { + const char *name; + unsigned int value; +} LookupEntry; + +bool +LookupString(const LookupEntry tab[], const char *string, + unsigned int *value_rtrn); + +const char * +LookupValue(const LookupEntry tab[], unsigned int value); + +extern const LookupEntry ctrlMaskNames[]; +extern const LookupEntry modComponentMaskNames[]; +extern const LookupEntry groupComponentMaskNames[]; +extern const LookupEntry groupMaskNames[]; +extern const LookupEntry groupNames[]; +extern const LookupEntry levelNames[]; +extern const LookupEntry buttonNames[]; +extern const LookupEntry useModMapValueNames[]; +extern const LookupEntry actionTypeNames[]; +extern const LookupEntry symInterpretMatchMaskNames[]; + +const char * +ModMaskText(struct xkb_context *ctx, const struct xkb_mod_set *mods, + xkb_mod_mask_t mask); + +const char * +ModIndexText(struct xkb_context *ctx, const struct xkb_mod_set *mods, + xkb_mod_index_t ndx); + +const char * +ActionTypeText(enum xkb_action_type type); + +const char * +KeysymText(struct xkb_context *ctx, xkb_keysym_t sym); + +const char * +KeyNameText(struct xkb_context *ctx, xkb_atom_t name); + +const char * +SIMatchText(enum xkb_match_operation type); + +const char * +LedStateMaskText(struct xkb_context *ctx, enum xkb_state_component mask); + +const char * +ControlMaskText(struct xkb_context *ctx, enum xkb_action_controls mask); + +#endif /* TEXT_H */ diff --git a/src/utf8.c b/src/utf8.c new file mode 100644 index 0000000..15aa237 --- /dev/null +++ b/src/utf8.c @@ -0,0 +1,140 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2014 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rob Bradford <rob@linux.intel.com> + */ + +#include "config.h" + +#include <stddef.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "utf8.h" + +int +utf32_to_utf8(uint32_t unichar, char *buffer) +{ + int count, shift, length; + uint8_t head; + + if (unichar <= 0x007f) { + buffer[0] = unichar; + buffer[1] = '\0'; + return 2; + } + else if (unichar <= 0x07FF) { + length = 2; + head = 0xc0; + } + else if (unichar <= 0xffff) { + length = 3; + head = 0xe0; + } + else if (unichar <= 0x10ffff) { + length = 4; + head = 0xf0; + } + else { + buffer[0] = '\0'; + return 0; + } + + for (count = length - 1, shift = 0; count > 0; count--, shift += 6) + buffer[count] = 0x80 | ((unichar >> shift) & 0x3f); + + buffer[0] = head | ((unichar >> shift) & 0x3f); + buffer[length] = '\0'; + + return length + 1; +} + +bool +is_valid_utf8(const char *ss, size_t len) +{ + size_t i = 0; + size_t tail_bytes = 0; + const uint8_t *s = (const uint8_t *) ss; + + /* This beauty is from: + * The Unicode Standard Version 6.2 - Core Specification, Table 3.7 + * https://www.unicode.org/versions/Unicode6.2.0/ch03.pdf#G7404 + * We can optimize if needed. */ + while (i < len) + { + if (s[i] <= 0x7F) { + tail_bytes = 0; + } + else if (s[i] >= 0xC2 && s[i] <= 0xDF) { + tail_bytes = 1; + } + else if (s[i] == 0xE0) { + i++; + if (i >= len || !(s[i] >= 0xA0 && s[i] <= 0xBF)) + return false; + tail_bytes = 1; + } + else if (s[i] >= 0xE1 && s[i] <= 0xEC) { + tail_bytes = 2; + } + else if (s[i] == 0xED) { + i++; + if (i >= len || !(s[i] >= 0x80 && s[i] <= 0x9F)) + return false; + tail_bytes = 1; + } + else if (s[i] >= 0xEE && s[i] <= 0xEF) { + tail_bytes = 2; + } + else if (s[i] == 0xF0) { + i++; + if (i >= len || !(s[i] >= 0x90 && s[i] <= 0xBF)) + return false; + tail_bytes = 2; + } + else if (s[i] >= 0xF1 && s[i] <= 0xF3) { + tail_bytes = 3; + } + else if (s[i] == 0xF4) { + i++; + if (i >= len || !(s[i] >= 0x80 && s[i] <= 0x8F)) + return false; + tail_bytes = 2; + } + else { + return false; + } + + i++; + + while (i < len && tail_bytes > 0 && s[i] >= 0x80 && s[i] <= 0xBF) { + i++; + tail_bytes--; + } + + if (tail_bytes != 0) + return false; + } + + return true; +} diff --git a/src/utf8.h b/src/utf8.h new file mode 100644 index 0000000..6371cb5 --- /dev/null +++ b/src/utf8.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2014 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rob Bradford <rob@linux.intel.com> + */ + +#ifndef XKBCOMMON_UTF8_H +#define XKBCOMMON_UTF8_H + +int +utf32_to_utf8(uint32_t unichar, char *buffer); + +bool +is_valid_utf8(const char *ss, size_t len); + +#endif diff --git a/src/util-list.c b/src/util-list.c new file mode 100644 index 0000000..b6f5069 --- /dev/null +++ b/src/util-list.c @@ -0,0 +1,94 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2011 Intel Corporation + * Copyright © 2013-2015 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <assert.h> +#include <stddef.h> +#include <stdbool.h> + +#include "util-list.h" + +void +list_init(struct list *list) +{ + list->prev = list; + list->next = list; +} + +void +list_insert(struct list *list, struct list *elm) +{ + assert((list->next != NULL && list->prev != NULL) || + !"list->next|prev is NULL, possibly missing list_init()"); + assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) || + !"elm->next|prev is not NULL, list node used twice?"); + + elm->prev = list; + elm->next = list->next; + list->next = elm; + elm->next->prev = elm; +} + +void +list_append(struct list *list, struct list *elm) +{ + assert((list->next != NULL && list->prev != NULL) || + !"list->next|prev is NULL, possibly missing list_init()"); + assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) || + !"elm->next|prev is not NULL, list node used twice?"); + + elm->next = list; + elm->prev = list->prev; + list->prev = elm; + elm->prev->next = elm; +} + +void +list_remove(struct list *elm) +{ + assert((elm->next != NULL && elm->prev != NULL) || + !"list->next|prev is NULL, possibly missing list_init()"); + + elm->prev->next = elm->next; + elm->next->prev = elm->prev; + elm->next = NULL; + elm->prev = NULL; +} + +bool +list_empty(const struct list *list) +{ + assert((list->next != NULL && list->prev != NULL) || + !"list->next|prev is NULL, possibly missing list_init()"); + + return list->next == list; +} + +bool +list_is_last(const struct list *list, const struct list *elm) +{ + return elm->next == list; +} diff --git a/src/util-list.h b/src/util-list.h new file mode 100644 index 0000000..573dff7 --- /dev/null +++ b/src/util-list.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2011 Intel Corporation + * Copyright © 2013-2015 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "config.h" + +#include <stdbool.h> +#include <stddef.h> + +/* + * This list data structure is a verbatim copy from wayland-util.h from the + * Wayland project; except that wl_ prefix has been removed. + */ + +struct list { + struct list *prev; + struct list *next; +}; + +void list_init(struct list *list); +void list_insert(struct list *list, struct list *elm); +void list_append(struct list *list, struct list *elm); +void list_remove(struct list *elm); +bool list_empty(const struct list *list); +bool list_is_last(const struct list *list, const struct list *elm); + +#define container_of(ptr, type, member) \ + (__typeof__(type) *)((char *)(ptr) - \ + offsetof(__typeof__(type), member)) + +#define list_first_entry(head, pos, member) \ + container_of((head)->next, __typeof__(*pos), member) + +#define list_last_entry(head, pos, member) \ + container_of((head)->prev, __typeof__(*pos), member) + +#define list_for_each(pos, head, member) \ + for (pos = 0, pos = list_first_entry(head, pos, member); \ + &pos->member != (head); \ + pos = list_first_entry(&pos->member, pos, member)) + +#define list_for_each_safe(pos, tmp, head, member) \ + for (pos = 0, tmp = 0, \ + pos = list_first_entry(head, pos, member), \ + tmp = list_first_entry(&pos->member, tmp, member); \ + &pos->member != (head); \ + pos = tmp, \ + tmp = list_first_entry(&pos->member, tmp, member)) diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..dbb0662 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,210 @@ +/* + * Copyright © 2013 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "utils.h" + +#ifdef HAVE_MMAP + +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +bool +map_file(FILE *file, char **string_out, size_t *size_out) +{ + struct stat stat_buf; + int fd; + char *string; + + /* Make sure to keep the errno on failure! */ + fd = fileno(file); + if (fd < 0) + return false; + + if (fstat(fd, &stat_buf) != 0) + return false; + + string = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (string == MAP_FAILED) + return false; + + *string_out = string; + *size_out = stat_buf.st_size; + return true; +} + +void +unmap_file(char *str, size_t size) +{ + munmap(str, size); +} + +#else + +bool +map_file(FILE *file, char **string_out, size_t *size_out) +{ + long ret; + size_t ret_s; + char *string; + size_t size; + + /* Make sure to keep the errno on failure! */ + + ret = fseek(file, 0, SEEK_END); + if (ret != 0) + return false; + + ret = ftell(file); + if (ret < 0) + return false; + size = (size_t) ret; + + ret = fseek(file, 0, SEEK_SET); + if (ret < 0) + return false; + + string = malloc(size); + if (!string) + return false; + + ret_s = fread(string, 1, size, file); + if (ret_s < size) { + free(string); + return false; + } + + *string_out = string; + *size_out = size; + return true; +} + +void +unmap_file(char *str, size_t size) +{ + free(str); +} + +#endif + +// ASCII lower-case map. +static const unsigned char lower_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +// ASCII tolower (to avoid locale issues). +char +to_lower(char c) +{ + return (char) lower_map[(unsigned char) c]; +} + +// ASCII strcasecmp (to avoid locale issues). +int +istrcmp(const char *a, const char *b) +{ + for (size_t i = 0; ; i++) { + if (to_lower(a[i]) != to_lower(b[i])) + return (int) to_lower(a[i]) - (int) to_lower(b[i]); + if (!a[i]) + break; + } + return 0; +} + +// ASCII strncasecmp (to avoid locale issues). +int +istrncmp(const char *a, const char *b, size_t n) +{ + for (size_t i = 0; i < n; i++) { + if (to_lower(a[i]) != to_lower(b[i])) + return (int) to_lower(a[i]) - (int) to_lower(b[i]); + if (!a[i]) + break; + } + return 0; +} + +#if !(defined(HAVE_ASPRINTF) && HAVE_ASPRINTF) +int +asprintf(char **strp, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vasprintf(strp, fmt, ap); + va_end(ap); + return ret; +} + +# if !(defined(HAVE_VASPRINTF) && HAVE_VASPRINTF) +int +vasprintf(char **strp, const char *fmt, va_list ap) +{ + int ret; + char *buf; + va_list ap_copy; + + /* + * The value of the va_list parameter is undefined after the call to + * vsnprintf() returns: pass a copy to make sure "ap" remains valid. + */ + va_copy(ap_copy, ap); + ret = vsnprintf(NULL, 0, fmt, ap_copy); + va_end(ap_copy); + + if (ret < 0) + return ret; + + if (!(buf = malloc(ret + 1))) + return -1; + + if ((ret = vsnprintf(buf, ret + 1, fmt, ap)) < 0) { + free(buf); + return ret; + } + + *strp = buf; + return ret; +} +# endif /* !HAVE_VASPRINTF */ +#endif /* !HAVE_ASPRINTF */ diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..d9827c0 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,348 @@ +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef UTILS_H +#define UTILS_H 1 + +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#if defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS) +#include <unistd.h> +#else +/* Required on Windows where unistd.h doesn't exist */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +#define X_OK 1 /* Test for execute permission. */ +#define F_OK 0 /* Test for existence. */ +#endif + +#include "darray.h" + +#define STATIC_ASSERT(expr, message) do { \ + switch (0) { case 0: case (expr): ; } \ +} while (0) + +#define ARRAY_SIZE(arr) ((sizeof(arr) / sizeof(*(arr)))) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* Round up @a so it's divisible by @b. */ +#define ROUNDUP(a, b) (((a) + (b) - 1) / (b) * (b)) + +char +to_lower(char c); + +int +istrcmp(const char *a, const char *b); + +int +istrncmp(const char *a, const char *b, size_t n); + +static inline bool +streq(const char *s1, const char *s2) +{ + return strcmp(s1, s2) == 0; +} + +static inline bool +streq_null(const char *s1, const char *s2) +{ + if (s1 == NULL || s2 == NULL) + return s1 == s2; + return streq(s1, s2); +} + +static inline bool +streq_not_null(const char *s1, const char *s2) +{ + if (!s1 || !s2) + return false; + return streq(s1, s2); +} + +static inline bool +istreq(const char *s1, const char *s2) +{ + return istrcmp(s1, s2) == 0; +} + +static inline bool +istreq_prefix(const char *s1, const char *s2) +{ + return istrncmp(s1, s2, strlen(s1)) == 0; +} + +static inline char * +strdup_safe(const char *s) +{ + return s ? strdup(s) : NULL; +} + +static inline size_t +strlen_safe(const char *s) +{ + return s ? strlen(s) : 0; +} + +static inline bool +isempty(const char *s) +{ + return s == NULL || s[0] == '\0'; +} + +static inline const char * +strnull(const char *s) +{ + return s ? s : "(null)"; +} + +static inline const char * +strempty(const char *s) +{ + return s ? s : ""; +} + +static inline void * +memdup(const void *mem, size_t nmemb, size_t size) +{ + void *p = calloc(nmemb, size); + if (p) + memcpy(p, mem, nmemb * size); + return p; +} + +#if !(defined(HAVE_STRNDUP) && HAVE_STRNDUP) +static inline char * +strndup(const char *s, size_t n) +{ + size_t slen = strlen(s); + size_t len = MIN(slen, n); + char *p = malloc(len + 1); + if (!p) + return NULL; + memcpy(p, s, len); + p[len] = '\0'; + return p; +} +#endif + +/* ctype.h is locale-dependent and has other oddities. */ +static inline bool +is_space(char ch) +{ + return ch == ' ' || (ch >= '\t' && ch <= '\r'); +} + +static inline bool +is_alpha(char ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + +static inline bool +is_digit(char ch) +{ + return ch >= '0' && ch <= '9'; +} + +static inline bool +is_alnum(char ch) +{ + return is_alpha(ch) || is_digit(ch); +} + +static inline bool +is_xdigit(char ch) +{ + return + (ch >= '0' && ch <= '9') || + (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F'); +} + +static inline bool +is_graph(char ch) +{ + /* See table in ascii(7). */ + return ch >= '!' && ch <= '~'; +} + +/* + * Return the bit position of the most significant bit. + * Note: this is 1-based! It's more useful this way, and returns 0 when + * mask is all 0s. + */ +static inline unsigned +msb_pos(uint32_t mask) +{ + unsigned pos = 0; + while (mask) { + pos++; + mask >>= 1u; + } + return pos; +} + +static inline int +one_bit_set(uint32_t x) +{ + return x && (x & (x - 1)) == 0; +} + +bool +map_file(FILE *file, char **string_out, size_t *size_out); + +void +unmap_file(char *string, size_t size); + +static inline bool +check_eaccess(const char *path, int mode) +{ +#if defined(HAVE_EACCESS) + if (eaccess(path, mode) != 0) + return false; +#elif defined(HAVE_EUIDACCESS) + if (euidaccess(path, mode) != 0) + return false; +#endif + + return true; +} + +#if defined(HAVE_SECURE_GETENV) +# define secure_getenv secure_getenv +#elif defined(HAVE___SECURE_GETENV) +# define secure_getenv __secure_getenv +#else +# define secure_getenv getenv +#endif + +#if defined(HAVE___BUILTIN_EXPECT) +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + +/* Compiler Attributes */ + +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__CYGWIN__) +# define XKB_EXPORT __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define XKB_EXPORT __global +#else /* not gcc >= 4 and not Sun Studio >= 8 */ +# define XKB_EXPORT +#endif + +#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 203) +# define ATTR_PRINTF(x,y) __attribute__((__format__(__printf__, x, y))) +#else /* not gcc >= 2.3 */ +# define ATTR_PRINTF(x,y) +#endif + +#if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 205)) \ + || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) +# define ATTR_NORETURN __attribute__((__noreturn__)) +#else +# define ATTR_NORETURN +#endif /* GNUC */ + +#if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 296) +#define ATTR_MALLOC __attribute__((__malloc__)) +#else +#define ATTR_MALLOC +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +# define ATTR_NULL_SENTINEL __attribute__((__sentinel__)) +#else +# define ATTR_NULL_SENTINEL +#endif /* GNUC >= 4 */ + +#if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 295) +#define ATTR_PACKED __attribute__((__packed__)) +#else +#define ATTR_PACKED +#endif + +#if !(defined(HAVE_ASPRINTF) && HAVE_ASPRINTF) +int asprintf(char **strp, const char *fmt, ...) ATTR_PRINTF(2, 3); +# if !(defined(HAVE_VASPRINTF) && HAVE_VASPRINTF) +# include <stdarg.h> +int vasprintf(char **strp, const char *fmt, va_list ap); +# endif /* !HAVE_VASPRINTF */ +#endif /* !HAVE_ASPRINTF */ + +static inline bool +ATTR_PRINTF(3, 4) +snprintf_safe(char *buf, size_t sz, const char *format, ...) +{ + va_list ap; + int rc; + + va_start(ap, format); + rc = vsnprintf(buf, sz, format, ap); + va_end(ap); + + return rc >= 0 && (size_t)rc < sz; +} + +static inline char * +ATTR_PRINTF(1, 0) +vasprintf_safe(const char *fmt, va_list args) +{ + char *str; + int len; + + len = vasprintf(&str, fmt, args); + + if (len == -1) + return NULL; + + return str; +} + +/** + * A version of asprintf that returns the allocated string or NULL on error. + */ +static inline char * +ATTR_PRINTF(1, 2) +asprintf_safe(const char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + str = vasprintf_safe(fmt, args); + va_end(args); + + return str; +} + +#endif /* UTILS_H */ diff --git a/src/x11/keymap.c b/src/x11/keymap.c new file mode 100644 index 0000000..7369d5d --- /dev/null +++ b/src/x11/keymap.c @@ -0,0 +1,1179 @@ +/* + * Copyright © 2013 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "x11-priv.h" + +/* + * References for the lonesome traveler: + * Xkb protocol specification: + * https://www.x.org/releases/current/doc/kbproto/xkbproto.html + * The XCB xkb XML protocol file: + * /user/share/xcb/xkb.xml + * The XCB xkb header file: + * /usr/include/xcb/xkb.h + * The old kbproto header files: + * /usr/include/X11/extensions/XKB{,proto,str}.h + * Xlib XKB source code: + * <libX11>/src/xkb/XKBGetMap.c (and friends) + * X server XKB protocol handling: + * <xserver>/xkb/xkb.c + * Man pages: + * XkbGetMap(3), XkbGetCompatMap(3), etc. + */ + +/* Constants from /usr/include/X11/extensions/XKB.h */ +/* XkbNumModifiers. */ +#define NUM_REAL_MODS 8u +/* XkbNumVirtualMods. */ +#define NUM_VMODS 16u +/* XkbNoModifier. */ +#define NO_MODIFIER 0xff +/* XkbNumIndicators. */ +#define NUM_INDICATORS 32u +/* XkbAllIndicatorsMask. */ +#define ALL_INDICATORS_MASK 0xffffffff + +/* Some macros. Not very nice but it'd be worse without them. */ + +/* + * We try not to trust the server too much and be paranoid. If we get + * something which we definitely shouldn't, we fail. + */ +#define STRINGIFY(expr) #expr +#define FAIL_UNLESS(expr) do { \ + if (!(expr)) { \ + log_err(keymap->ctx, \ + "x11: failed to get keymap from X server: unmet condition in %s(): %s\n", \ + __func__, STRINGIFY(expr)); \ + goto fail; \ + } \ +} while (0) + +#define FAIL_IF_BAD_REPLY(reply, request_name) do { \ + if (!reply) { \ + log_err(keymap->ctx, \ + "x11: failed to get keymap from X server: %s request failed\n", \ + (request_name)); \ + goto fail; \ + } \ +} while (0) + +#define ALLOC_OR_FAIL(arr, nmemb) do { \ + if ((nmemb) > 0) { \ + (arr) = calloc((nmemb), sizeof(*(arr))); \ + if (!(arr)) \ + goto fail; \ + } \ +} while (0) + + +static xkb_mod_mask_t +translate_mods(uint8_t rmods, uint16_t vmods_low, uint16_t vmods_high) +{ + /* We represent mod masks in a single uint32_t value, with real mods + * first and vmods after (though we don't make these distinctions). */ + return + ((xkb_mod_mask_t) rmods) | + ((xkb_mod_mask_t) vmods_low << 8) | + ((xkb_mod_mask_t) vmods_high << 16); +} + +static enum xkb_action_controls +translate_controls_mask(uint32_t wire) +{ + enum xkb_action_controls ret = 0; + if (wire & XCB_XKB_BOOL_CTRL_REPEAT_KEYS) + ret |= CONTROL_REPEAT; + if (wire & XCB_XKB_BOOL_CTRL_SLOW_KEYS) + ret |= CONTROL_SLOW; + if (wire & XCB_XKB_BOOL_CTRL_BOUNCE_KEYS) + ret |= CONTROL_DEBOUNCE; + if (wire & XCB_XKB_BOOL_CTRL_STICKY_KEYS) + ret |= CONTROL_STICKY; + if (wire & XCB_XKB_BOOL_CTRL_MOUSE_KEYS) + ret |= CONTROL_MOUSEKEYS; + if (wire & XCB_XKB_BOOL_CTRL_MOUSE_KEYS_ACCEL) + ret |= CONTROL_MOUSEKEYS_ACCEL; + if (wire & XCB_XKB_BOOL_CTRL_ACCESS_X_KEYS) + ret |= CONTROL_AX; + if (wire & XCB_XKB_BOOL_CTRL_ACCESS_X_TIMEOUT_MASK) + ret |= CONTROL_AX_TIMEOUT; + if (wire & XCB_XKB_BOOL_CTRL_ACCESS_X_FEEDBACK_MASK) + ret |= CONTROL_AX_FEEDBACK; + if (wire & XCB_XKB_BOOL_CTRL_AUDIBLE_BELL_MASK) + ret |= CONTROL_BELL; + if (wire & XCB_XKB_BOOL_CTRL_IGNORE_GROUP_LOCK_MASK) + ret |= CONTROL_IGNORE_GROUP_LOCK; + /* Some controls are not supported and don't appear here. */ + return ret; +} + +static void +translate_action(union xkb_action *action, const xcb_xkb_action_t *wire) +{ + switch (wire->type) { + case XCB_XKB_SA_TYPE_SET_MODS: + action->type = ACTION_TYPE_MOD_SET; + + action->mods.mods.mods = translate_mods(wire->setmods.realMods, + wire->setmods.vmodsLow, + wire->setmods.vmodsHigh); + action->mods.mods.mask = translate_mods(wire->setmods.mask, 0, 0); + + if (wire->setmods.flags & XCB_XKB_SA_CLEAR_LOCKS) + action->mods.flags |= ACTION_LOCK_CLEAR; + if (wire->setmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) + action->mods.flags |= ACTION_LATCH_TO_LOCK; + if (wire->setmods.flags & XCB_XKB_SA_USE_MOD_MAP_MODS) + action->mods.flags |= ACTION_MODS_LOOKUP_MODMAP; + + break; + case XCB_XKB_SA_TYPE_LATCH_MODS: + action->type = ACTION_TYPE_MOD_LATCH; + + action->mods.mods.mods = translate_mods(wire->latchmods.realMods, + wire->latchmods.vmodsLow, + wire->latchmods.vmodsHigh); + action->mods.mods.mask = translate_mods(wire->latchmods.mask, 0, 0); + + if (wire->latchmods.flags & XCB_XKB_SA_CLEAR_LOCKS) + action->mods.flags |= ACTION_LOCK_CLEAR; + if (wire->latchmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) + action->mods.flags |= ACTION_LATCH_TO_LOCK; + if (wire->latchmods.flags & XCB_XKB_SA_USE_MOD_MAP_MODS) + action->mods.flags |= ACTION_MODS_LOOKUP_MODMAP; + + break; + case XCB_XKB_SA_TYPE_LOCK_MODS: + action->type = ACTION_TYPE_MOD_LOCK; + + action->mods.mods.mods = translate_mods(wire->lockmods.realMods, + wire->lockmods.vmodsLow, + wire->lockmods.vmodsHigh); + action->mods.mods.mask = translate_mods(wire->lockmods.mask, 0, 0); + + if (wire->lockmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_LOCK) + action->mods.flags |= ACTION_LOCK_NO_LOCK; + if (wire->lockmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_UNLOCK) + action->mods.flags |= ACTION_LOCK_NO_UNLOCK; + if (wire->lockmods.flags & XCB_XKB_SA_USE_MOD_MAP_MODS) + action->mods.flags |= ACTION_MODS_LOOKUP_MODMAP; + + break; + case XCB_XKB_SA_TYPE_SET_GROUP: + action->type = ACTION_TYPE_GROUP_SET; + + action->group.group = wire->setgroup.group; + + if (wire->setmods.flags & XCB_XKB_SA_CLEAR_LOCKS) + action->group.flags |= ACTION_LOCK_CLEAR; + if (wire->setmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) + action->group.flags |= ACTION_LATCH_TO_LOCK; + if (wire->setmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_GROUP_ABSOLUTE) + action->group.flags |= ACTION_ABSOLUTE_SWITCH; + + break; + case XCB_XKB_SA_TYPE_LATCH_GROUP: + action->type = ACTION_TYPE_GROUP_LATCH; + + action->group.group = wire->latchgroup.group; + + if (wire->latchmods.flags & XCB_XKB_SA_CLEAR_LOCKS) + action->group.flags |= ACTION_LOCK_CLEAR; + if (wire->latchmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) + action->group.flags |= ACTION_LATCH_TO_LOCK; + if (wire->latchmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_GROUP_ABSOLUTE) + action->group.flags |= ACTION_ABSOLUTE_SWITCH; + + break; + case XCB_XKB_SA_TYPE_LOCK_GROUP: + action->type = ACTION_TYPE_GROUP_LOCK; + + action->group.group = wire->lockgroup.group; + + if (wire->lockgroup.flags & XCB_XKB_SA_ISO_LOCK_FLAG_GROUP_ABSOLUTE) + action->group.flags |= ACTION_ABSOLUTE_SWITCH; + + break; + case XCB_XKB_SA_TYPE_MOVE_PTR: + action->type = ACTION_TYPE_PTR_MOVE; + + action->ptr.x = (int16_t) (wire->moveptr.xLow | ((uint16_t) wire->moveptr.xHigh << 8)); + action->ptr.y = (int16_t) (wire->moveptr.yLow | ((uint16_t) wire->moveptr.yHigh << 8)); + + if (!(wire->moveptr.flags & XCB_XKB_SA_MOVE_PTR_FLAG_NO_ACCELERATION)) + action->ptr.flags |= ACTION_ACCEL; + if (wire->moveptr.flags & XCB_XKB_SA_MOVE_PTR_FLAG_MOVE_ABSOLUTE_X) + action->ptr.flags |= ACTION_ABSOLUTE_X; + if (wire->moveptr.flags & XCB_XKB_SA_MOVE_PTR_FLAG_MOVE_ABSOLUTE_Y) + action->ptr.flags |= ACTION_ABSOLUTE_Y; + + break; + case XCB_XKB_SA_TYPE_PTR_BTN: + action->type = ACTION_TYPE_PTR_BUTTON; + + action->btn.count = wire->ptrbtn.count; + action->btn.button = wire->ptrbtn.button; + action->btn.flags = 0; + + break; + case XCB_XKB_SA_TYPE_LOCK_PTR_BTN: + action->type = ACTION_TYPE_PTR_LOCK; + + action->btn.button = wire->lockptrbtn.button; + + if (wire->lockptrbtn.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_LOCK) + action->btn.flags |= ACTION_LOCK_NO_LOCK; + if (wire->lockptrbtn.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_UNLOCK) + action->btn.flags |= ACTION_LOCK_NO_UNLOCK; + + break; + case XCB_XKB_SA_TYPE_SET_PTR_DFLT: + action->type = ACTION_TYPE_PTR_DEFAULT; + + action->dflt.value = wire->setptrdflt.value; + + if (wire->setptrdflt.flags & XCB_XKB_SA_SET_PTR_DFLT_FLAG_DFLT_BTN_ABSOLUTE) + action->dflt.flags |= ACTION_ABSOLUTE_SWITCH; + + break; + case XCB_XKB_SA_TYPE_TERMINATE: + action->type = ACTION_TYPE_TERMINATE; + + break; + case XCB_XKB_SA_TYPE_SWITCH_SCREEN: + action->type = ACTION_TYPE_SWITCH_VT; + + action->screen.screen = wire->switchscreen.newScreen; + + if (!(wire->switchscreen.flags & XCB_XKB_SWITCH_SCREEN_FLAG_APPLICATION)) + action->screen.flags |= ACTION_SAME_SCREEN; + if (wire->switchscreen.flags & XCB_XKB_SWITCH_SCREEN_FLAG_ABSOLUTE) + action->screen.flags |= ACTION_ABSOLUTE_SWITCH; + + break; + case XCB_XKB_SA_TYPE_SET_CONTROLS: + action->type = ACTION_TYPE_CTRL_SET; + { + const uint16_t mask = (wire->setcontrols.boolCtrlsLow | + (wire->setcontrols.boolCtrlsHigh << 8)); + action->ctrls.ctrls = translate_controls_mask(mask); + } + break; + case XCB_XKB_SA_TYPE_LOCK_CONTROLS: + action->type = ACTION_TYPE_CTRL_LOCK; + { + const uint16_t mask = (wire->lockcontrols.boolCtrlsLow | + (wire->lockcontrols.boolCtrlsHigh << 8)); + action->ctrls.ctrls = translate_controls_mask(mask); + } + break; + + case XCB_XKB_SA_TYPE_NO_ACTION: + /* We don't support these. */ + case XCB_XKB_SA_TYPE_ISO_LOCK: + case XCB_XKB_SA_TYPE_REDIRECT_KEY: + case XCB_XKB_SA_TYPE_ACTION_MESSAGE: + case XCB_XKB_SA_TYPE_DEVICE_BTN: + case XCB_XKB_SA_TYPE_LOCK_DEVICE_BTN: + case XCB_XKB_SA_TYPE_DEVICE_VALUATOR: + action->type = ACTION_TYPE_NONE; + break; + + default: + if (wire->type < ACTION_TYPE_PRIVATE) { + action->type = ACTION_TYPE_NONE; + break; + } + + /* Treat high unknown actions as Private actions. */ + action->priv.type = wire->noaction.type; + STATIC_ASSERT(sizeof(action->priv.data) == 7 && + sizeof(wire->noaction.pad0) == 7, + "The private action data must be 7 bytes long!"); + memcpy(action->priv.data, wire->noaction.pad0, 7); + break; + } +} + +static bool +get_types(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + int types_length = xcb_xkb_get_map_map_types_rtrn_length(reply, map); + xcb_xkb_key_type_iterator_t types_iter = + xcb_xkb_get_map_map_types_rtrn_iterator(reply, map); + + FAIL_UNLESS(reply->firstType == 0); + + keymap->num_types = reply->nTypes; + ALLOC_OR_FAIL(keymap->types, keymap->num_types); + + for (int i = 0; i < types_length; i++) { + xcb_xkb_key_type_t *wire_type = types_iter.data; + struct xkb_key_type *type = &keymap->types[i]; + + FAIL_UNLESS(wire_type->numLevels > 0); + + type->mods.mods = translate_mods(wire_type->mods_mods, + wire_type->mods_vmods, 0); + type->mods.mask = translate_mods(wire_type->mods_mask, 0, 0); + type->num_levels = wire_type->numLevels; + + { + int entries_length = xcb_xkb_key_type_map_length(wire_type); + xcb_xkb_kt_map_entry_iterator_t entries_iter = + xcb_xkb_key_type_map_iterator(wire_type); + + type->num_entries = wire_type->nMapEntries; + ALLOC_OR_FAIL(type->entries, type->num_entries); + + for (int j = 0; j < entries_length; j++) { + xcb_xkb_kt_map_entry_t *wire_entry = entries_iter.data; + struct xkb_key_type_entry *entry = &type->entries[j]; + + FAIL_UNLESS(wire_entry->level < type->num_levels); + + entry->level = wire_entry->level; + entry->mods.mods = translate_mods(wire_entry->mods_mods, + wire_entry->mods_vmods, 0); + entry->mods.mask = translate_mods(wire_entry->mods_mask, 0, 0); + + xcb_xkb_kt_map_entry_next(&entries_iter); + } + } + + { + int preserves_length = xcb_xkb_key_type_preserve_length(wire_type); + xcb_xkb_mod_def_iterator_t preserves_iter = + xcb_xkb_key_type_preserve_iterator(wire_type); + + FAIL_UNLESS((unsigned) preserves_length <= type->num_entries); + + for (int j = 0; j < preserves_length; j++) { + xcb_xkb_mod_def_t *wire_preserve = preserves_iter.data; + struct xkb_key_type_entry *entry = &type->entries[j]; + + entry->preserve.mods = translate_mods(wire_preserve->realMods, + wire_preserve->vmods, 0); + entry->preserve.mask = translate_mods(wire_preserve->mask, 0, 0); + + xcb_xkb_mod_def_next(&preserves_iter); + } + } + + xcb_xkb_key_type_next(&types_iter); + } + + return true; + +fail: + return false; +} + +static bool +get_sym_maps(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + int sym_maps_length = xcb_xkb_get_map_map_syms_rtrn_length(reply, map); + xcb_xkb_key_sym_map_iterator_t sym_maps_iter = + xcb_xkb_get_map_map_syms_rtrn_iterator(reply, map); + + FAIL_UNLESS(reply->minKeyCode <= reply->maxKeyCode); + FAIL_UNLESS(reply->firstKeySym >= reply->minKeyCode); + FAIL_UNLESS(reply->firstKeySym + reply->nKeySyms <= reply->maxKeyCode + 1); + + keymap->min_key_code = reply->minKeyCode; + keymap->max_key_code = reply->maxKeyCode; + + ALLOC_OR_FAIL(keymap->keys, keymap->max_key_code + 1); + + for (xkb_keycode_t kc = keymap->min_key_code; kc <= keymap->max_key_code; kc++) + keymap->keys[kc].keycode = kc; + + for (int i = 0; i < sym_maps_length; i++) { + xcb_xkb_key_sym_map_t *wire_sym_map = sym_maps_iter.data; + struct xkb_key *key = &keymap->keys[reply->firstKeySym + i]; + + key->num_groups = wire_sym_map->groupInfo & 0x0f; + FAIL_UNLESS(key->num_groups <= ARRAY_SIZE(wire_sym_map->kt_index)); + ALLOC_OR_FAIL(key->groups, key->num_groups); + + for (unsigned j = 0; j < key->num_groups; j++) { + FAIL_UNLESS(wire_sym_map->kt_index[j] < keymap->num_types); + key->groups[j].type = &keymap->types[wire_sym_map->kt_index[j]]; + + ALLOC_OR_FAIL(key->groups[j].levels, key->groups[j].type->num_levels); + } + + key->out_of_range_group_number = (wire_sym_map->groupInfo & 0x30) >> 4; + + FAIL_UNLESS(key->out_of_range_group_number <= key->num_groups); + + if (wire_sym_map->groupInfo & XCB_XKB_GROUPS_WRAP_CLAMP_INTO_RANGE) + key->out_of_range_group_action = RANGE_SATURATE; + else if (wire_sym_map->groupInfo & XCB_XKB_GROUPS_WRAP_REDIRECT_INTO_RANGE) + key->out_of_range_group_action = RANGE_REDIRECT; + else + key->out_of_range_group_action = RANGE_WRAP; + + { + int syms_length = xcb_xkb_key_sym_map_syms_length(wire_sym_map); + xcb_keysym_t *syms_iter = xcb_xkb_key_sym_map_syms(wire_sym_map); + + FAIL_UNLESS((unsigned) syms_length == wire_sym_map->width * key->num_groups); + + for (int j = 0; j < syms_length; j++) { + xcb_keysym_t wire_keysym = *syms_iter; + const xkb_layout_index_t group = j / wire_sym_map->width; + const xkb_level_index_t level = j % wire_sym_map->width; + + assert(key->groups[group].type != NULL); + if (level < key->groups[group].type->num_levels && + wire_keysym != XKB_KEY_NoSymbol) { + key->groups[group].levels[level].num_syms = 1; + key->groups[group].levels[level].u.sym = wire_keysym; + } + + syms_iter++; + } + } + + xcb_xkb_key_sym_map_next(&sym_maps_iter); + } + + return true; + +fail: + return false; +} + +static bool +get_actions(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + int acts_count_length = + xcb_xkb_get_map_map_acts_rtrn_count_length(reply, map); + uint8_t *acts_count_iter = xcb_xkb_get_map_map_acts_rtrn_count(map); + xcb_xkb_action_iterator_t acts_iter = + xcb_xkb_get_map_map_acts_rtrn_acts_iterator(reply, map); + xcb_xkb_key_sym_map_iterator_t sym_maps_iter = + xcb_xkb_get_map_map_syms_rtrn_iterator(reply, map); + + FAIL_UNLESS(reply->firstKeyAction == keymap->min_key_code); + FAIL_UNLESS(reply->firstKeyAction + reply->nKeyActions == + keymap->max_key_code + 1); + + for (int i = 0; i < acts_count_length; i++) { + xcb_xkb_key_sym_map_t *wire_sym_map = sym_maps_iter.data; + int syms_length = xcb_xkb_key_sym_map_syms_length(wire_sym_map); + uint8_t wire_count = *acts_count_iter; + struct xkb_key *key = &keymap->keys[reply->firstKeyAction + i]; + + FAIL_UNLESS(wire_count == 0 || wire_count == syms_length); + + for (int j = 0; j < wire_count; j++) { + xcb_xkb_action_t *wire_action = acts_iter.data; + const xkb_layout_index_t group = j / wire_sym_map->width; + const xkb_level_index_t level = j % wire_sym_map->width; + + if (level < key->groups[group].type->num_levels) { + union xkb_action *action = + &key->groups[group].levels[level].action; + + translate_action(action, wire_action); + } + + xcb_xkb_action_next(&acts_iter); + } + + acts_count_iter++; + xcb_xkb_key_sym_map_next(&sym_maps_iter); + } + + return true; + +fail: + return false; +} + +static bool +get_vmods(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + uint8_t *iter = xcb_xkb_get_map_map_vmods_rtrn(map); + + keymap->mods.num_mods = + NUM_REAL_MODS + MIN(msb_pos(reply->virtualMods), NUM_VMODS); + + for (unsigned i = 0; i < NUM_VMODS; i++) { + if (reply->virtualMods & (1u << i)) { + uint8_t wire = *iter; + struct xkb_mod *mod = &keymap->mods.mods[NUM_REAL_MODS + i]; + + mod->type = MOD_VIRT; + mod->mapping = translate_mods(wire, 0, 0); + + iter++; + } + } + + return true; +} + +static bool +get_explicits(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + int length = xcb_xkb_get_map_map_explicit_rtrn_length(reply, map); + xcb_xkb_set_explicit_iterator_t iter = + xcb_xkb_get_map_map_explicit_rtrn_iterator(reply, map); + + for (int i = 0; i < length; i++) { + xcb_xkb_set_explicit_t *wire = iter.data; + struct xkb_key *key; + + FAIL_UNLESS(wire->keycode >= keymap->min_key_code && + wire->keycode <= keymap->max_key_code); + + key = &keymap->keys[wire->keycode]; + + if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_1) && + key->num_groups > 0) + key->groups[0].explicit_type = true; + if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_2) && + key->num_groups > 1) + key->groups[1].explicit_type = true; + if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_3) && + key->num_groups > 2) + key->groups[2].explicit_type = true; + if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_4) && + key->num_groups > 3) + key->groups[3].explicit_type = true; + if (wire->explicit & XCB_XKB_EXPLICIT_INTERPRET) + key->explicit |= EXPLICIT_INTERP; + if (wire->explicit & XCB_XKB_EXPLICIT_AUTO_REPEAT) + key->explicit |= EXPLICIT_REPEAT; + if (wire->explicit & XCB_XKB_EXPLICIT_V_MOD_MAP) + key->explicit |= EXPLICIT_VMODMAP; + + xcb_xkb_set_explicit_next(&iter); + } + + return true; + +fail: + return false; +} + +static bool +get_modmaps(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + int length = xcb_xkb_get_map_map_modmap_rtrn_length(reply, map); + xcb_xkb_key_mod_map_iterator_t iter = + xcb_xkb_get_map_map_modmap_rtrn_iterator(reply, map); + + for (int i = 0; i < length; i++) { + xcb_xkb_key_mod_map_t *wire = iter.data; + struct xkb_key *key; + + FAIL_UNLESS(wire->keycode >= keymap->min_key_code && + wire->keycode <= keymap->max_key_code); + + key = &keymap->keys[wire->keycode]; + key->modmap = wire->mods; + + xcb_xkb_key_mod_map_next(&iter); + } + + return true; + +fail: + return false; +} + +static bool +get_vmodmaps(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) +{ + int length = xcb_xkb_get_map_map_vmodmap_rtrn_length(reply, map); + xcb_xkb_key_v_mod_map_iterator_t iter = + xcb_xkb_get_map_map_vmodmap_rtrn_iterator(reply, map); + + for (int i = 0; i < length; i++) { + xcb_xkb_key_v_mod_map_t *wire = iter.data; + struct xkb_key *key; + + FAIL_UNLESS(wire->keycode >= keymap->min_key_code && + wire->keycode <= keymap->max_key_code); + + key = &keymap->keys[wire->keycode]; + key->vmodmap = translate_mods(0, wire->vmods, 0); + + xcb_xkb_key_v_mod_map_next(&iter); + } + + return true; + +fail: + return false; +} + +static bool +get_map(struct xkb_keymap *keymap, xcb_connection_t *conn, uint16_t device_id) +{ + static const xcb_xkb_map_part_t required_components = + (XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); + + xcb_xkb_get_map_cookie_t cookie = + xcb_xkb_get_map(conn, device_id, required_components, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + xcb_xkb_get_map_reply_t *reply = xcb_xkb_get_map_reply(conn, cookie, NULL); + xcb_xkb_get_map_map_t map; + + FAIL_IF_BAD_REPLY(reply, "XkbGetMap"); + + if ((reply->present & required_components) != required_components) + goto fail; + + xcb_xkb_get_map_map_unpack(xcb_xkb_get_map_map(reply), + reply->nTypes, + reply->nKeySyms, + reply->nKeyActions, + reply->totalActions, + reply->totalKeyBehaviors, + reply->virtualMods, + reply->totalKeyExplicit, + reply->totalModMapKeys, + reply->totalVModMapKeys, + reply->present, + &map); + + if (!get_types(keymap, conn, reply, &map) || + !get_sym_maps(keymap, conn, reply, &map) || + !get_actions(keymap, conn, reply, &map) || + !get_vmods(keymap, conn, reply, &map) || + !get_explicits(keymap, conn, reply, &map) || + !get_modmaps(keymap, conn, reply, &map) || + !get_vmodmaps(keymap, conn, reply, &map)) + goto fail; + + free(reply); + return true; + +fail: + free(reply); + return false; +} + +static bool +get_indicators(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_indicator_map_reply_t *reply) +{ + xcb_xkb_indicator_map_iterator_t iter = + xcb_xkb_get_indicator_map_maps_iterator(reply); + + keymap->num_leds = msb_pos(reply->which); + + for (unsigned i = 0; i < NUM_INDICATORS; i++) { + if (reply->which & (1u << i)) { + xcb_xkb_indicator_map_t *wire = iter.data; + struct xkb_led *led = &keymap->leds[i]; + + if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_BASE) + led->which_groups |= XKB_STATE_LAYOUT_DEPRESSED; + if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_LATCHED) + led->which_groups |= XKB_STATE_LAYOUT_LATCHED; + if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_LOCKED) + led->which_groups |= XKB_STATE_LAYOUT_LOCKED; + if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_EFFECTIVE) + led->which_groups |= XKB_STATE_LAYOUT_EFFECTIVE; + if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_COMPAT) + led->which_groups |= XKB_STATE_LAYOUT_EFFECTIVE; + + led->groups = wire->groups; + + if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_BASE) + led->which_mods |= XKB_STATE_MODS_DEPRESSED; + if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_LATCHED) + led->which_mods |= XKB_STATE_MODS_LATCHED; + if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_LOCKED) + led->which_mods |= XKB_STATE_MODS_LOCKED; + if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_EFFECTIVE) + led->which_mods |= XKB_STATE_MODS_EFFECTIVE; + if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_COMPAT) + led->which_mods |= XKB_STATE_MODS_EFFECTIVE; + + led->mods.mods = translate_mods(wire->realMods, wire->vmods, 0); + led->mods.mask = translate_mods(wire->mods, 0, 0); + + led->ctrls = translate_controls_mask(wire->ctrls); + + xcb_xkb_indicator_map_next(&iter); + } + } + + return true; +} + +static bool +get_indicator_map(struct xkb_keymap *keymap, xcb_connection_t *conn, + uint16_t device_id) +{ + xcb_xkb_get_indicator_map_cookie_t cookie = + xcb_xkb_get_indicator_map(conn, device_id, ALL_INDICATORS_MASK); + xcb_xkb_get_indicator_map_reply_t *reply = + xcb_xkb_get_indicator_map_reply(conn, cookie, NULL); + + FAIL_IF_BAD_REPLY(reply, "XkbGetIndicatorMap"); + + if (!get_indicators(keymap, conn, reply)) + goto fail; + + free(reply); + return true; + +fail: + free(reply); + return false; +} + +static bool +get_sym_interprets(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_compat_map_reply_t *reply) +{ + int length = xcb_xkb_get_compat_map_si_rtrn_length(reply); + xcb_xkb_sym_interpret_iterator_t iter = + xcb_xkb_get_compat_map_si_rtrn_iterator(reply); + + FAIL_UNLESS(reply->firstSIRtrn == 0); + FAIL_UNLESS(reply->nSIRtrn == reply->nTotalSI); + + keymap->num_sym_interprets = reply->nSIRtrn; + ALLOC_OR_FAIL(keymap->sym_interprets, keymap->num_sym_interprets); + + for (int i = 0; i < length; i++) { + xcb_xkb_sym_interpret_t *wire = iter.data; + struct xkb_sym_interpret *sym_interpret = &keymap->sym_interprets[i]; + + sym_interpret->sym = wire->sym; + + switch (wire->match & XCB_XKB_SYM_INTERP_MATCH_OP_MASK) { + case XCB_XKB_SYM_INTERPRET_MATCH_NONE_OF: + sym_interpret->match = MATCH_NONE; + break; + case XCB_XKB_SYM_INTERPRET_MATCH_ANY_OF_OR_NONE: + sym_interpret->match = MATCH_ANY_OR_NONE; + break; + case XCB_XKB_SYM_INTERPRET_MATCH_ANY_OF: + sym_interpret->match = MATCH_ANY; + break; + case XCB_XKB_SYM_INTERPRET_MATCH_ALL_OF: + sym_interpret->match = MATCH_ALL; + break; + case XCB_XKB_SYM_INTERPRET_MATCH_EXACTLY: + sym_interpret->match = MATCH_EXACTLY; + break; + } + + sym_interpret->level_one_only = + (wire->match & XCB_XKB_SYM_INTERP_MATCH_LEVEL_ONE_ONLY); + sym_interpret->mods = wire->mods; + + if (wire->virtualMod == NO_MODIFIER) + sym_interpret->virtual_mod = XKB_MOD_INVALID; + else + sym_interpret->virtual_mod = NUM_REAL_MODS + wire->virtualMod; + + sym_interpret->repeat = (wire->flags & 0x01); + translate_action(&sym_interpret->action, + (xcb_xkb_action_t *) &wire->action); + + xcb_xkb_sym_interpret_next(&iter); + } + + return true; + +fail: + return false; +} + +static bool +get_compat_map(struct xkb_keymap *keymap, xcb_connection_t *conn, + uint16_t device_id) +{ + xcb_xkb_get_compat_map_cookie_t cookie = + xcb_xkb_get_compat_map(conn, device_id, 0, true, 0, 0); + xcb_xkb_get_compat_map_reply_t *reply = + xcb_xkb_get_compat_map_reply(conn, cookie, NULL); + + FAIL_IF_BAD_REPLY(reply, "XkbGetCompatMap"); + + if (!get_sym_interprets(keymap, conn, reply)) + goto fail; + + free(reply); + return true; + +fail: + free(reply); + return false; +} + +static bool +get_type_names(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_names_reply_t *reply, + xcb_xkb_get_names_value_list_t *list) +{ + int key_type_names_length = + xcb_xkb_get_names_value_list_type_names_length(reply, list); + xcb_atom_t *key_type_names_iter = + xcb_xkb_get_names_value_list_type_names(list); + int n_levels_per_type_length = + xcb_xkb_get_names_value_list_n_levels_per_type_length(reply, list); + uint8_t *n_levels_per_type_iter = + xcb_xkb_get_names_value_list_n_levels_per_type(list); + xcb_atom_t *kt_level_names_iter = + xcb_xkb_get_names_value_list_kt_level_names(list); + + FAIL_UNLESS(reply->nTypes == keymap->num_types); + FAIL_UNLESS(key_type_names_length == n_levels_per_type_length); + + for (int i = 0; i < key_type_names_length; i++) { + xcb_atom_t wire_type_name = *key_type_names_iter; + uint8_t wire_num_levels = *n_levels_per_type_iter; + struct xkb_key_type *type = &keymap->types[i]; + + /* Levels must have names. */ + FAIL_UNLESS(type->num_levels == wire_num_levels); + + ALLOC_OR_FAIL(type->level_names, type->num_levels); + + if (!adopt_atom(keymap->ctx, conn, wire_type_name, &type->name)) + goto fail; + + if (!adopt_atoms(keymap->ctx, conn, + kt_level_names_iter, type->level_names, + wire_num_levels)) + goto fail; + + kt_level_names_iter += wire_num_levels; + key_type_names_iter++; + n_levels_per_type_iter++; + } + + return true; + +fail: + return false; +} + +static bool +get_indicator_names(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_names_reply_t *reply, + xcb_xkb_get_names_value_list_t *list) +{ + xcb_atom_t *iter = xcb_xkb_get_names_value_list_indicator_names(list); + + FAIL_UNLESS(msb_pos(reply->indicators) <= keymap->num_leds); + + for (unsigned i = 0; i < NUM_INDICATORS; i++) { + if (reply->indicators & (1u << i)) { + xcb_atom_t wire = *iter; + struct xkb_led *led = &keymap->leds[i]; + + if (!adopt_atom(keymap->ctx, conn, wire, &led->name)) + return false; + + iter++; + } + } + + return true; + +fail: + return false; +} + +static bool +get_vmod_names(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_names_reply_t *reply, + xcb_xkb_get_names_value_list_t *list) +{ + xcb_atom_t *iter = xcb_xkb_get_names_value_list_virtual_mod_names(list); + + /* + * GetMap's reply->virtualMods is always 0xffff. This one really + * tells us which vmods exist (a vmod must have a name), so we fix + * up the size here. + */ + keymap->mods.num_mods = + NUM_REAL_MODS + MIN(msb_pos(reply->virtualMods), NUM_VMODS); + + for (unsigned i = 0; i < NUM_VMODS; i++) { + if (reply->virtualMods & (1u << i)) { + xcb_atom_t wire = *iter; + struct xkb_mod *mod = &keymap->mods.mods[NUM_REAL_MODS + i]; + + if (!adopt_atom(keymap->ctx, conn, wire, &mod->name)) + return false; + + iter++; + } + } + + return true; +} + +static bool +get_group_names(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_names_reply_t *reply, + xcb_xkb_get_names_value_list_t *list) +{ + int length = xcb_xkb_get_names_value_list_groups_length(reply, list); + xcb_atom_t *iter = xcb_xkb_get_names_value_list_groups(list); + + keymap->num_group_names = msb_pos(reply->groupNames); + ALLOC_OR_FAIL(keymap->group_names, keymap->num_group_names); + + if (!adopt_atoms(keymap->ctx, conn, + iter, keymap->group_names, length)) + goto fail; + + return true; + +fail: + return false; +} + +static bool +get_key_names(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_names_reply_t *reply, + xcb_xkb_get_names_value_list_t *list) +{ + int length = xcb_xkb_get_names_value_list_key_names_length(reply, list); + xcb_xkb_key_name_iterator_t iter = + xcb_xkb_get_names_value_list_key_names_iterator(reply, list); + + FAIL_UNLESS(reply->minKeyCode == keymap->min_key_code); + FAIL_UNLESS(reply->maxKeyCode == keymap->max_key_code); + FAIL_UNLESS(reply->firstKey == keymap->min_key_code); + FAIL_UNLESS(reply->firstKey + reply->nKeys - 1U == keymap->max_key_code); + + for (int i = 0; i < length; i++) { + xcb_xkb_key_name_t *wire = iter.data; + xkb_atom_t *key_name = &keymap->keys[reply->firstKey + i].name; + + if (wire->name[0] == '\0') { + *key_name = XKB_ATOM_NONE; + } + else { + *key_name = xkb_atom_intern(keymap->ctx, wire->name, + strnlen(wire->name, + XCB_XKB_CONST_KEY_NAME_LENGTH)); + if (!*key_name) + return false; + } + + xcb_xkb_key_name_next(&iter); + } + + return true; + +fail: + return false; +} + +static bool +get_aliases(struct xkb_keymap *keymap, xcb_connection_t *conn, + xcb_xkb_get_names_reply_t *reply, + xcb_xkb_get_names_value_list_t *list) +{ + int length = xcb_xkb_get_names_value_list_key_aliases_length(reply, list); + xcb_xkb_key_alias_iterator_t iter = + xcb_xkb_get_names_value_list_key_aliases_iterator(reply, list); + + keymap->num_key_aliases = reply->nKeyAliases; + ALLOC_OR_FAIL(keymap->key_aliases, keymap->num_key_aliases); + + for (int i = 0; i < length; i++) { + xcb_xkb_key_alias_t *wire = iter.data; + struct xkb_key_alias *alias = &keymap->key_aliases[i]; + + alias->real = + xkb_atom_intern(keymap->ctx, wire->real, + strnlen(wire->real, XCB_XKB_CONST_KEY_NAME_LENGTH)); + alias->alias = + xkb_atom_intern(keymap->ctx, wire->alias, + strnlen(wire->alias, XCB_XKB_CONST_KEY_NAME_LENGTH)); + if (!alias->real || !alias->alias) + goto fail; + + xcb_xkb_key_alias_next(&iter); + } + + return true; + +fail: + return false; +} + +static bool +get_names(struct xkb_keymap *keymap, xcb_connection_t *conn, + uint16_t device_id) +{ + static const xcb_xkb_name_detail_t wanted = + (XCB_XKB_NAME_DETAIL_KEYCODES | + XCB_XKB_NAME_DETAIL_SYMBOLS | + XCB_XKB_NAME_DETAIL_TYPES | + XCB_XKB_NAME_DETAIL_COMPAT | + XCB_XKB_NAME_DETAIL_KEY_TYPE_NAMES | + XCB_XKB_NAME_DETAIL_KT_LEVEL_NAMES | + XCB_XKB_NAME_DETAIL_INDICATOR_NAMES | + XCB_XKB_NAME_DETAIL_KEY_NAMES | + XCB_XKB_NAME_DETAIL_KEY_ALIASES | + XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES | + XCB_XKB_NAME_DETAIL_GROUP_NAMES); + static const xcb_xkb_name_detail_t required = + (XCB_XKB_NAME_DETAIL_KEY_TYPE_NAMES | + XCB_XKB_NAME_DETAIL_KT_LEVEL_NAMES | + XCB_XKB_NAME_DETAIL_KEY_NAMES | + XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES); + + xcb_xkb_get_names_cookie_t cookie = + xcb_xkb_get_names(conn, device_id, wanted); + xcb_xkb_get_names_reply_t *reply = + xcb_xkb_get_names_reply(conn, cookie, NULL); + xcb_xkb_get_names_value_list_t list; + + FAIL_IF_BAD_REPLY(reply, "XkbGetNames"); + + FAIL_UNLESS((reply->which & required) == required); + + xcb_xkb_get_names_value_list_unpack(xcb_xkb_get_names_value_list(reply), + reply->nTypes, + reply->indicators, + reply->virtualMods, + reply->groupNames, + reply->nKeys, + reply->nKeyAliases, + reply->nRadioGroups, + reply->which, + &list); + + if (!get_atom_name(conn, list.keycodesName, &keymap->keycodes_section_name) || + !get_atom_name(conn, list.symbolsName, &keymap->symbols_section_name) || + !get_atom_name(conn, list.typesName, &keymap->types_section_name) || + !get_atom_name(conn, list.compatName, &keymap->compat_section_name) || + !get_type_names(keymap, conn, reply, &list) || + !get_indicator_names(keymap, conn, reply, &list) || + !get_vmod_names(keymap, conn, reply, &list) || + !get_group_names(keymap, conn, reply, &list) || + !get_key_names(keymap, conn, reply, &list) || + !get_aliases(keymap, conn, reply, &list)) + goto fail; + + XkbEscapeMapName(keymap->keycodes_section_name); + XkbEscapeMapName(keymap->symbols_section_name); + XkbEscapeMapName(keymap->types_section_name); + XkbEscapeMapName(keymap->compat_section_name); + + free(reply); + return true; + +fail: + free(reply); + return false; +} + +static bool +get_controls(struct xkb_keymap *keymap, xcb_connection_t *conn, + uint16_t device_id) +{ + xcb_xkb_get_controls_cookie_t cookie = + xcb_xkb_get_controls(conn, device_id); + xcb_xkb_get_controls_reply_t *reply = + xcb_xkb_get_controls_reply(conn, cookie, NULL); + + FAIL_IF_BAD_REPLY(reply, "XkbGetControls"); + FAIL_UNLESS(reply->numGroups > 0 && reply->numGroups <= 4); + + keymap->enabled_ctrls = translate_controls_mask(reply->enabledControls); + keymap->num_groups = reply->numGroups; + + FAIL_UNLESS(keymap->max_key_code < XCB_XKB_CONST_PER_KEY_BIT_ARRAY_SIZE * 8); + + for (xkb_keycode_t i = keymap->min_key_code; i <= keymap->max_key_code; i++) + keymap->keys[i].repeats = (reply->perKeyRepeat[i / 8] & (1 << (i % 8))); + + free(reply); + return true; + +fail: + free(reply); + return false; +} + +XKB_EXPORT struct xkb_keymap * +xkb_x11_keymap_new_from_device(struct xkb_context *ctx, + xcb_connection_t *conn, + int32_t device_id, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap *keymap; + const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1; + + if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + if (device_id < 0 || device_id > 127) { + log_err_func(ctx, "illegal device ID: %d\n", device_id); + return NULL; + } + + keymap = xkb_keymap_new(ctx, format, flags); + if (!keymap) + return NULL; + + if (!get_map(keymap, conn, device_id) || + !get_indicator_map(keymap, conn, device_id) || + !get_compat_map(keymap, conn, device_id) || + !get_names(keymap, conn, device_id) || + !get_controls(keymap, conn, device_id)) { + xkb_keymap_unref(keymap); + return NULL; + } + + return keymap; +} diff --git a/src/x11/state.c b/src/x11/state.c new file mode 100644 index 0000000..d111880 --- /dev/null +++ b/src/x11/state.c @@ -0,0 +1,73 @@ +/* + * Copyright © 2013 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "x11-priv.h" + +static bool +update_initial_state(struct xkb_state *state, xcb_connection_t *conn, + uint16_t device_id) +{ + xcb_xkb_get_state_cookie_t cookie = + xcb_xkb_get_state(conn, device_id); + xcb_xkb_get_state_reply_t *reply = + xcb_xkb_get_state_reply(conn, cookie, NULL); + + if (!reply) + return false; + + xkb_state_update_mask(state, + reply->baseMods, + reply->latchedMods, + reply->lockedMods, + reply->baseGroup, + reply->latchedGroup, + reply->lockedGroup); + + free(reply); + return true; +} + +XKB_EXPORT struct xkb_state * +xkb_x11_state_new_from_device(struct xkb_keymap *keymap, + xcb_connection_t *conn, int32_t device_id) +{ + struct xkb_state *state; + + if (device_id < 0 || device_id > 255) { + log_err_func(keymap->ctx, "illegal device ID: %d", device_id); + return NULL; + } + + state = xkb_state_new(keymap); + if (!state) + return NULL; + + if (!update_initial_state(state, conn, device_id)) { + xkb_state_unref(state); + return NULL; + } + + return state; +} diff --git a/src/x11/util.c b/src/x11/util.c new file mode 100644 index 0000000..3959a5a --- /dev/null +++ b/src/x11/util.c @@ -0,0 +1,219 @@ +/* + * Copyright © 2013 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "x11-priv.h" + +XKB_EXPORT int +xkb_x11_setup_xkb_extension(xcb_connection_t *conn, + uint16_t major_xkb_version, + uint16_t minor_xkb_version, + enum xkb_x11_setup_xkb_extension_flags flags, + uint16_t *major_xkb_version_out, + uint16_t *minor_xkb_version_out, + uint8_t *base_event_out, + uint8_t *base_error_out) +{ + uint8_t base_event, base_error; + uint16_t server_major, server_minor; + + if (flags & ~(XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS)) { + /* log_err_func(ctx, "unrecognized flags: %#x\n", flags); */ + return 0; + } + + { + const xcb_query_extension_reply_t *reply = + xcb_get_extension_data(conn, &xcb_xkb_id); + if (!reply) { + /* log_err_func(ctx, "failed to query for XKB extension\n"); */ + return 0; + } + + if (!reply->present) { + /* log_err_func(ctx, "failed to start using XKB extension: not available in server\n"); */ + return 0; + } + + base_event = reply->first_event; + base_error = reply->first_error; + } + + { + xcb_generic_error_t *error = NULL; + xcb_xkb_use_extension_cookie_t cookie = + xcb_xkb_use_extension(conn, major_xkb_version, minor_xkb_version); + xcb_xkb_use_extension_reply_t *reply = + xcb_xkb_use_extension_reply(conn, cookie, &error); + + if (!reply) { + /* log_err_func(ctx, */ + /* "failed to start using XKB extension: error code %d\n", */ + /* error ? error->error_code : -1); */ + free(error); + return 0; + } + + if (!reply->supported) { + /* log_err_func(ctx, */ + /* "failed to start using XKB extension: server doesn't support version %d.%d\n", */ + /* major_xkb_version, minor_xkb_version); */ + free(reply); + return 0; + } + + server_major = reply->serverMajor; + server_minor = reply->serverMinor; + + free(reply); + } + + /* + * The XkbUseExtension() in libX11 has a *bunch* of legacy stuff, but + * it doesn't seem like any of it is useful to us. + */ + + if (major_xkb_version_out) + *major_xkb_version_out = server_major; + if (minor_xkb_version_out) + *minor_xkb_version_out = server_minor; + if (base_event_out) + *base_event_out = base_event; + if (base_error_out) + *base_error_out = base_error; + + return 1; +} + +XKB_EXPORT int32_t +xkb_x11_get_core_keyboard_device_id(xcb_connection_t *conn) +{ + int32_t device_id; + xcb_xkb_get_device_info_cookie_t cookie = + xcb_xkb_get_device_info(conn, XCB_XKB_ID_USE_CORE_KBD, + 0, 0, 0, 0, 0, 0); + xcb_xkb_get_device_info_reply_t *reply = + xcb_xkb_get_device_info_reply(conn, cookie, NULL); + + if (!reply) + return -1; + + device_id = reply->deviceID; + free(reply); + return device_id; +} + +bool +get_atom_name(xcb_connection_t *conn, xcb_atom_t atom, char **out) +{ + xcb_get_atom_name_cookie_t cookie; + xcb_get_atom_name_reply_t *reply; + int length; + char *name; + + if (atom == 0) { + *out = NULL; + return true; + } + + cookie = xcb_get_atom_name(conn, atom); + reply = xcb_get_atom_name_reply(conn, cookie, NULL); + if (!reply) + return false; + + length = xcb_get_atom_name_name_length(reply); + name = xcb_get_atom_name_name(reply); + + *out = strndup(name, length); + if (!*out) { + free(reply); + return false; + } + + free(reply); + return true; +} + +bool +adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn, + const xcb_atom_t *from, xkb_atom_t *to, const size_t count) +{ + enum { SIZE = 128 }; + xcb_get_atom_name_cookie_t cookies[SIZE]; + const size_t num_batches = ROUNDUP(count, SIZE) / SIZE; + + /* Send and collect the atoms in batches of reasonable SIZE. */ + for (size_t batch = 0; batch < num_batches; batch++) { + const size_t start = batch * SIZE; + const size_t stop = MIN((batch + 1) * SIZE, count); + + /* Send. */ + for (size_t i = start; i < stop; i++) + if (from[i] != XCB_ATOM_NONE) + cookies[i % SIZE] = xcb_get_atom_name(conn, from[i]); + + /* Collect. */ + for (size_t i = start; i < stop; i++) { + xcb_get_atom_name_reply_t *reply; + + if (from[i] == XCB_ATOM_NONE) { + to[i] = XKB_ATOM_NONE; + continue; + } + + reply = xcb_get_atom_name_reply(conn, cookies[i % SIZE], NULL); + if (!reply) + goto err_discard; + + to[i] = xkb_atom_intern(ctx, + xcb_get_atom_name_name(reply), + xcb_get_atom_name_name_length(reply)); + free(reply); + + if (to[i] == XKB_ATOM_NONE) + goto err_discard; + + continue; + + /* + * If we don't discard the uncollected replies, they just + * sit in the XCB queue waiting forever. Sad. + */ +err_discard: + for (size_t j = i + 1; j < stop; j++) + if (from[j] != XCB_ATOM_NONE) + xcb_discard_reply(conn, cookies[j % SIZE].sequence); + return false; + } + } + + return true; +} + +bool +adopt_atom(struct xkb_context *ctx, xcb_connection_t *conn, xcb_atom_t atom, + xkb_atom_t *out) +{ + return adopt_atoms(ctx, conn, &atom, out, 1); +} diff --git a/src/x11/x11-priv.h b/src/x11/x11-priv.h new file mode 100644 index 0000000..3a19e99 --- /dev/null +++ b/src/x11/x11-priv.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2013 Ran Benita + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef _XKBCOMMON_X11_PRIV_H +#define _XKBCOMMON_X11_PRIV_H + +#include <xcb/xkb.h> + +#include "keymap.h" +#include "xkbcommon/xkbcommon-x11.h" + +/* Get a strdup'd name of an X atom. */ +bool +get_atom_name(xcb_connection_t *conn, xcb_atom_t atom, char **out); + +/* + * Make a xkb_atom_t's from X atoms (prefer to send as many as possible + * at once, to avoid many roundtrips). + * + * TODO: We can make this more flexible, such that @to doesn't have to + * be sequential. Then we can convert most adopt_atom() calls to + * adopt_atoms(). + * Atom caching would also likely be useful for avoiding quite a + * few requests. + */ +bool +adopt_atoms(struct xkb_context *ctx, xcb_connection_t *conn, + const xcb_atom_t *from, xkb_atom_t *to, size_t count); + +bool +adopt_atom(struct xkb_context *ctx, xcb_connection_t *conn, xcb_atom_t atom, + xkb_atom_t *out); + +#endif diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c new file mode 100644 index 0000000..605f159 --- /dev/null +++ b/src/xkbcomp/action.c @@ -0,0 +1,873 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "action.h" + +static const ExprBoolean constTrue = { + .expr = { + .common = { .type = STMT_EXPR, .next = NULL }, + .op = EXPR_VALUE, + .value_type = EXPR_TYPE_BOOLEAN, + }, + .set = true, +}; + +static const ExprBoolean constFalse = { + .expr = { + .common = { .type = STMT_EXPR, .next = NULL }, + .op = EXPR_VALUE, + .value_type = EXPR_TYPE_BOOLEAN, + }, + .set = false, +}; + +enum action_field { + ACTION_FIELD_CLEAR_LOCKS, + ACTION_FIELD_LATCH_TO_LOCK, + ACTION_FIELD_GEN_KEY_EVENT, + ACTION_FIELD_REPORT, + ACTION_FIELD_DEFAULT, + ACTION_FIELD_AFFECT, + ACTION_FIELD_INCREMENT, + ACTION_FIELD_MODIFIERS, + ACTION_FIELD_GROUP, + ACTION_FIELD_X, + ACTION_FIELD_Y, + ACTION_FIELD_ACCEL, + ACTION_FIELD_BUTTON, + ACTION_FIELD_VALUE, + ACTION_FIELD_CONTROLS, + ACTION_FIELD_TYPE, + ACTION_FIELD_COUNT, + ACTION_FIELD_SCREEN, + ACTION_FIELD_SAME, + ACTION_FIELD_DATA, + ACTION_FIELD_DEVICE, + ACTION_FIELD_KEYCODE, + ACTION_FIELD_MODS_TO_CLEAR, +}; + +ActionsInfo * +NewActionsInfo(void) +{ + enum xkb_action_type type; + ActionsInfo *info; + + info = calloc(1, sizeof(*info)); + if (!info) + return NULL; + + for (type = 0; type < _ACTION_TYPE_NUM_ENTRIES; type++) + info->actions[type].type = type; + + /* Apply some "factory defaults". */ + + /* Increment default button. */ + info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.flags = 0; + info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.value = 1; + info->actions[ACTION_TYPE_PTR_MOVE].ptr.flags = ACTION_ACCEL; + info->actions[ACTION_TYPE_SWITCH_VT].screen.flags = ACTION_SAME_SCREEN; + + return info; +} + +void +FreeActionsInfo(ActionsInfo *info) +{ + free(info); +} + +static const LookupEntry fieldStrings[] = { + { "clearLocks", ACTION_FIELD_CLEAR_LOCKS }, + { "latchToLock", ACTION_FIELD_LATCH_TO_LOCK }, + { "genKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, + { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, + { "report", ACTION_FIELD_REPORT }, + { "default", ACTION_FIELD_DEFAULT }, + { "affect", ACTION_FIELD_AFFECT }, + { "increment", ACTION_FIELD_INCREMENT }, + { "modifiers", ACTION_FIELD_MODIFIERS }, + { "mods", ACTION_FIELD_MODIFIERS }, + { "group", ACTION_FIELD_GROUP }, + { "x", ACTION_FIELD_X }, + { "y", ACTION_FIELD_Y }, + { "accel", ACTION_FIELD_ACCEL }, + { "accelerate", ACTION_FIELD_ACCEL }, + { "repeat", ACTION_FIELD_ACCEL }, + { "button", ACTION_FIELD_BUTTON }, + { "value", ACTION_FIELD_VALUE }, + { "controls", ACTION_FIELD_CONTROLS }, + { "ctrls", ACTION_FIELD_CONTROLS }, + { "type", ACTION_FIELD_TYPE }, + { "count", ACTION_FIELD_COUNT }, + { "screen", ACTION_FIELD_SCREEN }, + { "same", ACTION_FIELD_SAME }, + { "sameServer", ACTION_FIELD_SAME }, + { "data", ACTION_FIELD_DATA }, + { "device", ACTION_FIELD_DEVICE }, + { "dev", ACTION_FIELD_DEVICE }, + { "key", ACTION_FIELD_KEYCODE }, + { "keycode", ACTION_FIELD_KEYCODE }, + { "kc", ACTION_FIELD_KEYCODE }, + { "clearmods", ACTION_FIELD_MODS_TO_CLEAR }, + { "clearmodifiers", ACTION_FIELD_MODS_TO_CLEAR }, + { NULL, 0 } +}; + +static bool +stringToAction(const char *str, enum xkb_action_type *type_rtrn) +{ + return LookupString(actionTypeNames, str, type_rtrn); +} + +static bool +stringToField(const char *str, enum action_field *field_rtrn) +{ + return LookupString(fieldStrings, str, field_rtrn); +} + +static const char * +fieldText(enum action_field field) +{ + return LookupValue(fieldStrings, field); +} + +/***====================================================================***/ + +static inline bool +ReportMismatch(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field, const char *type) +{ + log_err(ctx, + "Value of %s field must be of type %s; " + "Action %s definition ignored\n", + fieldText(field), type, ActionTypeText(action)); + return false; +} + +static inline bool +ReportIllegal(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field) +{ + log_err(ctx, + "Field %s is not defined for an action of type %s; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action)); + return false; +} + +static inline bool +ReportActionNotArray(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field) +{ + log_err(ctx, + "The %s field in the %s action is not an array; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action)); + return false; +} + +static bool +HandleNoAction(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) + +{ + return true; +} + +static bool +CheckBooleanFlag(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field, enum xkb_action_flags flag, + const ExprDef *array_ndx, const ExprDef *value, + enum xkb_action_flags *flags_inout) +{ + bool set; + + if (array_ndx) + return ReportActionNotArray(ctx, action, field); + + if (!ExprResolveBoolean(ctx, value, &set)) + return ReportMismatch(ctx, action, field, "boolean"); + + if (set) + *flags_inout |= flag; + else + *flags_inout &= ~flag; + + return true; +} + +static bool +CheckModifierField(struct xkb_context *ctx, const struct xkb_mod_set *mods, + enum xkb_action_type action, const ExprDef *array_ndx, + const ExprDef *value, enum xkb_action_flags *flags_inout, + xkb_mod_mask_t *mods_rtrn) +{ + if (array_ndx) + return ReportActionNotArray(ctx, action, ACTION_FIELD_MODIFIERS); + + if (value->expr.op == EXPR_IDENT) { + const char *valStr; + valStr = xkb_atom_text(ctx, value->ident.ident); + if (valStr && (istreq(valStr, "usemodmapmods") || + istreq(valStr, "modmapmods"))) { + *mods_rtrn = 0; + *flags_inout |= ACTION_MODS_LOOKUP_MODMAP; + return true; + } + } + + if (!ExprResolveModMask(ctx, value, MOD_BOTH, mods, mods_rtrn)) + return ReportMismatch(ctx, action, + ACTION_FIELD_MODIFIERS, "modifier mask"); + + *flags_inout &= ~ACTION_MODS_LOOKUP_MODMAP; + return true; +} + +static const LookupEntry lockWhich[] = { + { "both", 0 }, + { "lock", ACTION_LOCK_NO_UNLOCK }, + { "neither", (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK) }, + { "unlock", ACTION_LOCK_NO_LOCK }, + { NULL, 0 } +}; + +static bool +CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action, + const ExprDef *array_ndx, const ExprDef *value, + enum xkb_action_flags *flags_inout) +{ + enum xkb_action_flags flags; + + if (array_ndx) + return ReportActionNotArray(ctx, action, ACTION_FIELD_AFFECT); + + if (!ExprResolveEnum(ctx, value, &flags, lockWhich)) + return ReportMismatch(ctx, action, ACTION_FIELD_AFFECT, + "lock, unlock, both, neither"); + + *flags_inout &= ~(ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK); + *flags_inout |= flags; + return true; +} + +static bool +HandleSetLatchLockMods(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_mod_action *act = &action->mods; + const enum xkb_action_type type = action->type; + + if (field == ACTION_FIELD_MODIFIERS) + return CheckModifierField(ctx, mods, action->type, array_ndx, value, + &act->flags, &act->mods.mods); + if ((type == ACTION_TYPE_MOD_SET || type == ACTION_TYPE_MOD_LATCH) && + field == ACTION_FIELD_CLEAR_LOCKS) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LOCK_CLEAR, array_ndx, value, + &act->flags); + if (type == ACTION_TYPE_MOD_LATCH && + field == ACTION_FIELD_LATCH_TO_LOCK) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LATCH_TO_LOCK, array_ndx, value, + &act->flags); + if (type == ACTION_TYPE_MOD_LOCK && + field == ACTION_FIELD_AFFECT) + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + + return ReportIllegal(ctx, action->type, field); +} + +static bool +CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action, + const ExprDef *array_ndx, const ExprDef *value, + enum xkb_action_flags *flags_inout, int32_t *group_rtrn) +{ + const ExprDef *spec; + xkb_layout_index_t idx; + enum xkb_action_flags flags = *flags_inout; + + if (array_ndx) + return ReportActionNotArray(ctx, action, ACTION_FIELD_GROUP); + + if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) { + flags &= ~ACTION_ABSOLUTE_SWITCH; + spec = value->unary.child; + } + else { + flags |= ACTION_ABSOLUTE_SWITCH; + spec = value; + } + + if (!ExprResolveGroup(ctx, spec, &idx)) + return ReportMismatch(ctx, action, ACTION_FIELD_GROUP, + "integer (range 1..8)"); + + /* +n, -n are relative, n is absolute. */ + if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) { + *group_rtrn = (int32_t) idx; + if (value->expr.op == EXPR_NEGATE) + *group_rtrn = -*group_rtrn; + } + else { + *group_rtrn = (int32_t) (idx - 1); + } + *flags_inout = flags; + return true; +} + +static bool +HandleSetLatchLockGroup(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_group_action *act = &action->group; + const enum xkb_action_type type = action->type; + + if (field == ACTION_FIELD_GROUP) + return CheckGroupField(ctx, action->type, array_ndx, value, + &act->flags, &act->group); + if ((type == ACTION_TYPE_GROUP_SET || type == ACTION_TYPE_GROUP_LATCH) && + field == ACTION_FIELD_CLEAR_LOCKS) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LOCK_CLEAR, array_ndx, value, + &act->flags); + if (type == ACTION_TYPE_GROUP_LATCH && + field == ACTION_FIELD_LATCH_TO_LOCK) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LATCH_TO_LOCK, array_ndx, value, + &act->flags); + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_pointer_action *act = &action->ptr; + + if (field == ACTION_FIELD_X || field == ACTION_FIELD_Y) { + int val; + const bool absolute = (value->expr.op != EXPR_NEGATE && + value->expr.op != EXPR_UNARY_PLUS); + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveInteger(ctx, value, &val)) + return ReportMismatch(ctx, action->type, field, "integer"); + + if (val < INT16_MIN || val > INT16_MAX) { + log_err(ctx, + "The %s field in the %s action must be in range %d..%d; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action->type), + INT16_MIN, INT16_MAX); + return false; + } + + if (field == ACTION_FIELD_X) { + if (absolute) + act->flags |= ACTION_ABSOLUTE_X; + act->x = (int16_t) val; + } + else { + if (absolute) + act->flags |= ACTION_ABSOLUTE_Y; + act->y = (int16_t) val; + } + + return true; + } + else if (field == ACTION_FIELD_ACCEL) { + return CheckBooleanFlag(ctx, action->type, field, + ACTION_ACCEL, array_ndx, value, &act->flags); + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_pointer_button_action *act = &action->btn; + + if (field == ACTION_FIELD_BUTTON) { + int btn; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveButton(ctx, value, &btn)) + return ReportMismatch(ctx, action->type, field, + "integer (range 1..5)"); + + if (btn < 0 || btn > 5) { + log_err(ctx, + "Button must specify default or be in the range 1..5; " + "Illegal button value %d ignored\n", btn); + return false; + } + + act->button = btn; + return true; + } + else if (action->type == ACTION_TYPE_PTR_LOCK && + field == ACTION_FIELD_AFFECT) { + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + } + else if (field == ACTION_FIELD_COUNT) { + int val; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveInteger(ctx, value, &val)) + return ReportMismatch(ctx, action->type, field, "integer"); + + if (val < 0 || val > 255) { + log_err(ctx, + "The count field must have a value in the range 0..255; " + "Illegal count %d ignored\n", val); + return false; + } + + act->count = (uint8_t) val; + return true; + } + + return ReportIllegal(ctx, action->type, field); +} + +static const LookupEntry ptrDflts[] = { + { "dfltbtn", 1 }, + { "defaultbutton", 1 }, + { "button", 1 }, + { NULL, 0 } +}; + +static bool +HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_pointer_default_action *act = &action->dflt; + + if (field == ACTION_FIELD_AFFECT) { + unsigned int val; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveEnum(ctx, value, &val, ptrDflts)) + return ReportMismatch(ctx, action->type, field, + "pointer component"); + return true; + } + else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) { + const ExprDef *button; + int btn; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (value->expr.op == EXPR_NEGATE || + value->expr.op == EXPR_UNARY_PLUS) { + act->flags &= ~ACTION_ABSOLUTE_SWITCH; + button = value->unary.child; + } + else { + act->flags |= ACTION_ABSOLUTE_SWITCH; + button = value; + } + + if (!ExprResolveButton(ctx, button, &btn)) + return ReportMismatch(ctx, action->type, field, + "integer (range 1..5)"); + + if (btn < 0 || btn > 5) { + log_err(ctx, + "New default button value must be in the range 1..5; " + "Illegal default button value %d ignored\n", btn); + return false; + } + if (btn == 0) { + log_err(ctx, + "Cannot set default pointer button to \"default\"; " + "Illegal default button setting ignored\n"); + return false; + } + + act->value = (value->expr.op == EXPR_NEGATE ? -btn: btn); + return true; + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_switch_screen_action *act = &action->screen; + + if (field == ACTION_FIELD_SCREEN) { + const ExprDef *scrn; + int val; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (value->expr.op == EXPR_NEGATE || + value->expr.op == EXPR_UNARY_PLUS) { + act->flags &= ~ACTION_ABSOLUTE_SWITCH; + scrn = value->unary.child; + } + else { + act->flags |= ACTION_ABSOLUTE_SWITCH; + scrn = value; + } + + if (!ExprResolveInteger(ctx, scrn, &val)) + return ReportMismatch(ctx, action->type, field, + "integer (0..255)"); + + if (val < 0 || val > 255) { + log_err(ctx, + "Screen index must be in the range 1..255; " + "Illegal screen value %d ignored\n", val); + return false; + } + + act->screen = (value->expr.op == EXPR_NEGATE ? -val : val); + return true; + } + else if (field == ACTION_FIELD_SAME) { + return CheckBooleanFlag(ctx, action->type, field, + ACTION_SAME_SCREEN, array_ndx, value, + &act->flags); + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_controls_action *act = &action->ctrls; + + if (field == ACTION_FIELD_CONTROLS) { + enum xkb_action_controls mask; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveMask(ctx, value, &mask, ctrlMaskNames)) + return ReportMismatch(ctx, action->type, field, + "controls mask"); + + act->ctrls = mask; + return true; + } + else if (field == ACTION_FIELD_AFFECT) { + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_private_action *act = &action->priv; + + if (field == ACTION_FIELD_TYPE) { + int type; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveInteger(ctx, value, &type)) + return ReportMismatch(ctx, ACTION_TYPE_PRIVATE, field, "integer"); + + if (type < 0 || type > 255) { + log_err(ctx, + "Private action type must be in the range 0..255; " + "Illegal type %d ignored\n", type); + return false; + } + + /* + * It's possible for someone to write something like this: + * actions = [ Private(type=3,data[0]=1,data[1]=3,data[2]=3) ] + * where the type refers to some existing action type, e.g. LockMods. + * This assumes that this action's struct is laid out in memory + * exactly as described in the XKB specification and libraries. + * We, however, have changed these structs in various ways, so this + * assumption is no longer true. Since this is a lousy "feature", we + * make actions like these no-ops for now. + */ + if (type < ACTION_TYPE_PRIVATE) { + log_info(ctx, + "Private actions of type %s are not supported; Ignored\n", + ActionTypeText(type)); + act->type = ACTION_TYPE_NONE; + } + else { + act->type = (enum xkb_action_type) type; + } + + return true; + } + else if (field == ACTION_FIELD_DATA) { + if (array_ndx == NULL) { + xkb_atom_t val; + const char *str; + size_t len; + + if (!ExprResolveString(ctx, value, &val)) + return ReportMismatch(ctx, action->type, field, "string"); + + str = xkb_atom_text(ctx, val); + len = strlen(str); + if (len < 1 || len > 7) { + log_warn(ctx, + "A private action has 7 data bytes; " + "Illegal data ignored\n"); + return false; + } + + /* act->data may not be null-terminated, this is intentional */ + strncpy((char *) act->data, str, sizeof(act->data)); + return true; + } + else { + int ndx, datum; + + if (!ExprResolveInteger(ctx, array_ndx, &ndx)) { + log_err(ctx, + "Array subscript must be integer; " + "Illegal subscript ignored\n"); + return false; + } + + if (ndx < 0 || (size_t) ndx >= sizeof(act->data)) { + log_err(ctx, + "The data for a private action is %lu bytes long; " + "Attempt to use data[%d] ignored\n", + (unsigned long) sizeof(act->data), ndx); + return false; + } + + if (!ExprResolveInteger(ctx, value, &datum)) + return ReportMismatch(ctx, act->type, field, "integer"); + + if (datum < 0 || datum > 255) { + log_err(ctx, + "All data for a private action must be 0..255; " + "Illegal datum %d ignored\n", datum); + return false; + } + + act->data[ndx] = (uint8_t) datum; + return true; + } + } + + return ReportIllegal(ctx, ACTION_TYPE_NONE, field); +} + +typedef bool (*actionHandler)(struct xkb_context *ctx, + const struct xkb_mod_set *mods, + union xkb_action *action, + enum action_field field, + const ExprDef *array_ndx, + const ExprDef *value); + +static const actionHandler handleAction[_ACTION_TYPE_NUM_ENTRIES] = { + [ACTION_TYPE_NONE] = HandleNoAction, + [ACTION_TYPE_MOD_SET] = HandleSetLatchLockMods, + [ACTION_TYPE_MOD_LATCH] = HandleSetLatchLockMods, + [ACTION_TYPE_MOD_LOCK] = HandleSetLatchLockMods, + [ACTION_TYPE_GROUP_SET] = HandleSetLatchLockGroup, + [ACTION_TYPE_GROUP_LATCH] = HandleSetLatchLockGroup, + [ACTION_TYPE_GROUP_LOCK] = HandleSetLatchLockGroup, + [ACTION_TYPE_PTR_MOVE] = HandleMovePtr, + [ACTION_TYPE_PTR_BUTTON] = HandlePtrBtn, + [ACTION_TYPE_PTR_LOCK] = HandlePtrBtn, + [ACTION_TYPE_PTR_DEFAULT] = HandleSetPtrDflt, + [ACTION_TYPE_TERMINATE] = HandleNoAction, + [ACTION_TYPE_SWITCH_VT] = HandleSwitchScreen, + [ACTION_TYPE_CTRL_SET] = HandleSetLockControls, + [ACTION_TYPE_CTRL_LOCK] = HandleSetLockControls, + [ACTION_TYPE_PRIVATE] = HandlePrivate, +}; + +/***====================================================================***/ + +bool +HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, + const struct xkb_mod_set *mods, ExprDef *def, + union xkb_action *action) +{ + ExprDef *arg; + const char *str; + enum xkb_action_type handler_type; + + if (def->expr.op != EXPR_ACTION_DECL) { + log_err(ctx, "Expected an action definition, found %s\n", + expr_op_type_to_string(def->expr.op)); + return false; + } + + str = xkb_atom_text(ctx, def->action.name); + if (!stringToAction(str, &handler_type)) { + log_err(ctx, "Unknown action %s\n", str); + return false; + } + + /* + * Get the default values for this action type, as modified by + * statements such as: + * latchMods.clearLocks = True; + */ + *action = info->actions[handler_type]; + + /* + * Now change the action properties as specified for this + * particular instance, e.g. "modifiers" and "clearLocks" in: + * SetMods(modifiers=Alt,clearLocks); + */ + for (arg = def->action.args; arg != NULL; + arg = (ExprDef *) arg->common.next) { + const ExprDef *value; + ExprDef *field, *arrayRtrn; + const char *elemRtrn, *fieldRtrn; + enum action_field fieldNdx; + + if (arg->expr.op == EXPR_ASSIGN) { + field = arg->binary.left; + value = arg->binary.right; + } + else if (arg->expr.op == EXPR_NOT || arg->expr.op == EXPR_INVERT) { + field = arg->unary.child; + value = (const ExprDef *) &constFalse; + } + else { + field = arg; + value = (const ExprDef *) &constTrue; + } + + if (!ExprResolveLhs(ctx, field, &elemRtrn, &fieldRtrn, &arrayRtrn)) + return false; + + if (elemRtrn) { + log_err(ctx, + "Cannot change defaults in an action definition; " + "Ignoring attempt to change %s.%s\n", + elemRtrn, fieldRtrn); + return false; + } + + if (!stringToField(fieldRtrn, &fieldNdx)) { + log_err(ctx, "Unknown field name %s\n", fieldRtrn); + return false; + } + + if (!handleAction[handler_type](ctx, mods, action, fieldNdx, + arrayRtrn, value)) + return false; + } + + return true; +} + +bool +SetActionField(struct xkb_context *ctx, ActionsInfo *info, + struct xkb_mod_set *mods, const char *elem, + const char *field, ExprDef *array_ndx, ExprDef *value) +{ + enum xkb_action_type action; + enum action_field action_field; + + if (!stringToAction(elem, &action)) + return false; + + if (!stringToField(field, &action_field)) { + log_err(ctx, "\"%s\" is not a legal field name\n", field); + return false; + } + + return handleAction[action](ctx, mods, &info->actions[action], + action_field, array_ndx, value); +} diff --git a/src/xkbcomp/action.h b/src/xkbcomp/action.h new file mode 100644 index 0000000..1f92e7b --- /dev/null +++ b/src/xkbcomp/action.h @@ -0,0 +1,56 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_ACTION_H +#define XKBCOMP_ACTION_H + +/* + * This struct contains the default values which every new action + * (e.g. in an interpret statement) starts off with. It can be + * modified within the files (see calls to SetActionField). + */ +typedef struct { + union xkb_action actions[_ACTION_TYPE_NUM_ENTRIES]; +} ActionsInfo; + +ActionsInfo * +NewActionsInfo(void); + +void +FreeActionsInfo(ActionsInfo *info); + +bool +HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, + const struct xkb_mod_set *mods, ExprDef *def, + union xkb_action *action); + +bool +SetActionField(struct xkb_context *ctx, ActionsInfo *info, + struct xkb_mod_set *mods, const char *elem, + const char *field, ExprDef *array_ndx, ExprDef *value); + + +#endif diff --git a/src/xkbcomp/ast-build.c b/src/xkbcomp/ast-build.c new file mode 100644 index 0000000..7ee13d0 --- /dev/null +++ b/src/xkbcomp/ast-build.c @@ -0,0 +1,830 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "ast-build.h" +#include "include.h" + +static ExprDef * +ExprCreate(enum expr_op_type op, enum expr_value_type type, size_t size) +{ + ExprDef *expr = malloc(size); + if (!expr) + return NULL; + + expr->common.type = STMT_EXPR; + expr->common.next = NULL; + expr->expr.op = op; + expr->expr.value_type = type; + + return expr; +} + +ExprDef * +ExprCreateString(xkb_atom_t str) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_STRING, sizeof(ExprString)); + if (!expr) + return NULL; + expr->string.str = str; + return expr; +} + +ExprDef * +ExprCreateInteger(int ival) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_INT, sizeof(ExprInteger)); + if (!expr) + return NULL; + expr->integer.ival = ival; + return expr; +} + +ExprDef * +ExprCreateFloat(void) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_FLOAT, sizeof(ExprFloat)); + if (!expr) + return NULL; + return expr; +} + +ExprDef * +ExprCreateBoolean(bool set) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_BOOLEAN, sizeof(ExprBoolean)); + if (!expr) + return NULL; + expr->boolean.set = set; + return expr; +} + +ExprDef * +ExprCreateKeyName(xkb_atom_t key_name) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_KEYNAME, sizeof(ExprKeyName)); + if (!expr) + return NULL; + expr->key_name.key_name = key_name; + return expr; +} + +ExprDef * +ExprCreateIdent(xkb_atom_t ident) +{ + ExprDef *expr = ExprCreate(EXPR_IDENT, EXPR_TYPE_UNKNOWN, sizeof(ExprIdent)); + if (!expr) + return NULL; + expr->ident.ident = ident; + return expr; +} + +ExprDef * +ExprCreateUnary(enum expr_op_type op, enum expr_value_type type, + ExprDef *child) +{ + ExprDef *expr = ExprCreate(op, type, sizeof(ExprUnary)); + if (!expr) + return NULL; + expr->unary.child = child; + return expr; +} + +ExprDef * +ExprCreateBinary(enum expr_op_type op, ExprDef *left, ExprDef *right) +{ + ExprDef *expr = ExprCreate(op, EXPR_TYPE_UNKNOWN, sizeof(ExprBinary)); + if (!expr) + return NULL; + + if (op == EXPR_ASSIGN || left->expr.value_type == EXPR_TYPE_UNKNOWN) + expr->expr.value_type = right->expr.value_type; + else if (left->expr.value_type == right->expr.value_type || + right->expr.value_type == EXPR_TYPE_UNKNOWN) + expr->expr.value_type = left->expr.value_type; + expr->binary.left = left; + expr->binary.right = right; + + return expr; +} + +ExprDef * +ExprCreateFieldRef(xkb_atom_t element, xkb_atom_t field) +{ + ExprDef *expr = ExprCreate(EXPR_FIELD_REF, EXPR_TYPE_UNKNOWN, sizeof(ExprFieldRef)); + if (!expr) + return NULL; + expr->field_ref.element = element; + expr->field_ref.field = field; + return expr; +} + +ExprDef * +ExprCreateArrayRef(xkb_atom_t element, xkb_atom_t field, ExprDef *entry) +{ + ExprDef *expr = ExprCreate(EXPR_ARRAY_REF, EXPR_TYPE_UNKNOWN, sizeof(ExprArrayRef)); + if (!expr) + return NULL; + expr->array_ref.element = element; + expr->array_ref.field = field; + expr->array_ref.entry = entry; + return expr; +} + +ExprDef * +ExprCreateAction(xkb_atom_t name, ExprDef *args) +{ + ExprDef *expr = ExprCreate(EXPR_ACTION_DECL, EXPR_TYPE_UNKNOWN, sizeof(ExprAction)); + if (!expr) + return NULL; + expr->action.name = name; + expr->action.args = args; + return expr; +} + +ExprDef * +ExprCreateActionList(ExprDef *actions) +{ + ExprDef *expr = ExprCreate(EXPR_ACTION_LIST, EXPR_TYPE_ACTIONS, sizeof(ExprActionList)); + if (!expr) + return NULL; + expr->actions.actions = actions; + return expr; +} + +ExprDef * +ExprCreateKeysymList(xkb_keysym_t sym) +{ + ExprDef *expr = ExprCreate(EXPR_KEYSYM_LIST, EXPR_TYPE_SYMBOLS, sizeof(ExprKeysymList)); + if (!expr) + return NULL; + + darray_init(expr->keysym_list.syms); + darray_init(expr->keysym_list.symsMapIndex); + darray_init(expr->keysym_list.symsNumEntries); + + darray_append(expr->keysym_list.syms, sym); + darray_append(expr->keysym_list.symsMapIndex, 0); + darray_append(expr->keysym_list.symsNumEntries, 1); + + return expr; +} + +ExprDef * +ExprCreateMultiKeysymList(ExprDef *expr) +{ + unsigned nLevels = darray_size(expr->keysym_list.symsMapIndex); + + darray_resize(expr->keysym_list.symsMapIndex, 1); + darray_resize(expr->keysym_list.symsNumEntries, 1); + darray_item(expr->keysym_list.symsMapIndex, 0) = 0; + darray_item(expr->keysym_list.symsNumEntries, 0) = nLevels; + + return expr; +} + +ExprDef * +ExprAppendKeysymList(ExprDef *expr, xkb_keysym_t sym) +{ + unsigned nSyms = darray_size(expr->keysym_list.syms); + + darray_append(expr->keysym_list.symsMapIndex, nSyms); + darray_append(expr->keysym_list.symsNumEntries, 1); + darray_append(expr->keysym_list.syms, sym); + + return expr; +} + +ExprDef * +ExprAppendMultiKeysymList(ExprDef *expr, ExprDef *append) +{ + unsigned nSyms = darray_size(expr->keysym_list.syms); + unsigned numEntries = darray_size(append->keysym_list.syms); + + darray_append(expr->keysym_list.symsMapIndex, nSyms); + darray_append(expr->keysym_list.symsNumEntries, numEntries); + darray_concat(expr->keysym_list.syms, append->keysym_list.syms); + + FreeStmt((ParseCommon *) append); + + return expr; +} + +KeycodeDef * +KeycodeCreate(xkb_atom_t name, int64_t value) +{ + KeycodeDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_KEYCODE; + def->common.next = NULL; + def->name = name; + def->value = value; + + return def; +} + +KeyAliasDef * +KeyAliasCreate(xkb_atom_t alias, xkb_atom_t real) +{ + KeyAliasDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_ALIAS; + def->common.next = NULL; + def->alias = alias; + def->real = real; + + return def; +} + +VModDef * +VModCreate(xkb_atom_t name, ExprDef *value) +{ + VModDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_VMOD; + def->common.next = NULL; + def->name = name; + def->value = value; + + return def; +} + +VarDef * +VarCreate(ExprDef *name, ExprDef *value) +{ + VarDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_VAR; + def->common.next = NULL; + def->name = name; + def->value = value; + + return def; +} + +VarDef * +BoolVarCreate(xkb_atom_t ident, bool set) +{ + ExprDef *name, *value; + VarDef *def; + if (!(name = ExprCreateIdent(ident))) { + return NULL; + } + if (!(value = ExprCreateBoolean(set))) { + FreeStmt((ParseCommon *) name); + return NULL; + } + if (!(def = VarCreate(name, value))) { + FreeStmt((ParseCommon *) name); + FreeStmt((ParseCommon *) value); + return NULL; + } + return def; +} + +InterpDef * +InterpCreate(xkb_keysym_t sym, ExprDef *match) +{ + InterpDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_INTERP; + def->common.next = NULL; + def->sym = sym; + def->match = match; + def->def = NULL; + + return def; +} + +KeyTypeDef * +KeyTypeCreate(xkb_atom_t name, VarDef *body) +{ + KeyTypeDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_TYPE; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->name = name; + def->body = body; + + return def; +} + +SymbolsDef * +SymbolsCreate(xkb_atom_t keyName, VarDef *symbols) +{ + SymbolsDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_SYMBOLS; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->keyName = keyName; + def->symbols = symbols; + + return def; +} + +GroupCompatDef * +GroupCompatCreate(unsigned group, ExprDef *val) +{ + GroupCompatDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_GROUP_COMPAT; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->group = group; + def->def = val; + + return def; +} + +ModMapDef * +ModMapCreate(xkb_atom_t modifier, ExprDef *keys) +{ + ModMapDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_MODMAP; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->modifier = modifier; + def->keys = keys; + + return def; +} + +LedMapDef * +LedMapCreate(xkb_atom_t name, VarDef *body) +{ + LedMapDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_LED_MAP; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->name = name; + def->body = body; + + return def; +} + +LedNameDef * +LedNameCreate(unsigned ndx, ExprDef *name, bool virtual) +{ + LedNameDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_LED_NAME; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->ndx = ndx; + def->name = name; + def->virtual = virtual; + + return def; +} + +static void +FreeInclude(IncludeStmt *incl); + +IncludeStmt * +IncludeCreate(struct xkb_context *ctx, char *str, enum merge_mode merge) +{ + IncludeStmt *incl, *first; + char *file, *map, *stmt, *tmp, *extra_data; + char nextop; + + incl = first = NULL; + file = map = NULL; + tmp = str; + stmt = strdup_safe(str); + while (tmp && *tmp) + { + if (!ParseIncludeMap(&tmp, &file, &map, &nextop, &extra_data)) + goto err; + + /* + * Given an RMLVO (here layout) like 'us,,fr', the rules parser + * will give out something like 'pc+us+:2+fr:3+inet(evdev)'. + * We should just skip the ':2' in this case and leave it to the + * appropriate section to deal with the empty group. + */ + if (isempty(file)) { + free(file); + free(map); + free(extra_data); + continue; + } + + if (first == NULL) { + first = incl = malloc(sizeof(*first)); + } else { + incl->next_incl = malloc(sizeof(*first)); + incl = incl->next_incl; + } + + if (!incl) + break; + + incl->common.type = STMT_INCLUDE; + incl->common.next = NULL; + incl->merge = merge; + incl->stmt = NULL; + incl->file = file; + incl->map = map; + incl->modifier = extra_data; + incl->next_incl = NULL; + + if (nextop == '|') + merge = MERGE_AUGMENT; + else + merge = MERGE_OVERRIDE; + } + + if (first) + first->stmt = stmt; + else + free(stmt); + + return first; + +err: + log_err(ctx, "Illegal include statement \"%s\"; Ignored\n", stmt); + FreeInclude(first); + free(stmt); + return NULL; +} + +XkbFile * +XkbFileCreate(enum xkb_file_type type, char *name, ParseCommon *defs, + enum xkb_map_flags flags) +{ + XkbFile *file; + + file = calloc(1, sizeof(*file)); + if (!file) + return NULL; + + XkbEscapeMapName(name); + file->file_type = type; + file->name = name ? name : strdup("(unnamed)"); + file->defs = defs; + file->flags = flags; + + return file; +} + +XkbFile * +XkbFileFromComponents(struct xkb_context *ctx, + const struct xkb_component_names *kkctgs) +{ + char *const components[] = { + kkctgs->keycodes, kkctgs->types, + kkctgs->compat, kkctgs->symbols, + }; + enum xkb_file_type type; + IncludeStmt *include = NULL; + XkbFile *file = NULL; + ParseCommon *defs = NULL, *defsLast = NULL; + + for (type = FIRST_KEYMAP_FILE_TYPE; type <= LAST_KEYMAP_FILE_TYPE; type++) { + include = IncludeCreate(ctx, components[type], MERGE_DEFAULT); + if (!include) + goto err; + + file = XkbFileCreate(type, NULL, (ParseCommon *) include, 0); + if (!file) { + FreeInclude(include); + goto err; + } + + if (!defs) + defsLast = defs = &file->common; + else + defsLast = defsLast->next = &file->common; + } + + file = XkbFileCreate(FILE_TYPE_KEYMAP, NULL, defs, 0); + if (!file) + goto err; + + return file; + +err: + FreeXkbFile((XkbFile *) defs); + return NULL; +} + +static void +FreeExpr(ExprDef *expr) +{ + if (!expr) + return; + + switch (expr->expr.op) { + case EXPR_NEGATE: + case EXPR_UNARY_PLUS: + case EXPR_NOT: + case EXPR_INVERT: + FreeStmt((ParseCommon *) expr->unary.child); + break; + + case EXPR_DIVIDE: + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_ASSIGN: + FreeStmt((ParseCommon *) expr->binary.left); + FreeStmt((ParseCommon *) expr->binary.right); + break; + + case EXPR_ACTION_DECL: + FreeStmt((ParseCommon *) expr->action.args); + break; + + case EXPR_ACTION_LIST: + FreeStmt((ParseCommon *) expr->actions.actions); + break; + + case EXPR_ARRAY_REF: + FreeStmt((ParseCommon *) expr->array_ref.entry); + break; + + case EXPR_KEYSYM_LIST: + darray_free(expr->keysym_list.syms); + darray_free(expr->keysym_list.symsMapIndex); + darray_free(expr->keysym_list.symsNumEntries); + break; + + default: + break; + } +} + +static void +FreeInclude(IncludeStmt *incl) +{ + IncludeStmt *next; + + while (incl) + { + next = incl->next_incl; + + free(incl->file); + free(incl->map); + free(incl->modifier); + free(incl->stmt); + + free(incl); + incl = next; + } +} + +void +FreeStmt(ParseCommon *stmt) +{ + ParseCommon *next; + + while (stmt) + { + next = stmt->next; + + switch (stmt->type) { + case STMT_INCLUDE: + FreeInclude((IncludeStmt *) stmt); + /* stmt is already free'd here. */ + stmt = NULL; + break; + case STMT_EXPR: + FreeExpr((ExprDef *) stmt); + break; + case STMT_VAR: + FreeStmt((ParseCommon *) ((VarDef *) stmt)->name); + FreeStmt((ParseCommon *) ((VarDef *) stmt)->value); + break; + case STMT_TYPE: + FreeStmt((ParseCommon *) ((KeyTypeDef *) stmt)->body); + break; + case STMT_INTERP: + FreeStmt((ParseCommon *) ((InterpDef *) stmt)->match); + FreeStmt((ParseCommon *) ((InterpDef *) stmt)->def); + break; + case STMT_VMOD: + FreeStmt((ParseCommon *) ((VModDef *) stmt)->value); + break; + case STMT_SYMBOLS: + FreeStmt((ParseCommon *) ((SymbolsDef *) stmt)->symbols); + break; + case STMT_MODMAP: + FreeStmt((ParseCommon *) ((ModMapDef *) stmt)->keys); + break; + case STMT_GROUP_COMPAT: + FreeStmt((ParseCommon *) ((GroupCompatDef *) stmt)->def); + break; + case STMT_LED_MAP: + FreeStmt((ParseCommon *) ((LedMapDef *) stmt)->body); + break; + case STMT_LED_NAME: + FreeStmt((ParseCommon *) ((LedNameDef *) stmt)->name); + break; + default: + break; + } + + free(stmt); + stmt = next; + } +} + +void +FreeXkbFile(XkbFile *file) +{ + XkbFile *next; + + while (file) + { + next = (XkbFile *) file->common.next; + + switch (file->file_type) { + case FILE_TYPE_KEYMAP: + FreeXkbFile((XkbFile *) file->defs); + break; + + case FILE_TYPE_TYPES: + case FILE_TYPE_COMPAT: + case FILE_TYPE_SYMBOLS: + case FILE_TYPE_KEYCODES: + case FILE_TYPE_GEOMETRY: + FreeStmt(file->defs); + break; + + default: + break; + } + + free(file->name); + free(file); + file = next; + } +} + +static const char *xkb_file_type_strings[_FILE_TYPE_NUM_ENTRIES] = { + [FILE_TYPE_KEYCODES] = "xkb_keycodes", + [FILE_TYPE_TYPES] = "xkb_types", + [FILE_TYPE_COMPAT] = "xkb_compatibility", + [FILE_TYPE_SYMBOLS] = "xkb_symbols", + [FILE_TYPE_GEOMETRY] = "xkb_geometry", + [FILE_TYPE_KEYMAP] = "xkb_keymap", + [FILE_TYPE_RULES] = "rules", +}; + +const char * +xkb_file_type_to_string(enum xkb_file_type type) +{ + if (type >= _FILE_TYPE_NUM_ENTRIES) + return "unknown"; + return xkb_file_type_strings[type]; +} + +static const char *stmt_type_strings[_STMT_NUM_VALUES] = { + [STMT_UNKNOWN] = "unknown statement", + [STMT_INCLUDE] = "include statement", + [STMT_KEYCODE] = "key name definition", + [STMT_ALIAS] = "key alias definition", + [STMT_EXPR] = "expression", + [STMT_VAR] = "variable definition", + [STMT_TYPE] = "key type definition", + [STMT_INTERP] = "symbol interpretation definition", + [STMT_VMOD] = "virtual modifiers definition", + [STMT_SYMBOLS] = "key symbols definition", + [STMT_MODMAP] = "modifier map declaration", + [STMT_GROUP_COMPAT] = "group declaration", + [STMT_LED_MAP] = "indicator map declaration", + [STMT_LED_NAME] = "indicator name declaration", +}; + +const char * +stmt_type_to_string(enum stmt_type type) +{ + if (type >= _STMT_NUM_VALUES) + return NULL; + return stmt_type_strings[type]; +} + +static const char *expr_op_type_strings[_EXPR_NUM_VALUES] = { + [EXPR_VALUE] = "literal", + [EXPR_IDENT] = "identifier", + [EXPR_ACTION_DECL] = "action declaration", + [EXPR_FIELD_REF] = "field reference", + [EXPR_ARRAY_REF] = "array reference", + [EXPR_KEYSYM_LIST] = "list of keysyms", + [EXPR_ACTION_LIST] = "list of actions", + [EXPR_ADD] = "addition", + [EXPR_SUBTRACT] = "subtraction", + [EXPR_MULTIPLY] = "multiplication", + [EXPR_DIVIDE] = "division", + [EXPR_ASSIGN] = "assignment", + [EXPR_NOT] = "logical negation", + [EXPR_NEGATE] = "arithmetic negation", + [EXPR_INVERT] = "bitwise inversion", + [EXPR_UNARY_PLUS] = "unary plus", +}; + +const char * +expr_op_type_to_string(enum expr_op_type type) +{ + if (type >= _EXPR_NUM_VALUES) + return NULL; + return expr_op_type_strings[type]; +} + +static const char *expr_value_type_strings[_EXPR_TYPE_NUM_VALUES] = { + [EXPR_TYPE_UNKNOWN] = "unknown", + [EXPR_TYPE_BOOLEAN] = "boolean", + [EXPR_TYPE_INT] = "int", + [EXPR_TYPE_FLOAT] = "float", + [EXPR_TYPE_STRING] = "string", + [EXPR_TYPE_ACTION] = "action", + [EXPR_TYPE_ACTIONS] = "actions", + [EXPR_TYPE_KEYNAME] = "keyname", + [EXPR_TYPE_SYMBOLS] = "symbols", +}; + +const char * +expr_value_type_to_string(enum expr_value_type type) +{ + if (type >= _EXPR_TYPE_NUM_VALUES) + return NULL; + return expr_value_type_strings[type]; +} diff --git a/src/xkbcomp/ast-build.h b/src/xkbcomp/ast-build.h new file mode 100644 index 0000000..3afb080 --- /dev/null +++ b/src/xkbcomp/ast-build.h @@ -0,0 +1,125 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_AST_BUILD_H +#define XKBCOMP_AST_BUILD_H + +ExprDef * +ExprCreateString(xkb_atom_t str); + +ExprDef * +ExprCreateInteger(int ival); + +ExprDef * +ExprCreateFloat(void); + +ExprDef * +ExprCreateBoolean(bool set); + +ExprDef * +ExprCreateKeyName(xkb_atom_t key_name); + +ExprDef * +ExprCreateIdent(xkb_atom_t ident); + +ExprDef * +ExprCreateUnary(enum expr_op_type op, enum expr_value_type type, + ExprDef *child); + +ExprDef * +ExprCreateBinary(enum expr_op_type op, ExprDef *left, ExprDef *right); + +ExprDef * +ExprCreateFieldRef(xkb_atom_t element, xkb_atom_t field); + +ExprDef * +ExprCreateArrayRef(xkb_atom_t element, xkb_atom_t field, ExprDef *entry); + +ExprDef * +ExprCreateAction(xkb_atom_t name, ExprDef *args); + +ExprDef * +ExprCreateActionList(ExprDef *actions); + +ExprDef * +ExprCreateMultiKeysymList(ExprDef *list); + +ExprDef * +ExprCreateKeysymList(xkb_keysym_t sym); + +ExprDef * +ExprAppendMultiKeysymList(ExprDef *list, ExprDef *append); + +ExprDef * +ExprAppendKeysymList(ExprDef *list, xkb_keysym_t sym); + +KeycodeDef * +KeycodeCreate(xkb_atom_t name, int64_t value); + +KeyAliasDef * +KeyAliasCreate(xkb_atom_t alias, xkb_atom_t real); + +VModDef * +VModCreate(xkb_atom_t name, ExprDef *value); + +VarDef * +VarCreate(ExprDef *name, ExprDef *value); + +VarDef * +BoolVarCreate(xkb_atom_t ident, bool set); + +InterpDef * +InterpCreate(xkb_keysym_t sym, ExprDef *match); + +KeyTypeDef * +KeyTypeCreate(xkb_atom_t name, VarDef *body); + +SymbolsDef * +SymbolsCreate(xkb_atom_t keyName, VarDef *symbols); + +GroupCompatDef * +GroupCompatCreate(unsigned group, ExprDef *def); + +ModMapDef * +ModMapCreate(xkb_atom_t modifier, ExprDef *keys); + +LedMapDef * +LedMapCreate(xkb_atom_t name, VarDef *body); + +LedNameDef * +LedNameCreate(unsigned ndx, ExprDef *name, bool virtual); + +IncludeStmt * +IncludeCreate(struct xkb_context *ctx, char *str, enum merge_mode merge); + +XkbFile * +XkbFileCreate(enum xkb_file_type type, char *name, ParseCommon *defs, + enum xkb_map_flags flags); + +void +FreeStmt(ParseCommon *stmt); + +#endif diff --git a/src/xkbcomp/ast.h b/src/xkbcomp/ast.h new file mode 100644 index 0000000..ee61106 --- /dev/null +++ b/src/xkbcomp/ast.h @@ -0,0 +1,360 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef XKBCOMP_AST_H +#define XKBCOMP_AST_H + +enum xkb_file_type { + /* Component files, by order of compilation. */ + FILE_TYPE_KEYCODES = 0, + FILE_TYPE_TYPES = 1, + FILE_TYPE_COMPAT = 2, + FILE_TYPE_SYMBOLS = 3, + /* Geometry is not compiled any more. */ + FILE_TYPE_GEOMETRY = 4, + + /* A top level file which includes the above files. */ + FILE_TYPE_KEYMAP, + +/* File types which must be found in a keymap file. */ +#define FIRST_KEYMAP_FILE_TYPE FILE_TYPE_KEYCODES +#define LAST_KEYMAP_FILE_TYPE FILE_TYPE_SYMBOLS + + /* This one doesn't mix with the others, but useful here as well. */ + FILE_TYPE_RULES, + + _FILE_TYPE_NUM_ENTRIES +}; + +enum stmt_type { + STMT_UNKNOWN = 0, + STMT_INCLUDE, + STMT_KEYCODE, + STMT_ALIAS, + STMT_EXPR, + STMT_VAR, + STMT_TYPE, + STMT_INTERP, + STMT_VMOD, + STMT_SYMBOLS, + STMT_MODMAP, + STMT_GROUP_COMPAT, + STMT_LED_MAP, + STMT_LED_NAME, + + _STMT_NUM_VALUES +}; + +enum expr_value_type { + EXPR_TYPE_UNKNOWN = 0, + EXPR_TYPE_BOOLEAN, + EXPR_TYPE_INT, + EXPR_TYPE_FLOAT, + EXPR_TYPE_STRING, + EXPR_TYPE_ACTION, + EXPR_TYPE_ACTIONS, + EXPR_TYPE_KEYNAME, + EXPR_TYPE_SYMBOLS, + + _EXPR_TYPE_NUM_VALUES +}; + +enum expr_op_type { + EXPR_VALUE, + EXPR_IDENT, + EXPR_ACTION_DECL, + EXPR_FIELD_REF, + EXPR_ARRAY_REF, + EXPR_KEYSYM_LIST, + EXPR_ACTION_LIST, + EXPR_ADD, + EXPR_SUBTRACT, + EXPR_MULTIPLY, + EXPR_DIVIDE, + EXPR_ASSIGN, + EXPR_NOT, + EXPR_NEGATE, + EXPR_INVERT, + EXPR_UNARY_PLUS, + + _EXPR_NUM_VALUES +}; + +enum merge_mode { + MERGE_DEFAULT, + MERGE_AUGMENT, + MERGE_OVERRIDE, + MERGE_REPLACE, +}; + +const char * +xkb_file_type_to_string(enum xkb_file_type type); + +const char * +stmt_type_to_string(enum stmt_type type); + +const char * +expr_op_type_to_string(enum expr_op_type type); + +const char * +expr_value_type_to_string(enum expr_value_type type); + +typedef struct _ParseCommon { + struct _ParseCommon *next; + enum stmt_type type; +} ParseCommon; + +typedef struct _IncludeStmt { + ParseCommon common; + enum merge_mode merge; + char *stmt; + char *file; + char *map; + char *modifier; + struct _IncludeStmt *next_incl; +} IncludeStmt; + +typedef struct { + ParseCommon common; + enum expr_op_type op; + enum expr_value_type value_type; +} ExprCommon; + +typedef union ExprDef ExprDef; + +typedef struct { + ExprCommon expr; + xkb_atom_t ident; +} ExprIdent; + +typedef struct { + ExprCommon expr; + xkb_atom_t str; +} ExprString; + +typedef struct { + ExprCommon expr; + bool set; +} ExprBoolean; + +typedef struct { + ExprCommon expr; + int ival; +} ExprInteger; + +typedef struct { + ExprCommon expr; + /* We don't support floats, but we still represnt them in the AST, in + * order to provide proper error messages. */ +} ExprFloat; + +typedef struct { + ExprCommon expr; + xkb_atom_t key_name; +} ExprKeyName; + +typedef struct { + ExprCommon expr; + ExprDef *left; + ExprDef *right; +} ExprBinary; + +typedef struct { + ExprCommon expr; + ExprDef *child; +} ExprUnary; + +typedef struct { + ExprCommon expr; + xkb_atom_t element; + xkb_atom_t field; +} ExprFieldRef; + +typedef struct { + ExprCommon expr; + xkb_atom_t element; + xkb_atom_t field; + ExprDef *entry; +} ExprArrayRef; + +typedef struct { + ExprCommon expr; + xkb_atom_t name; + ExprDef *args; +} ExprAction; + +typedef struct { + ExprCommon expr; + ExprDef *actions; +} ExprActionList; + +typedef struct { + ExprCommon expr; + darray(xkb_keysym_t) syms; + darray(unsigned int) symsMapIndex; + darray(unsigned int) symsNumEntries; +} ExprKeysymList; + +union ExprDef { + ParseCommon common; + /* Maybe someday we can use C11 anonymous struct for ExprCommon here. */ + ExprCommon expr; + ExprIdent ident; + ExprString string; + ExprBoolean boolean; + ExprInteger integer; + ExprKeyName key_name; + ExprBinary binary; + ExprUnary unary; + ExprFieldRef field_ref; + ExprArrayRef array_ref; + ExprAction action; + ExprActionList actions; + ExprKeysymList keysym_list; +}; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + ExprDef *name; + ExprDef *value; +} VarDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + ExprDef *value; +} VModDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + int64_t value; +} KeycodeDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t alias; + xkb_atom_t real; +} KeyAliasDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + VarDef *body; +} KeyTypeDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t keyName; + VarDef *symbols; +} SymbolsDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t modifier; + ExprDef *keys; +} ModMapDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + unsigned group; + ExprDef *def; +} GroupCompatDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_keysym_t sym; + ExprDef *match; + VarDef *def; +} InterpDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + unsigned ndx; + ExprDef *name; + bool virtual; +} LedNameDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + VarDef *body; +} LedMapDef; + +enum xkb_map_flags { + MAP_IS_DEFAULT = (1 << 0), + MAP_IS_PARTIAL = (1 << 1), + MAP_IS_HIDDEN = (1 << 2), + MAP_HAS_ALPHANUMERIC = (1 << 3), + MAP_HAS_MODIFIER = (1 << 4), + MAP_HAS_KEYPAD = (1 << 5), + MAP_HAS_FN = (1 << 6), + MAP_IS_ALTGR = (1 << 7), +}; + +typedef struct { + ParseCommon common; + enum xkb_file_type file_type; + char *name; + ParseCommon *defs; + enum xkb_map_flags flags; +} XkbFile; + +#endif diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c new file mode 100644 index 0000000..26b2bb7 --- /dev/null +++ b/src/xkbcomp/compat.c @@ -0,0 +1,934 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "action.h" +#include "vmod.h" +#include "include.h" + +enum si_field { + SI_FIELD_VIRTUAL_MOD = (1 << 0), + SI_FIELD_ACTION = (1 << 1), + SI_FIELD_AUTO_REPEAT = (1 << 2), + SI_FIELD_LEVEL_ONE_ONLY = (1 << 3), +}; + +typedef struct { + enum si_field defined; + enum merge_mode merge; + + struct xkb_sym_interpret interp; +} SymInterpInfo; + +enum led_field { + LED_FIELD_MODS = (1 << 0), + LED_FIELD_GROUPS = (1 << 1), + LED_FIELD_CTRLS = (1 << 2), +}; + +typedef struct { + enum led_field defined; + enum merge_mode merge; + + struct xkb_led led; +} LedInfo; + +typedef struct { + char *name; + int errorCount; + SymInterpInfo default_interp; + darray(SymInterpInfo) interps; + LedInfo default_led; + LedInfo leds[XKB_MAX_LEDS]; + unsigned int num_leds; + ActionsInfo *actions; + struct xkb_mod_set mods; + + struct xkb_context *ctx; +} CompatInfo; + +static const char * +siText(SymInterpInfo *si, CompatInfo *info) +{ + char *buf = xkb_context_get_buffer(info->ctx, 128); + + if (si == &info->default_interp) + return "default"; + + snprintf(buf, 128, "%s+%s(%s)", + KeysymText(info->ctx, si->interp.sym), + SIMatchText(si->interp.match), + ModMaskText(info->ctx, &info->mods, si->interp.mods)); + + return buf; +} + +static inline bool +ReportSINotArray(CompatInfo *info, SymInterpInfo *si, const char *field) +{ + return ReportNotArray(info->ctx, "symbol interpretation", field, + siText(si, info)); +} + +static inline bool +ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field, + const char *wanted) +{ + return ReportBadType(info->ctx, "symbol interpretation", field, + siText(si, info), wanted); +} + +static inline bool +ReportLedBadType(CompatInfo *info, LedInfo *ledi, const char *field, + const char *wanted) +{ + return ReportBadType(info->ctx, "indicator map", field, + xkb_atom_text(info->ctx, ledi->led.name), + wanted); +} + +static inline bool +ReportLedNotArray(CompatInfo *info, LedInfo *ledi, const char *field) +{ + return ReportNotArray(info->ctx, "indicator map", field, + xkb_atom_text(info->ctx, ledi->led.name)); +} + +static void +InitCompatInfo(CompatInfo *info, struct xkb_context *ctx, + ActionsInfo *actions, const struct xkb_mod_set *mods) +{ + memset(info, 0, sizeof(*info)); + info->ctx = ctx; + info->actions = actions; + info->mods = *mods; + info->default_interp.merge = MERGE_OVERRIDE; + info->default_interp.interp.virtual_mod = XKB_MOD_INVALID; + info->default_led.merge = MERGE_OVERRIDE; +} + +static void +ClearCompatInfo(CompatInfo *info) +{ + free(info->name); + darray_free(info->interps); +} + +static SymInterpInfo * +FindMatchingInterp(CompatInfo *info, SymInterpInfo *new) +{ + SymInterpInfo *old; + + darray_foreach(old, info->interps) + if (old->interp.sym == new->interp.sym && + old->interp.mods == new->interp.mods && + old->interp.match == new->interp.match) + return old; + + return NULL; +} + +static bool +UseNewInterpField(enum si_field field, SymInterpInfo *old, SymInterpInfo *new, + bool report, enum si_field *collide) +{ + if (!(old->defined & field)) + return true; + + if (new->defined & field) { + if (report) + *collide |= field; + + if (new->merge != MERGE_AUGMENT) + return true; + } + + return false; +} + +static bool +AddInterp(CompatInfo *info, SymInterpInfo *new, bool same_file) +{ + SymInterpInfo *old = FindMatchingInterp(info, new); + if (old) { + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + enum si_field collide = 0; + + if (new->merge == MERGE_REPLACE) { + if (report) + log_warn(info->ctx, + "Multiple definitions for \"%s\"; " + "Earlier interpretation ignored\n", + siText(new, info)); + *old = *new; + return true; + } + + if (UseNewInterpField(SI_FIELD_VIRTUAL_MOD, old, new, report, + &collide)) { + old->interp.virtual_mod = new->interp.virtual_mod; + old->defined |= SI_FIELD_VIRTUAL_MOD; + } + if (UseNewInterpField(SI_FIELD_ACTION, old, new, report, + &collide)) { + old->interp.action = new->interp.action; + old->defined |= SI_FIELD_ACTION; + } + if (UseNewInterpField(SI_FIELD_AUTO_REPEAT, old, new, report, + &collide)) { + old->interp.repeat = new->interp.repeat; + old->defined |= SI_FIELD_AUTO_REPEAT; + } + if (UseNewInterpField(SI_FIELD_LEVEL_ONE_ONLY, old, new, report, + &collide)) { + old->interp.level_one_only = new->interp.level_one_only; + old->defined |= SI_FIELD_LEVEL_ONE_ONLY; + } + + if (collide) { + log_warn(info->ctx, + "Multiple interpretations of \"%s\"; " + "Using %s definition for duplicate fields\n", + siText(new, info), + (new->merge != MERGE_AUGMENT ? "last" : "first")); + } + + return true; + } + + darray_append(info->interps, *new); + return true; +} + +/***====================================================================***/ + +static bool +ResolveStateAndPredicate(ExprDef *expr, enum xkb_match_operation *pred_rtrn, + xkb_mod_mask_t *mods_rtrn, CompatInfo *info) +{ + if (expr == NULL) { + *pred_rtrn = MATCH_ANY_OR_NONE; + *mods_rtrn = MOD_REAL_MASK_ALL; + return true; + } + + *pred_rtrn = MATCH_EXACTLY; + if (expr->expr.op == EXPR_ACTION_DECL) { + const char *pred_txt = xkb_atom_text(info->ctx, expr->action.name); + if (!LookupString(symInterpretMatchMaskNames, pred_txt, pred_rtrn) || + !expr->action.args || expr->action.args->common.next) { + log_err(info->ctx, + "Illegal modifier predicate \"%s\"; Ignored\n", pred_txt); + return false; + } + expr = expr->action.args; + } + else if (expr->expr.op == EXPR_IDENT) { + const char *pred_txt = xkb_atom_text(info->ctx, expr->ident.ident); + if (pred_txt && istreq(pred_txt, "any")) { + *pred_rtrn = MATCH_ANY; + *mods_rtrn = MOD_REAL_MASK_ALL; + return true; + } + } + + return ExprResolveModMask(info->ctx, expr, MOD_REAL, &info->mods, + mods_rtrn); +} + +/***====================================================================***/ + +static bool +UseNewLEDField(enum led_field field, LedInfo *old, LedInfo *new, + bool report, enum led_field *collide) +{ + if (!(old->defined & field)) + return true; + + if (new->defined & field) { + if (report) + *collide |= field; + + if (new->merge != MERGE_AUGMENT) + return true; + } + + return false; +} + +static bool +AddLedMap(CompatInfo *info, LedInfo *new, bool same_file) +{ + enum led_field collide; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + + for (xkb_led_index_t i = 0; i < info->num_leds; i++) { + LedInfo *old = &info->leds[i]; + + if (old->led.name != new->led.name) + continue; + + if (old->led.mods.mods == new->led.mods.mods && + old->led.groups == new->led.groups && + old->led.ctrls == new->led.ctrls && + old->led.which_mods == new->led.which_mods && + old->led.which_groups == new->led.which_groups) { + old->defined |= new->defined; + return true; + } + + if (new->merge == MERGE_REPLACE) { + if (report) + log_warn(info->ctx, + "Map for indicator %s redefined; " + "Earlier definition ignored\n", + xkb_atom_text(info->ctx, old->led.name)); + *old = *new; + return true; + } + + collide = 0; + if (UseNewLEDField(LED_FIELD_MODS, old, new, report, &collide)) { + old->led.which_mods = new->led.which_mods; + old->led.mods = new->led.mods; + old->defined |= LED_FIELD_MODS; + } + if (UseNewLEDField(LED_FIELD_GROUPS, old, new, report, &collide)) { + old->led.which_groups = new->led.which_groups; + old->led.groups = new->led.groups; + old->defined |= LED_FIELD_GROUPS; + } + if (UseNewLEDField(LED_FIELD_CTRLS, old, new, report, &collide)) { + old->led.ctrls = new->led.ctrls; + old->defined |= LED_FIELD_CTRLS; + } + + if (collide) { + log_warn(info->ctx, + "Map for indicator %s redefined; " + "Using %s definition for duplicate fields\n", + xkb_atom_text(info->ctx, old->led.name), + (new->merge == MERGE_AUGMENT ? "first" : "last")); + } + + return true; + } + + if (info->num_leds >= XKB_MAX_LEDS) { + log_err(info->ctx, + "Too many LEDs defined (maximum %d)\n", + XKB_MAX_LEDS); + return false; + } + info->leds[info->num_leds++] = *new; + return true; +} + +static void +MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from, + enum merge_mode merge) +{ + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + into->mods = from->mods; + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + if (darray_empty(into->interps)) { + into->interps = from->interps; + darray_init(from->interps); + } + else { + SymInterpInfo *si; + darray_foreach(si, from->interps) { + si->merge = (merge == MERGE_DEFAULT ? si->merge : merge); + if (!AddInterp(into, si, false)) + into->errorCount++; + } + } + + if (into->num_leds == 0) { + memcpy(into->leds, from->leds, sizeof(*from->leds) * from->num_leds); + into->num_leds = from->num_leds; + from->num_leds = 0; + } + else { + for (xkb_led_index_t i = 0; i < from->num_leds; i++) { + LedInfo *ledi = &from->leds[i]; + ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge); + if (!AddLedMap(into, ledi, false)) + into->errorCount++; + } + } +} + +static void +HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *include) +{ + CompatInfo included; + + InitCompatInfo(&included, info->ctx, info->actions, &info->mods); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + CompatInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_COMPAT); + if (!file) { + info->errorCount += 10; + ClearCompatInfo(&included); + return false; + } + + InitCompatInfo(&next_incl, info->ctx, info->actions, &included.mods); + next_incl.default_interp = info->default_interp; + next_incl.default_interp.merge = stmt->merge; + next_incl.default_led = info->default_led; + next_incl.default_led.merge = stmt->merge; + + HandleCompatMapFile(&next_incl, file, MERGE_OVERRIDE); + + MergeIncludedCompatMaps(&included, &next_incl, stmt->merge); + + ClearCompatInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedCompatMaps(info, &included, include->merge); + ClearCompatInfo(&included); + + return (info->errorCount == 0); +} + +static bool +SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field, + ExprDef *arrayNdx, ExprDef *value) +{ + xkb_mod_index_t ndx; + + if (istreq(field, "action")) { + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!HandleActionDef(info->ctx, info->actions, &info->mods, + value, &si->interp.action)) + return false; + + si->defined |= SI_FIELD_ACTION; + } + else if (istreq(field, "virtualmodifier") || + istreq(field, "virtualmod")) { + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!ExprResolveMod(info->ctx, value, MOD_VIRT, &info->mods, &ndx)) + return ReportSIBadType(info, si, field, "virtual modifier"); + + si->interp.virtual_mod = ndx; + si->defined |= SI_FIELD_VIRTUAL_MOD; + } + else if (istreq(field, "repeat")) { + bool set; + + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!ExprResolveBoolean(info->ctx, value, &set)) + return ReportSIBadType(info, si, field, "boolean"); + + si->interp.repeat = set; + + si->defined |= SI_FIELD_AUTO_REPEAT; + } + else if (istreq(field, "locking")) { + log_dbg(info->ctx, + "The \"locking\" field in symbol interpretation is unsupported; " + "Ignored\n"); + } + else if (istreq(field, "usemodmap") || + istreq(field, "usemodmapmods")) { + unsigned int val; + + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!ExprResolveEnum(info->ctx, value, &val, useModMapValueNames)) + return ReportSIBadType(info, si, field, "level specification"); + + si->interp.level_one_only = val; + si->defined |= SI_FIELD_LEVEL_ONE_ONLY; + } + else { + return ReportBadField(info->ctx, "symbol interpretation", field, + siText(si, info)); + } + + return true; +} + +static bool +SetLedMapField(CompatInfo *info, LedInfo *ledi, const char *field, + ExprDef *arrayNdx, ExprDef *value) +{ + bool ok = true; + + if (istreq(field, "modifiers") || istreq(field, "mods")) { + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveModMask(info->ctx, value, MOD_BOTH, + &info->mods, &ledi->led.mods.mods)) + return ReportLedBadType(info, ledi, field, "modifier mask"); + + ledi->defined |= LED_FIELD_MODS; + } + else if (istreq(field, "groups")) { + unsigned int mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, groupMaskNames)) + return ReportLedBadType(info, ledi, field, "group mask"); + + ledi->led.groups = mask; + ledi->defined |= LED_FIELD_GROUPS; + } + else if (istreq(field, "controls") || istreq(field, "ctrls")) { + unsigned int mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, ctrlMaskNames)) + return ReportLedBadType(info, ledi, field, "controls mask"); + + ledi->led.ctrls = mask; + ledi->defined |= LED_FIELD_CTRLS; + } + else if (istreq(field, "allowexplicit")) { + log_dbg(info->ctx, + "The \"allowExplicit\" field in indicator statements is unsupported; " + "Ignored\n"); + } + else if (istreq(field, "whichmodstate") || + istreq(field, "whichmodifierstate")) { + unsigned int mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, + modComponentMaskNames)) + return ReportLedBadType(info, ledi, field, + "mask of modifier state components"); + + ledi->led.which_mods = mask; + } + else if (istreq(field, "whichgroupstate")) { + unsigned mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, + groupComponentMaskNames)) + return ReportLedBadType(info, ledi, field, + "mask of group state components"); + + ledi->led.which_groups = mask; + } + else if (istreq(field, "driveskbd") || + istreq(field, "driveskeyboard") || + istreq(field, "leddriveskbd") || + istreq(field, "leddriveskeyboard") || + istreq(field, "indicatordriveskbd") || + istreq(field, "indicatordriveskeyboard")) { + log_dbg(info->ctx, + "The \"%s\" field in indicator statements is unsupported; " + "Ignored\n", field); + } + else if (istreq(field, "index")) { + /* Users should see this, it might cause unexpected behavior. */ + log_err(info->ctx, + "The \"index\" field in indicator statements is unsupported; " + "Ignored\n"); + } + else { + log_err(info->ctx, + "Unknown field %s in map for %s indicator; " + "Definition ignored\n", + field, xkb_atom_text(info->ctx, ledi->led.name)); + ok = false; + } + + return ok; +} + +static bool +HandleGlobalVar(CompatInfo *info, VarDef *stmt) +{ + const char *elem, *field; + ExprDef *ndx; + bool ret; + + if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &ndx)) + ret = false; + else if (elem && istreq(elem, "interpret")) + ret = SetInterpField(info, &info->default_interp, field, ndx, + stmt->value); + else if (elem && istreq(elem, "indicator")) + ret = SetLedMapField(info, &info->default_led, field, ndx, + stmt->value); + else + ret = SetActionField(info->ctx, info->actions, &info->mods, + elem, field, ndx, stmt->value); + return ret; +} + +static bool +HandleInterpBody(CompatInfo *info, VarDef *def, SymInterpInfo *si) +{ + bool ok = true; + const char *elem, *field; + ExprDef *arrayNdx; + + for (; def; def = (VarDef *) def->common.next) { + if (def->name && def->name->expr.op == EXPR_FIELD_REF) { + log_err(info->ctx, + "Cannot set a global default value from within an interpret statement; " + "Move statements to the global file scope\n"); + ok = false; + continue; + } + + ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, &arrayNdx); + if (!ok) + continue; + + ok = SetInterpField(info, si, field, arrayNdx, def->value); + } + + return ok; +} + +static bool +HandleInterpDef(CompatInfo *info, InterpDef *def, enum merge_mode merge) +{ + enum xkb_match_operation pred; + xkb_mod_mask_t mods; + SymInterpInfo si; + + if (!ResolveStateAndPredicate(def->match, &pred, &mods, info)) { + log_err(info->ctx, + "Couldn't determine matching modifiers; " + "Symbol interpretation ignored\n"); + return false; + } + + si = info->default_interp; + si.merge = merge = (def->merge == MERGE_DEFAULT ? merge : def->merge); + si.interp.sym = def->sym; + si.interp.match = pred; + si.interp.mods = mods; + + if (!HandleInterpBody(info, def->def, &si)) { + info->errorCount++; + return false; + } + + if (!AddInterp(info, &si, true)) { + info->errorCount++; + return false; + } + + return true; +} + +static bool +HandleLedMapDef(CompatInfo *info, LedMapDef *def, enum merge_mode merge) +{ + LedInfo ledi; + VarDef *var; + bool ok; + + if (def->merge != MERGE_DEFAULT) + merge = def->merge; + + ledi = info->default_led; + ledi.merge = merge; + ledi.led.name = def->name; + + ok = true; + for (var = def->body; var != NULL; var = (VarDef *) var->common.next) { + const char *elem, *field; + ExprDef *arrayNdx; + if (!ExprResolveLhs(info->ctx, var->name, &elem, &field, &arrayNdx)) { + ok = false; + continue; + } + + if (elem) { + log_err(info->ctx, + "Cannot set defaults for \"%s\" element in indicator map; " + "Assignment to %s.%s ignored\n", elem, elem, field); + ok = false; + } + else { + ok = SetLedMapField(info, &ledi, field, arrayNdx, var->value) && ok; + } + } + + if (ok) + return AddLedMap(info, &ledi, true); + + return false; +} + +static void +HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge); + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt); + break; + case STMT_INTERP: + ok = HandleInterpDef(info, (InterpDef *) stmt, merge); + break; + case STMT_GROUP_COMPAT: + log_dbg(info->ctx, + "The \"group\" statement in compat is unsupported; " + "Ignored\n"); + ok = true; + break; + case STMT_LED_MAP: + ok = HandleLedMapDef(info, (LedMapDef *) stmt, merge); + break; + case STMT_VAR: + ok = HandleGlobalVar(info, (VarDef *) stmt); + break; + case STMT_VMOD: + ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge); + break; + default: + log_err(info->ctx, + "Compat files may not include other types; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, + "Abandoning compatibility map \"%s\"\n", file->name); + break; + } + } +} + +/* Temporary struct for CopyInterps. */ +struct collect { + darray(struct xkb_sym_interpret) sym_interprets; +}; + +static void +CopyInterps(CompatInfo *info, bool needSymbol, enum xkb_match_operation pred, + struct collect *collect) +{ + SymInterpInfo *si; + + darray_foreach(si, info->interps) + if (si->interp.match == pred && + (si->interp.sym != XKB_KEY_NoSymbol) == needSymbol) + darray_append(collect->sym_interprets, si->interp); +} + +static void +CopyLedMapDefsToKeymap(struct xkb_keymap *keymap, CompatInfo *info) +{ + for (xkb_led_index_t idx = 0; idx < info->num_leds; idx++) { + LedInfo *ledi = &info->leds[idx]; + xkb_led_index_t i; + struct xkb_led *led; + + /* + * Find the LED with the given name, if it was already declared + * in keycodes. + */ + xkb_leds_enumerate(i, led, keymap) + if (led->name == ledi->led.name) + break; + + /* Not previously declared; create it with next free index. */ + if (i >= keymap->num_leds) { + log_dbg(keymap->ctx, + "Indicator name \"%s\" was not declared in the keycodes section; " + "Adding new indicator\n", + xkb_atom_text(keymap->ctx, ledi->led.name)); + + xkb_leds_enumerate(i, led, keymap) + if (led->name == XKB_ATOM_NONE) + break; + + if (i >= keymap->num_leds) { + /* Not place to put it; ignore. */ + if (i >= XKB_MAX_LEDS) { + log_err(keymap->ctx, + "Too many indicators (maximum is %d); " + "Indicator name \"%s\" ignored\n", + XKB_MAX_LEDS, + xkb_atom_text(keymap->ctx, ledi->led.name)); + continue; + } + + /* Add a new LED. */ + led = &keymap->leds[keymap->num_leds++]; + } + } + + *led = ledi->led; + if (led->groups != 0 && led->which_groups == 0) + led->which_groups = XKB_STATE_LAYOUT_EFFECTIVE; + if (led->mods.mods != 0 && led->which_mods == 0) + led->which_mods = XKB_STATE_MODS_EFFECTIVE; + } +} + +static bool +CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info) +{ + keymap->compat_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->compat_section_name); + + keymap->mods = info->mods; + + if (!darray_empty(info->interps)) { + struct collect collect; + darray_init(collect.sym_interprets); + + /* Most specific to least specific. */ + CopyInterps(info, true, MATCH_EXACTLY, &collect); + CopyInterps(info, true, MATCH_ALL, &collect); + CopyInterps(info, true, MATCH_NONE, &collect); + CopyInterps(info, true, MATCH_ANY, &collect); + CopyInterps(info, true, MATCH_ANY_OR_NONE, &collect); + CopyInterps(info, false, MATCH_EXACTLY, &collect); + CopyInterps(info, false, MATCH_ALL, &collect); + CopyInterps(info, false, MATCH_NONE, &collect); + CopyInterps(info, false, MATCH_ANY, &collect); + CopyInterps(info, false, MATCH_ANY_OR_NONE, &collect); + + darray_steal(collect.sym_interprets, + &keymap->sym_interprets, &keymap->num_sym_interprets); + } + + CopyLedMapDefsToKeymap(keymap, info); + + return true; +} + +bool +CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + CompatInfo info; + ActionsInfo *actions; + + actions = NewActionsInfo(); + if (!actions) + return false; + + InitCompatInfo(&info, keymap->ctx, actions, &keymap->mods); + info.default_interp.merge = merge; + info.default_led.merge = merge; + + HandleCompatMapFile(&info, file, merge); + if (info.errorCount != 0) + goto err_info; + + if (!CopyCompatToKeymap(keymap, &info)) + goto err_info; + + ClearCompatInfo(&info); + FreeActionsInfo(actions); + return true; + +err_info: + ClearCompatInfo(&info); + FreeActionsInfo(actions); + return false; +} diff --git a/src/xkbcomp/expr.c b/src/xkbcomp/expr.c new file mode 100644 index 0000000..dbdf734 --- /dev/null +++ b/src/xkbcomp/expr.c @@ -0,0 +1,694 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" + +typedef bool (*IdentLookupFunc)(struct xkb_context *ctx, const void *priv, + xkb_atom_t field, enum expr_value_type type, + unsigned int *val_rtrn); + +bool +ExprResolveLhs(struct xkb_context *ctx, const ExprDef *expr, + const char **elem_rtrn, const char **field_rtrn, + ExprDef **index_rtrn) +{ + switch (expr->expr.op) { + case EXPR_IDENT: + *elem_rtrn = NULL; + *field_rtrn = xkb_atom_text(ctx, expr->ident.ident); + *index_rtrn = NULL; + return (*field_rtrn != NULL); + case EXPR_FIELD_REF: + *elem_rtrn = xkb_atom_text(ctx, expr->field_ref.element); + *field_rtrn = xkb_atom_text(ctx, expr->field_ref.field); + *index_rtrn = NULL; + return (*elem_rtrn != NULL && *field_rtrn != NULL); + case EXPR_ARRAY_REF: + *elem_rtrn = xkb_atom_text(ctx, expr->array_ref.element); + *field_rtrn = xkb_atom_text(ctx, expr->array_ref.field); + *index_rtrn = expr->array_ref.entry; + if (expr->array_ref.element != XKB_ATOM_NONE && *elem_rtrn == NULL) + return false; + if (*field_rtrn == NULL) + return false; + return true; + default: + break; + } + log_wsgo(ctx, "Unexpected operator %d in ResolveLhs\n", expr->expr.op); + return false; +} + +static bool +SimpleLookup(struct xkb_context *ctx, const void *priv, xkb_atom_t field, + enum expr_value_type type, unsigned int *val_rtrn) +{ + const LookupEntry *entry; + const char *str; + + if (!priv || field == XKB_ATOM_NONE || type != EXPR_TYPE_INT) + return false; + + str = xkb_atom_text(ctx, field); + for (entry = priv; entry && entry->name; entry++) { + if (istreq(str, entry->name)) { + *val_rtrn = entry->value; + return true; + } + } + + return false; +} + +/* Data passed in the *priv argument for LookupModMask. */ +typedef struct { + const struct xkb_mod_set *mods; + enum mod_type mod_type; +} LookupModMaskPriv; + +static bool +LookupModMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field, + enum expr_value_type type, xkb_mod_mask_t *val_rtrn) +{ + const char *str; + xkb_mod_index_t ndx; + const LookupModMaskPriv *arg = priv; + const struct xkb_mod_set *mods = arg->mods; + enum mod_type mod_type = arg->mod_type; + + if (type != EXPR_TYPE_INT) + return false; + + str = xkb_atom_text(ctx, field); + if (!str) + return false; + + if (istreq(str, "all")) { + *val_rtrn = MOD_REAL_MASK_ALL; + return true; + } + + if (istreq(str, "none")) { + *val_rtrn = 0; + return true; + } + + ndx = XkbModNameToIndex(mods, field, mod_type); + if (ndx == XKB_MOD_INVALID) + return false; + + *val_rtrn = (1u << ndx); + return true; +} + +bool +ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr, + bool *set_rtrn) +{ + bool ok = false; + const char *ident; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_BOOLEAN) { + log_err(ctx, + "Found constant of type %s where boolean was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + *set_rtrn = expr->boolean.set; + return true; + + case EXPR_IDENT: + ident = xkb_atom_text(ctx, expr->ident.ident); + if (ident) { + if (istreq(ident, "true") || + istreq(ident, "yes") || + istreq(ident, "on")) { + *set_rtrn = true; + return true; + } + else if (istreq(ident, "false") || + istreq(ident, "no") || + istreq(ident, "off")) { + *set_rtrn = false; + return true; + } + } + log_err(ctx, "Identifier \"%s\" of type boolean is unknown\n", ident); + return false; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type boolean is unknown\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_INVERT: + case EXPR_NOT: + ok = ExprResolveBoolean(ctx, expr->unary.child, set_rtrn); + if (ok) + *set_rtrn = !*set_rtrn; + return ok; + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + case EXPR_ASSIGN: + case EXPR_NEGATE: + case EXPR_UNARY_PLUS: + case EXPR_ACTION_DECL: + case EXPR_ACTION_LIST: + case EXPR_KEYSYM_LIST: + log_err(ctx, "%s of boolean values not permitted\n", + expr_op_type_to_string(expr->expr.op)); + break; + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveBoolean\n", + expr->expr.op); + break; + } + + return false; +} + +bool +ExprResolveKeyCode(struct xkb_context *ctx, const ExprDef *expr, + xkb_keycode_t *kc) +{ + xkb_keycode_t leftRtrn, rightRtrn; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_INT) { + log_err(ctx, + "Found constant of type %s where an int was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + + *kc = (xkb_keycode_t) expr->integer.ival; + return true; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + if (!ExprResolveKeyCode(ctx, expr->binary.left, &leftRtrn) || + !ExprResolveKeyCode(ctx, expr->binary.right, &rightRtrn)) + return false; + + switch (expr->expr.op) { + case EXPR_ADD: + *kc = leftRtrn + rightRtrn; + break; + case EXPR_SUBTRACT: + *kc = leftRtrn - rightRtrn; + break; + case EXPR_MULTIPLY: + *kc = leftRtrn * rightRtrn; + break; + case EXPR_DIVIDE: + if (rightRtrn == 0) { + log_err(ctx, "Cannot divide by zero: %d / %d\n", + leftRtrn, rightRtrn); + return false; + } + + *kc = leftRtrn / rightRtrn; + break; + default: + break; + } + + return true; + + case EXPR_NEGATE: + if (!ExprResolveKeyCode(ctx, expr->unary.child, &leftRtrn)) + return false; + + *kc = ~leftRtrn; + return true; + + case EXPR_UNARY_PLUS: + return ExprResolveKeyCode(ctx, expr->unary.child, kc); + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveKeyCode\n", + expr->expr.op); + break; + } + + return false; +} + +/** + * This function returns ... something. It's a bit of a guess, really. + * + * If an integer is given in value ctx, it will be returned in ival. + * If an ident or field reference is given, the lookup function (if given) + * will be called. At the moment, only SimpleLookup use this, and they both + * return the results in uval. And don't support field references. + * + * Cool. + */ +static bool +ExprResolveIntegerLookup(struct xkb_context *ctx, const ExprDef *expr, + int *val_rtrn, IdentLookupFunc lookup, + const void *lookupPriv) +{ + bool ok = false; + int l, r; + unsigned u; + ExprDef *left, *right; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_INT) { + log_err(ctx, + "Found constant of type %s where an int was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + + *val_rtrn = expr->integer.ival; + return true; + + case EXPR_IDENT: + if (lookup) + ok = lookup(ctx, lookupPriv, expr->ident.ident, EXPR_TYPE_INT, &u); + + if (!ok) + log_err(ctx, "Identifier \"%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->ident.ident)); + else + *val_rtrn = (int) u; + + return ok; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + left = expr->binary.left; + right = expr->binary.right; + if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv) || + !ExprResolveIntegerLookup(ctx, right, &r, lookup, lookupPriv)) + return false; + + switch (expr->expr.op) { + case EXPR_ADD: + *val_rtrn = l + r; + break; + case EXPR_SUBTRACT: + *val_rtrn = l - r; + break; + case EXPR_MULTIPLY: + *val_rtrn = l * r; + break; + case EXPR_DIVIDE: + if (r == 0) { + log_err(ctx, "Cannot divide by zero: %d / %d\n", l, r); + return false; + } + *val_rtrn = l / r; + break; + default: + log_err(ctx, "%s of integers not permitted\n", + expr_op_type_to_string(expr->expr.op)); + return false; + } + + return true; + + case EXPR_ASSIGN: + log_wsgo(ctx, "Assignment operator not implemented yet\n"); + break; + + case EXPR_NOT: + log_err(ctx, "The ! operator cannot be applied to an integer\n"); + return false; + + case EXPR_INVERT: + case EXPR_NEGATE: + left = expr->unary.child; + if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv)) + return false; + + *val_rtrn = (expr->expr.op == EXPR_NEGATE ? -l : ~l); + return true; + + case EXPR_UNARY_PLUS: + left = expr->unary.child; + return ExprResolveIntegerLookup(ctx, left, val_rtrn, lookup, + lookupPriv); + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveInteger\n", + expr->expr.op); + break; + } + + return false; +} + +bool +ExprResolveInteger(struct xkb_context *ctx, const ExprDef *expr, + int *val_rtrn) +{ + return ExprResolveIntegerLookup(ctx, expr, val_rtrn, NULL, NULL); +} + +bool +ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr, + xkb_layout_index_t *group_rtrn) +{ + bool ok; + int result; + + ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup, + groupNames); + if (!ok) + return false; + + if (result <= 0 || result > XKB_MAX_GROUPS) { + log_err(ctx, "Group index %u is out of range (1..%d)\n", + result, XKB_MAX_GROUPS); + return false; + } + + *group_rtrn = (xkb_layout_index_t) result; + return true; +} + +bool +ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr, + xkb_level_index_t *level_rtrn) +{ + bool ok; + int result; + + ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup, + levelNames); + if (!ok) + return false; + + if (result < 1) { + log_err(ctx, "Shift level %d is out of range\n", result); + return false; + } + + /* Level is zero-indexed from now on. */ + *level_rtrn = (unsigned int) (result - 1); + return true; +} + +bool +ExprResolveButton(struct xkb_context *ctx, const ExprDef *expr, int *btn_rtrn) +{ + return ExprResolveIntegerLookup(ctx, expr, btn_rtrn, SimpleLookup, + buttonNames); +} + +bool +ExprResolveString(struct xkb_context *ctx, const ExprDef *expr, + xkb_atom_t *val_rtrn) +{ + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_STRING) { + log_err(ctx, "Found constant of type %s, expected a string\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + + *val_rtrn = expr->string.str; + return true; + + case EXPR_IDENT: + log_err(ctx, "Identifier \"%s\" of type string not found\n", + xkb_atom_text(ctx, expr->ident.ident)); + return false; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type string not found\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + case EXPR_ASSIGN: + case EXPR_NEGATE: + case EXPR_INVERT: + case EXPR_NOT: + case EXPR_UNARY_PLUS: + case EXPR_ACTION_DECL: + case EXPR_ACTION_LIST: + case EXPR_KEYSYM_LIST: + log_err(ctx, "%s of strings not permitted\n", + expr_op_type_to_string(expr->expr.op)); + return false; + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveString\n", + expr->expr.op); + break; + } + return false; +} + +bool +ExprResolveEnum(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *val_rtrn, const LookupEntry *values) +{ + if (expr->expr.op != EXPR_IDENT) { + log_err(ctx, "Found a %s where an enumerated value was expected\n", + expr_op_type_to_string(expr->expr.op)); + return false; + } + + if (!SimpleLookup(ctx, values, expr->ident.ident, EXPR_TYPE_INT, + val_rtrn)) { + log_err(ctx, "Illegal identifier %s; expected one of:\n", + xkb_atom_text(ctx, expr->ident.ident)); + while (values && values->name) + { + log_err(ctx, "\t%s\n", values->name); + values++; + } + return false; + } + + return true; +} + +static bool +ExprResolveMaskLookup(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *val_rtrn, IdentLookupFunc lookup, + const void *lookupPriv) +{ + bool ok = false; + unsigned int l = 0, r = 0; + int v; + ExprDef *left, *right; + const char *bogus = NULL; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_INT) { + log_err(ctx, + "Found constant of type %s where a mask was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + *val_rtrn = (unsigned int) expr->integer.ival; + return true; + + case EXPR_IDENT: + ok = lookup(ctx, lookupPriv, expr->ident.ident, EXPR_TYPE_INT, + val_rtrn); + if (!ok) + log_err(ctx, "Identifier \"%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->ident.ident)); + return ok; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_ARRAY_REF: + bogus = "array reference"; + /* fallthrough */ + case EXPR_ACTION_DECL: + if (bogus == NULL) + bogus = "function use"; + log_err(ctx, + "Unexpected %s in mask expression; Expression Ignored\n", + bogus); + return false; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + left = expr->binary.left; + right = expr->binary.right; + if (!ExprResolveMaskLookup(ctx, left, &l, lookup, lookupPriv) || + !ExprResolveMaskLookup(ctx, right, &r, lookup, lookupPriv)) + return false; + + switch (expr->expr.op) { + case EXPR_ADD: + *val_rtrn = l | r; + break; + case EXPR_SUBTRACT: + *val_rtrn = l & (~r); + break; + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + log_err(ctx, "Cannot %s masks; Illegal operation ignored\n", + (expr->expr.op == EXPR_DIVIDE ? "divide" : "multiply")); + return false; + default: + break; + } + + return true; + + case EXPR_ASSIGN: + log_wsgo(ctx, "Assignment operator not implemented yet\n"); + break; + + case EXPR_INVERT: + left = expr->unary.child; + if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv)) + return false; + + *val_rtrn = ~v; + return true; + + case EXPR_UNARY_PLUS: + case EXPR_NEGATE: + case EXPR_NOT: + left = expr->unary.child; + if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv)) + log_err(ctx, "The %s operator cannot be used with a mask\n", + (expr->expr.op == EXPR_NEGATE ? "-" : "!")); + return false; + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveMask\n", + expr->expr.op); + break; + } + + return false; +} + +bool +ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *mask_rtrn, const LookupEntry *values) +{ + return ExprResolveMaskLookup(ctx, expr, mask_rtrn, SimpleLookup, values); +} + +bool +ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_mask_t *mask_rtrn) +{ + LookupModMaskPriv priv = { .mods = mods, .mod_type = mod_type }; + return ExprResolveMaskLookup(ctx, expr, mask_rtrn, LookupModMask, &priv); +} + +bool +ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, + xkb_keysym_t *sym_rtrn) +{ + int val; + + if (expr->expr.op == EXPR_IDENT) { + const char *str = xkb_atom_text(ctx, expr->ident.ident); + *sym_rtrn = xkb_keysym_from_name(str, 0); + if (*sym_rtrn != XKB_KEY_NoSymbol) + return true; + } + + if (!ExprResolveInteger(ctx, expr, &val)) + return false; + + if (val < 0 || val >= 10) + return false; + + *sym_rtrn = XKB_KEY_0 + (xkb_keysym_t) val; + return true; +} + +bool +ExprResolveMod(struct xkb_context *ctx, const ExprDef *def, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_index_t *ndx_rtrn) +{ + xkb_mod_index_t ndx; + xkb_atom_t name; + + if (def->expr.op != EXPR_IDENT) { + log_err(ctx, + "Cannot resolve virtual modifier: " + "found %s where a virtual modifier name was expected\n", + expr_op_type_to_string(def->expr.op)); + return false; + } + + name = def->ident.ident; + ndx = XkbModNameToIndex(mods, name, mod_type); + if (ndx == XKB_MOD_INVALID) { + log_err(ctx, + "Cannot resolve virtual modifier: " + "\"%s\" was not previously declared\n", + xkb_atom_text(ctx, name)); + return false; + } + + *ndx_rtrn = ndx; + return true; +} diff --git a/src/xkbcomp/expr.h b/src/xkbcomp/expr.h new file mode 100644 index 0000000..9882b8c --- /dev/null +++ b/src/xkbcomp/expr.h @@ -0,0 +1,85 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_EXPR_H +#define XKBCOMP_EXPR_H + +bool +ExprResolveLhs(struct xkb_context *ctx, const ExprDef *expr, + const char **elem_rtrn, const char **field_rtrn, + ExprDef **index_rtrn); + +bool +ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_mask_t *mask_rtrn); + +bool +ExprResolveMod(struct xkb_context *ctx, const ExprDef *def, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_index_t *ndx_rtrn); + +bool +ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr, + bool *set_rtrn); + +bool +ExprResolveKeyCode(struct xkb_context *ctx, const ExprDef *expr, + xkb_keycode_t *kc); + +bool +ExprResolveInteger(struct xkb_context *ctx, const ExprDef *expr, + int *val_rtrn); + +bool +ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr, + xkb_level_index_t *level_rtrn); + +bool +ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr, + xkb_layout_index_t *group_rtrn); + +bool +ExprResolveButton(struct xkb_context *ctx, const ExprDef *expr, + int *btn_rtrn); + +bool +ExprResolveString(struct xkb_context *ctx, const ExprDef *expr, + xkb_atom_t *val_rtrn); + +bool +ExprResolveEnum(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *val_rtrn, const LookupEntry *values); + +bool +ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *mask_rtrn, const LookupEntry *values); + +bool +ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, + xkb_keysym_t *sym_rtrn); + +#endif diff --git a/src/xkbcomp/include.c b/src/xkbcomp/include.c new file mode 100644 index 0000000..d39be89 --- /dev/null +++ b/src/xkbcomp/include.c @@ -0,0 +1,323 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> + +#include "xkbcomp-priv.h" +#include "include.h" + +/** + * Parse an include statement. Each call returns a file name, along with + * (possibly) a specific map in the file, an explicit group designator, and + * the separator from the next file, used to determine the merge mode. + * + * @param str_inout Input statement, modified in-place. Should be passed in + * repeatedly. If str_inout is NULL, the parsing has completed. + * + * @param file_rtrn Set to the name of the include file to be used. Combined + * with an enum xkb_file_type, this determines which file to look for in the + * include path. + * + * @param map_rtrn Set to the string between '(' and ')', if any. This will + * result in the compilation of a specific named map within the file (e.g. + * xkb_symbols "basic" { ... }) , as opposed to the default map of the file. + * + * @param nextop_rtrn Set to the next operation in the complete statement, + * which is '\0' if it's the last file or '+' or '|' if there are more. + * Separating the files with '+' sets the merge mode to MERGE_MODE_OVERRIDE, + * while '|' sets the merge mode to MERGE_MODE_AUGMENT. + * + * @param extra_data Set to the string after ':', if any. Currently the + * extra data is only used for setting an explicit group index for a symbols + * file. + * + * @return true if parsing was successful, false for an illegal string. + * + * Example: "evdev+aliases(qwerty):2" + * str_inout = "aliases(qwerty):2" + * file_rtrn = "evdev" + * map_rtrn = NULL + * nextop_retrn = "+" + * extra_data = NULL + * + * 2nd run with "aliases(qwerty):2" + * str_inout = NULL + * file_rtrn = "aliases" + * map_rtrn = "qwerty" + * nextop_retrn = "" + * extra_data = "2" + * + */ +bool +ParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn, + char *nextop_rtrn, char **extra_data) +{ + char *tmp, *str, *next; + + str = *str_inout; + + /* + * Find the position in the string where the next file is included, + * if there is more than one left in the statement. + */ + next = strpbrk(str, "|+"); + if (next) { + /* Got more files, this function will be called again. */ + *nextop_rtrn = *next; + /* Separate the string, for strchr etc. to work on this file only. */ + *next++ = '\0'; + } + else { + /* This is the last file in this statement, won't be called again. */ + *nextop_rtrn = '\0'; + next = NULL; + } + + /* + * Search for the explicit group designator, if any. If it's there, + * it goes after the file name and map. + */ + tmp = strchr(str, ':'); + if (tmp != NULL) { + *tmp++ = '\0'; + *extra_data = strdup(tmp); + } + else { + *extra_data = NULL; + } + + /* Look for a map, if any. */ + tmp = strchr(str, '('); + if (tmp == NULL) { + /* No map. */ + *file_rtrn = strdup(str); + *map_rtrn = NULL; + } + else if (str[0] == '(') { + /* Map without file - invalid. */ + free(*extra_data); + return false; + } + else { + /* Got a map; separate the file and the map for the strdup's. */ + *tmp++ = '\0'; + *file_rtrn = strdup(str); + str = tmp; + tmp = strchr(str, ')'); + if (tmp == NULL || tmp[1] != '\0') { + free(*file_rtrn); + free(*extra_data); + return false; + } + *tmp++ = '\0'; + *map_rtrn = strdup(str); + } + + /* Set up the next file for the next call, if any. */ + if (*nextop_rtrn == '\0') + *str_inout = NULL; + else if (*nextop_rtrn == '|' || *nextop_rtrn == '+') + *str_inout = next; + else + return false; + + return true; +} + +static const char *xkb_file_type_include_dirs[_FILE_TYPE_NUM_ENTRIES] = { + [FILE_TYPE_KEYCODES] = "keycodes", + [FILE_TYPE_TYPES] = "types", + [FILE_TYPE_COMPAT] = "compat", + [FILE_TYPE_SYMBOLS] = "symbols", + [FILE_TYPE_GEOMETRY] = "geometry", + [FILE_TYPE_KEYMAP] = "keymap", + [FILE_TYPE_RULES] = "rules", +}; + +/** + * Return the xkb directory based on the type. + */ +static const char * +DirectoryForInclude(enum xkb_file_type type) +{ + if (type >= _FILE_TYPE_NUM_ENTRIES) + return ""; + return xkb_file_type_include_dirs[type]; +} + +static void +LogIncludePaths(struct xkb_context *ctx) +{ + unsigned int i; + + if (xkb_context_num_include_paths(ctx) > 0) { + log_err(ctx, "%d include paths searched:\n", + xkb_context_num_include_paths(ctx)); + for (i = 0; i < xkb_context_num_include_paths(ctx); i++) + log_err(ctx, "\t%s\n", + xkb_context_include_path_get(ctx, i)); + } + else { + log_err(ctx, "There are no include paths to search\n"); + } + + if (xkb_context_num_failed_include_paths(ctx) > 0) { + log_err(ctx, "%d include paths could not be added:\n", + xkb_context_num_failed_include_paths(ctx)); + for (i = 0; i < xkb_context_num_failed_include_paths(ctx); i++) + log_err(ctx, "\t%s\n", + xkb_context_failed_include_path_get(ctx, i)); + } +} + +/** + * Return an open file handle to the first file (counting from offset) with the + * given name in the include paths, starting at the offset. + * + * offset must be zero the first time this is called and is set to the index the + * file was found. Call again with offset+1 to keep searching through the + * include paths. + * + * If this function returns NULL, no more files are available. + */ +FILE * +FindFileInXkbPath(struct xkb_context *ctx, const char *name, + enum xkb_file_type type, char **pathRtrn, + unsigned int *offset) +{ + unsigned int i; + FILE *file = NULL; + char *buf = NULL; + const char *typeDir; + + typeDir = DirectoryForInclude(type); + + for (i = *offset; i < xkb_context_num_include_paths(ctx); i++) { + buf = asprintf_safe("%s/%s/%s", xkb_context_include_path_get(ctx, i), + typeDir, name); + if (!buf) { + log_err(ctx, "Failed to alloc buffer for (%s/%s/%s)\n", + xkb_context_include_path_get(ctx, i), typeDir, name); + continue; + } + + file = fopen(buf, "rb"); + if (file) { + if (pathRtrn) { + *pathRtrn = buf; + buf = NULL; + } + *offset = i; + goto out; + } + } + + /* We only print warnings if we can't find the file on the first lookup */ + if (*offset == 0) { + log_err(ctx, "Couldn't find file \"%s/%s\" in include paths\n", + typeDir, name); + LogIncludePaths(ctx); + } + +out: + free(buf); + return file; +} + +XkbFile * +ProcessIncludeFile(struct xkb_context *ctx, IncludeStmt *stmt, + enum xkb_file_type file_type) +{ + FILE *file; + XkbFile *xkb_file = NULL; + unsigned int offset = 0; + + file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL, &offset); + if (!file) + return NULL; + + while (file) { + xkb_file = XkbParseFile(ctx, file, stmt->file, stmt->map); + fclose(file); + + if (xkb_file) { + if (xkb_file->file_type != file_type) { + log_err(ctx, + "Include file of wrong type (expected %s, got %s); " + "Include file \"%s\" ignored\n", + xkb_file_type_to_string(file_type), + xkb_file_type_to_string(xkb_file->file_type), stmt->file); + FreeXkbFile(xkb_file); + xkb_file = NULL; + } else { + break; + } + } + + offset++; + file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL, &offset); + } + + if (!xkb_file) { + if (stmt->map) + log_err(ctx, "Couldn't process include statement for '%s(%s)'\n", + stmt->file, stmt->map); + else + log_err(ctx, "Couldn't process include statement for '%s'\n", + stmt->file); + } + + /* FIXME: we have to check recursive includes here (or somewhere) */ + + return xkb_file; +} diff --git a/src/xkbcomp/include.h b/src/xkbcomp/include.h new file mode 100644 index 0000000..8f360e3 --- /dev/null +++ b/src/xkbcomp/include.h @@ -0,0 +1,43 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_INCLUDE_H +#define XKBCOMP_INCLUDE_H + +bool +ParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn, + char *nextop_rtrn, char **extra_data); + +FILE * +FindFileInXkbPath(struct xkb_context *ctx, const char *name, + enum xkb_file_type type, char **pathRtrn, + unsigned int *offset); + +XkbFile * +ProcessIncludeFile(struct xkb_context *ctx, IncludeStmt *stmt, + enum xkb_file_type file_type); + +#endif diff --git a/src/xkbcomp/keycodes.c b/src/xkbcomp/keycodes.c new file mode 100644 index 0000000..b8abf36 --- /dev/null +++ b/src/xkbcomp/keycodes.c @@ -0,0 +1,671 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "include.h" + +typedef struct { + enum merge_mode merge; + + xkb_atom_t alias; + xkb_atom_t real; +} AliasInfo; + +typedef struct { + enum merge_mode merge; + + xkb_atom_t name; +} LedNameInfo; + +typedef struct { + char *name; + int errorCount; + + xkb_keycode_t min_key_code; + xkb_keycode_t max_key_code; + darray(xkb_atom_t) key_names; + LedNameInfo led_names[XKB_MAX_LEDS]; + unsigned int num_led_names; + darray(AliasInfo) aliases; + + struct xkb_context *ctx; +} KeyNamesInfo; + +/***====================================================================***/ + +static void +InitAliasInfo(AliasInfo *info, enum merge_mode merge, + xkb_atom_t alias, xkb_atom_t real) +{ + memset(info, 0, sizeof(*info)); + info->merge = merge; + info->alias = alias; + info->real = real; +} + +static LedNameInfo * +FindLedByName(KeyNamesInfo *info, xkb_atom_t name, + xkb_led_index_t *idx_out) +{ + for (xkb_led_index_t idx = 0; idx < info->num_led_names; idx++) { + LedNameInfo *ledi = &info->led_names[idx]; + if (ledi->name == name) { + *idx_out = idx; + return ledi; + } + } + + return NULL; +} + +static bool +AddLedName(KeyNamesInfo *info, enum merge_mode merge, bool same_file, + LedNameInfo *new, xkb_led_index_t new_idx) +{ + xkb_led_index_t old_idx; + LedNameInfo *old; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + const bool replace = (merge == MERGE_REPLACE || merge == MERGE_OVERRIDE); + + /* LED with the same name already exists. */ + old = FindLedByName(info, new->name, &old_idx); + if (old) { + if (old_idx == new_idx) { + log_warn(info->ctx, + "Multiple indicators named \"%s\"; " + "Identical definitions ignored\n", + xkb_atom_text(info->ctx, new->name)); + return true; + } + + if (report) { + xkb_led_index_t use = (replace ? new_idx + 1 : old_idx + 1); + xkb_led_index_t ignore = (replace ? old_idx + 1 : new_idx + 1); + log_warn(info->ctx, + "Multiple indicators named %s; Using %d, ignoring %d\n", + xkb_atom_text(info->ctx, new->name), use, ignore); + } + + if (replace) + *old = *new; + + return true; + } + + if (new_idx >= info->num_led_names) + info->num_led_names = new_idx + 1; + + /* LED with the same index already exists. */ + old = &info->led_names[new_idx]; + if (old->name != XKB_ATOM_NONE) { + if (report) { + const xkb_atom_t use = (replace ? new->name : old->name); + const xkb_atom_t ignore = (replace ? old->name : new->name); + log_warn(info->ctx, "Multiple names for indicator %d; " + "Using %s, ignoring %s\n", new_idx + 1, + xkb_atom_text(info->ctx, use), + xkb_atom_text(info->ctx, ignore)); + } + + if (replace) + *old = *new; + + return true; + } + + *old = *new; + return true; +} + +static void +ClearKeyNamesInfo(KeyNamesInfo *info) +{ + free(info->name); + darray_free(info->key_names); + darray_free(info->aliases); +} + +static void +InitKeyNamesInfo(KeyNamesInfo *info, struct xkb_context *ctx) +{ + memset(info, 0, sizeof(*info)); + info->ctx = ctx; + info->min_key_code = XKB_KEYCODE_INVALID; +#if XKB_KEYCODE_INVALID < XKB_KEYCODE_MAX +#error "Hey, you can't be changing stuff like that." +#endif +} + +static xkb_keycode_t +FindKeyByName(KeyNamesInfo *info, xkb_atom_t name) +{ + xkb_keycode_t i; + + for (i = info->min_key_code; i <= info->max_key_code; i++) + if (darray_item(info->key_names, i) == name) + return i; + + return XKB_KEYCODE_INVALID; +} + +static bool +AddKeyName(KeyNamesInfo *info, xkb_keycode_t kc, xkb_atom_t name, + enum merge_mode merge, bool same_file, bool report) +{ + xkb_atom_t old_name; + xkb_keycode_t old_kc; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + + report = report && ((same_file && verbosity > 0) || verbosity > 7); + + if (kc >= darray_size(info->key_names)) + darray_resize0(info->key_names, kc + 1); + + info->min_key_code = MIN(info->min_key_code, kc); + info->max_key_code = MAX(info->max_key_code, kc); + + /* There's already a key with this keycode. */ + old_name = darray_item(info->key_names, kc); + if (old_name != XKB_ATOM_NONE) { + const char *lname = KeyNameText(info->ctx, old_name); + const char *kname = KeyNameText(info->ctx, name); + + if (old_name == name) { + if (report) + log_warn(info->ctx, + "Multiple identical key name definitions; " + "Later occurrences of \"%s = %d\" ignored\n", + lname, kc); + return true; + } + else if (merge == MERGE_AUGMENT) { + if (report) + log_warn(info->ctx, + "Multiple names for keycode %d; " + "Using %s, ignoring %s\n", kc, lname, kname); + return true; + } + else { + if (report) + log_warn(info->ctx, + "Multiple names for keycode %d; " + "Using %s, ignoring %s\n", kc, kname, lname); + darray_item(info->key_names, kc) = XKB_ATOM_NONE; + } + } + + /* There's already a key with this name. */ + old_kc = FindKeyByName(info, name); + if (old_kc != XKB_KEYCODE_INVALID && old_kc != kc) { + const char *kname = KeyNameText(info->ctx, name); + + if (merge == MERGE_OVERRIDE) { + darray_item(info->key_names, old_kc) = XKB_ATOM_NONE; + if (report) + log_warn(info->ctx, + "Key name %s assigned to multiple keys; " + "Using %d, ignoring %d\n", kname, kc, old_kc); + } + else { + if (report) + log_vrb(info->ctx, 3, + "Key name %s assigned to multiple keys; " + "Using %d, ignoring %d\n", kname, old_kc, kc); + return true; + } + } + + darray_item(info->key_names, kc) = name; + return true; +} + +/***====================================================================***/ + +static bool +HandleAliasDef(KeyNamesInfo *info, KeyAliasDef *def, enum merge_mode merge); + +static void +MergeIncludedKeycodes(KeyNamesInfo *into, KeyNamesInfo *from, + enum merge_mode merge) +{ + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + /* Merge key names. */ + if (darray_empty(into->key_names)) { + into->key_names = from->key_names; + darray_init(from->key_names); + into->min_key_code = from->min_key_code; + into->max_key_code = from->max_key_code; + } + else { + if (darray_size(into->key_names) < darray_size(from->key_names)) + darray_resize0(into->key_names, darray_size(from->key_names)); + + for (unsigned i = from->min_key_code; i <= from->max_key_code; i++) { + xkb_atom_t name = darray_item(from->key_names, i); + if (name == XKB_ATOM_NONE) + continue; + + if (!AddKeyName(into, i, name, merge, true, false)) + into->errorCount++; + } + } + + /* Merge key aliases. */ + if (darray_empty(into->aliases)) { + into->aliases = from->aliases; + darray_init(from->aliases); + } + else { + AliasInfo *alias; + + darray_foreach(alias, from->aliases) { + KeyAliasDef def; + + def.merge = (merge == MERGE_DEFAULT ? alias->merge : merge); + def.alias = alias->alias; + def.real = alias->real; + + if (!HandleAliasDef(into, &def, def.merge)) + into->errorCount++; + } + } + + /* Merge LED names. */ + if (into->num_led_names == 0) { + memcpy(into->led_names, from->led_names, + sizeof(*from->led_names) * from->num_led_names); + into->num_led_names = from->num_led_names; + from->num_led_names = 0; + } + else { + for (xkb_led_index_t idx = 0; idx < from->num_led_names; idx++) { + LedNameInfo *ledi = &from->led_names[idx]; + + if (ledi->name == XKB_ATOM_NONE) + continue; + + ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge); + if (!AddLedName(into, ledi->merge, false, ledi, idx)) + into->errorCount++; + } + } +} + +static void +HandleKeycodesFile(KeyNamesInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeKeycodes(KeyNamesInfo *info, IncludeStmt *include) +{ + KeyNamesInfo included; + + InitKeyNamesInfo(&included, info->ctx); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + KeyNamesInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_KEYCODES); + if (!file) { + info->errorCount += 10; + ClearKeyNamesInfo(&included); + return false; + } + + InitKeyNamesInfo(&next_incl, info->ctx); + + HandleKeycodesFile(&next_incl, file, MERGE_OVERRIDE); + + MergeIncludedKeycodes(&included, &next_incl, stmt->merge); + + ClearKeyNamesInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedKeycodes(info, &included, include->merge); + ClearKeyNamesInfo(&included); + + return (info->errorCount == 0); +} + +static bool +HandleKeycodeDef(KeyNamesInfo *info, KeycodeDef *stmt, enum merge_mode merge) +{ + if (stmt->merge != MERGE_DEFAULT) { + if (stmt->merge == MERGE_REPLACE) + merge = MERGE_OVERRIDE; + else + merge = stmt->merge; + } + + if (stmt->value < 0 || stmt->value > XKB_KEYCODE_MAX) { + log_err(info->ctx, + "Illegal keycode %lld: must be between 0..%u; " + "Key ignored\n", (long long) stmt->value, XKB_KEYCODE_MAX); + return false; + } + + return AddKeyName(info, (xkb_keycode_t) stmt->value, + stmt->name, merge, false, true); +} + +static bool +HandleAliasDef(KeyNamesInfo *info, KeyAliasDef *def, enum merge_mode merge) +{ + AliasInfo *old, new; + + darray_foreach(old, info->aliases) { + if (old->alias == def->alias) { + if (def->real == old->real) { + log_vrb(info->ctx, 1, + "Alias of %s for %s declared more than once; " + "First definition ignored\n", + KeyNameText(info->ctx, def->alias), + KeyNameText(info->ctx, def->real)); + } + else { + xkb_atom_t use, ignore; + + use = (merge == MERGE_AUGMENT ? old->real : def->real); + ignore = (merge == MERGE_AUGMENT ? def->real : old->real); + + log_warn(info->ctx, + "Multiple definitions for alias %s; " + "Using %s, ignoring %s\n", + KeyNameText(info->ctx, old->alias), + KeyNameText(info->ctx, use), + KeyNameText(info->ctx, ignore)); + + old->real = use; + } + + old->merge = merge; + return true; + } + } + + InitAliasInfo(&new, merge, def->alias, def->real); + darray_append(info->aliases, new); + return true; +} + +static bool +HandleKeyNameVar(KeyNamesInfo *info, VarDef *stmt) +{ + const char *elem, *field; + ExprDef *arrayNdx; + + if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &arrayNdx)) + return false; + + if (elem) { + log_err(info->ctx, "Unknown element %s encountered; " + "Default for field %s ignored\n", elem, field); + return false; + } + + if (!istreq(field, "minimum") && !istreq(field, "maximum")) { + log_err(info->ctx, "Unknown field encountered; " + "Assignment to field %s ignored\n", field); + return false; + } + + /* We ignore explicit min/max statements, we always use computed. */ + return true; +} + +static bool +HandleLedNameDef(KeyNamesInfo *info, LedNameDef *def, + enum merge_mode merge) +{ + LedNameInfo ledi; + xkb_atom_t name; + + if (def->ndx < 1 || def->ndx > XKB_MAX_LEDS) { + info->errorCount++; + log_err(info->ctx, + "Illegal indicator index (%d) specified; must be between 1 .. %d; " + "Ignored\n", def->ndx, XKB_MAX_LEDS); + return false; + } + + if (!ExprResolveString(info->ctx, def->name, &name)) { + char buf[20]; + snprintf(buf, sizeof(buf), "%u", def->ndx); + info->errorCount++; + return ReportBadType(info->ctx, "indicator", "name", buf, "string"); + } + + ledi.merge = merge; + ledi.name = name; + return AddLedName(info, merge, true, &ledi, def->ndx - 1); +} + +static void +HandleKeycodesFile(KeyNamesInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeKeycodes(info, (IncludeStmt *) stmt); + break; + case STMT_KEYCODE: + ok = HandleKeycodeDef(info, (KeycodeDef *) stmt, merge); + break; + case STMT_ALIAS: + ok = HandleAliasDef(info, (KeyAliasDef *) stmt, merge); + break; + case STMT_VAR: + ok = HandleKeyNameVar(info, (VarDef *) stmt); + break; + case STMT_LED_NAME: + ok = HandleLedNameDef(info, (LedNameDef *) stmt, merge); + break; + default: + log_err(info->ctx, + "Keycode files may define key and indicator names only; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, "Abandoning keycodes file \"%s\"\n", + file->name); + break; + } + } +} + +/***====================================================================***/ + +static bool +CopyKeyNamesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + struct xkb_key *keys; + xkb_keycode_t min_key_code, max_key_code, kc; + + min_key_code = info->min_key_code; + max_key_code = info->max_key_code; + /* If the keymap has no keys, let's just use the safest pair we know. */ + if (min_key_code == XKB_KEYCODE_INVALID) { + min_key_code = 8; + max_key_code = 255; + } + + keys = calloc(max_key_code + 1, sizeof(*keys)); + if (!keys) + return false; + + for (kc = min_key_code; kc <= max_key_code; kc++) + keys[kc].keycode = kc; + + for (kc = info->min_key_code; kc <= info->max_key_code; kc++) + keys[kc].name = darray_item(info->key_names, kc); + + keymap->min_key_code = min_key_code; + keymap->max_key_code = max_key_code; + keymap->keys = keys; + return true; +} + +static bool +CopyKeyAliasesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + AliasInfo *alias; + unsigned i, num_key_aliases; + struct xkb_key_alias *key_aliases; + + /* + * Do some sanity checking on the aliases. We can't do it before + * because keys and their aliases may be added out-of-order. + */ + num_key_aliases = 0; + darray_foreach(alias, info->aliases) { + /* Check that ->real is a key. */ + if (!XkbKeyByName(keymap, alias->real, false)) { + log_vrb(info->ctx, 5, + "Attempt to alias %s to non-existent key %s; Ignored\n", + KeyNameText(info->ctx, alias->alias), + KeyNameText(info->ctx, alias->real)); + alias->real = XKB_ATOM_NONE; + continue; + } + + /* Check that ->alias is not a key. */ + if (XkbKeyByName(keymap, alias->alias, false)) { + log_vrb(info->ctx, 5, + "Attempt to create alias with the name of a real key; " + "Alias \"%s = %s\" ignored\n", + KeyNameText(info->ctx, alias->alias), + KeyNameText(info->ctx, alias->real)); + alias->real = XKB_ATOM_NONE; + continue; + } + + num_key_aliases++; + } + + /* Copy key aliases. */ + key_aliases = NULL; + if (num_key_aliases > 0) { + key_aliases = calloc(num_key_aliases, sizeof(*key_aliases)); + if (!key_aliases) + return false; + + i = 0; + darray_foreach(alias, info->aliases) { + if (alias->real != XKB_ATOM_NONE) { + key_aliases[i].alias = alias->alias; + key_aliases[i].real = alias->real; + i++; + } + } + } + + keymap->num_key_aliases = num_key_aliases; + keymap->key_aliases = key_aliases; + return true; +} + +static bool +CopyLedNamesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + keymap->num_leds = info->num_led_names; + for (xkb_led_index_t idx = 0; idx < info->num_led_names; idx++) { + LedNameInfo *ledi = &info->led_names[idx]; + + if (ledi->name == XKB_ATOM_NONE) + continue; + + keymap->leds[idx].name = ledi->name; + } + + return true; +} + +static bool +CopyKeyNamesInfoToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + /* This function trashes keymap on error, but that's OK. */ + if (!CopyKeyNamesToKeymap(keymap, info) || + !CopyKeyAliasesToKeymap(keymap, info) || + !CopyLedNamesToKeymap(keymap, info)) + return false; + + keymap->keycodes_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->keycodes_section_name); + return true; +} + +/***====================================================================***/ + +bool +CompileKeycodes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + KeyNamesInfo info; + + InitKeyNamesInfo(&info, keymap->ctx); + + HandleKeycodesFile(&info, file, merge); + if (info.errorCount != 0) + goto err_info; + + if (!CopyKeyNamesInfoToKeymap(keymap, &info)) + goto err_info; + + ClearKeyNamesInfo(&info); + return true; + +err_info: + ClearKeyNamesInfo(&info); + return false; +} diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c new file mode 100644 index 0000000..e6b438a --- /dev/null +++ b/src/xkbcomp/keymap-dump.c @@ -0,0 +1,666 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" + +#define BUF_CHUNK_SIZE 4096 + +struct buf { + char *buf; + size_t size; + size_t alloc; +}; + +static bool +do_realloc(struct buf *buf, size_t at_least) +{ + char *new; + + buf->alloc += BUF_CHUNK_SIZE; + if (at_least >= BUF_CHUNK_SIZE) + buf->alloc += at_least; + + new = realloc(buf->buf, buf->alloc); + if (!new) + return false; + + buf->buf = new; + return true; +} + +ATTR_PRINTF(2, 3) static bool +check_write_buf(struct buf *buf, const char *fmt, ...) +{ + va_list args; + int printed; + size_t available; + + available = buf->alloc - buf->size; + va_start(args, fmt); + printed = vsnprintf(buf->buf + buf->size, available, fmt, args); + va_end(args); + + if (printed < 0) + goto err; + + if ((size_t) printed >= available) + if (!do_realloc(buf, printed)) + goto err; + + /* The buffer has enough space now. */ + + available = buf->alloc - buf->size; + va_start(args, fmt); + printed = vsnprintf(buf->buf + buf->size, available, fmt, args); + va_end(args); + + if (printed < 0 || (size_t) printed >= available) + goto err; + + buf->size += printed; + return true; + +err: + free(buf->buf); + buf->buf = NULL; + return false; +} + +#define write_buf(buf, ...) do { \ + if (!check_write_buf(buf, __VA_ARGS__)) \ + return false; \ +} while (0) + +static bool +write_vmods(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_mod *mod; + xkb_mod_index_t num_vmods = 0; + + xkb_mods_foreach(mod, &keymap->mods) { + if (mod->type != MOD_VIRT) + continue; + + if (num_vmods == 0) + write_buf(buf, "\tvirtual_modifiers "); + else + write_buf(buf, ","); + write_buf(buf, "%s", xkb_atom_text(keymap->ctx, mod->name)); + num_vmods++; + } + + if (num_vmods > 0) + write_buf(buf, ";\n\n"); + + return true; +} + +static bool +write_keycodes(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_key *key; + xkb_led_index_t idx; + const struct xkb_led *led; + + if (keymap->keycodes_section_name) + write_buf(buf, "xkb_keycodes \"%s\" {\n", + keymap->keycodes_section_name); + else + write_buf(buf, "xkb_keycodes {\n"); + + /* xkbcomp and X11 really want to see keymaps with a minimum of 8, and + * a maximum of at least 255, else XWayland really starts hating life. + * If this is a problem and people really need strictly bounded keymaps, + * we should probably control this with a flag. */ + write_buf(buf, "\tminimum = %u;\n", MIN(keymap->min_key_code, 8)); + write_buf(buf, "\tmaximum = %u;\n", MAX(keymap->max_key_code, 255)); + + xkb_keys_foreach(key, keymap) { + if (key->name == XKB_ATOM_NONE) + continue; + + write_buf(buf, "\t%-20s = %u;\n", + KeyNameText(keymap->ctx, key->name), key->keycode); + } + + xkb_leds_enumerate(idx, led, keymap) + if (led->name != XKB_ATOM_NONE) + write_buf(buf, "\tindicator %u = \"%s\";\n", + idx + 1, xkb_atom_text(keymap->ctx, led->name)); + + + for (unsigned i = 0; i < keymap->num_key_aliases; i++) + write_buf(buf, "\talias %-14s = %s;\n", + KeyNameText(keymap->ctx, keymap->key_aliases[i].alias), + KeyNameText(keymap->ctx, keymap->key_aliases[i].real)); + + write_buf(buf, "};\n\n"); + return true; +} + +static bool +write_types(struct xkb_keymap *keymap, struct buf *buf) +{ + if (keymap->types_section_name) + write_buf(buf, "xkb_types \"%s\" {\n", + keymap->types_section_name); + else + write_buf(buf, "xkb_types {\n"); + + write_vmods(keymap, buf); + + for (unsigned i = 0; i < keymap->num_types; i++) { + const struct xkb_key_type *type = &keymap->types[i]; + + write_buf(buf, "\ttype \"%s\" {\n", + xkb_atom_text(keymap->ctx, type->name)); + + write_buf(buf, "\t\tmodifiers= %s;\n", + ModMaskText(keymap->ctx, &keymap->mods, type->mods.mods)); + + for (unsigned j = 0; j < type->num_entries; j++) { + const char *str; + const struct xkb_key_type_entry *entry = &type->entries[j]; + + /* + * Printing level 1 entries is redundant, it's the default, + * unless there's preserve info. + */ + if (entry->level == 0 && entry->preserve.mods == 0) + continue; + + str = ModMaskText(keymap->ctx, &keymap->mods, entry->mods.mods); + write_buf(buf, "\t\tmap[%s]= %u;\n", + str, entry->level + 1); + + if (entry->preserve.mods) + write_buf(buf, "\t\tpreserve[%s]= %s;\n", + str, ModMaskText(keymap->ctx, &keymap->mods, + entry->preserve.mods)); + } + + for (xkb_level_index_t n = 0; n < type->num_level_names; n++) + if (type->level_names[n]) + write_buf(buf, "\t\tlevel_name[%u]= \"%s\";\n", n + 1, + xkb_atom_text(keymap->ctx, type->level_names[n])); + + write_buf(buf, "\t};\n"); + } + + write_buf(buf, "};\n\n"); + return true; +} + +static bool +write_led_map(struct xkb_keymap *keymap, struct buf *buf, + const struct xkb_led *led) +{ + write_buf(buf, "\tindicator \"%s\" {\n", + xkb_atom_text(keymap->ctx, led->name)); + + if (led->which_groups) { + if (led->which_groups != XKB_STATE_LAYOUT_EFFECTIVE) { + write_buf(buf, "\t\twhichGroupState= %s;\n", + LedStateMaskText(keymap->ctx, led->which_groups)); + } + write_buf(buf, "\t\tgroups= 0x%02x;\n", + led->groups); + } + + if (led->which_mods) { + if (led->which_mods != XKB_STATE_MODS_EFFECTIVE) { + write_buf(buf, "\t\twhichModState= %s;\n", + LedStateMaskText(keymap->ctx, led->which_mods)); + } + write_buf(buf, "\t\tmodifiers= %s;\n", + ModMaskText(keymap->ctx, &keymap->mods, led->mods.mods)); + } + + if (led->ctrls) { + write_buf(buf, "\t\tcontrols= %s;\n", + ControlMaskText(keymap->ctx, led->ctrls)); + } + + write_buf(buf, "\t};\n"); + return true; +} + +static const char * +affect_lock_text(enum xkb_action_flags flags) +{ + switch (flags & (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK)) { + case ACTION_LOCK_NO_UNLOCK: + return ",affect=lock"; + case ACTION_LOCK_NO_LOCK: + return ",affect=unlock"; + case ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK: + return ",affect=neither"; + } + return ""; +} + +static bool +write_action(struct xkb_keymap *keymap, struct buf *buf, + const union xkb_action *action, + const char *prefix, const char *suffix) +{ + const char *type; + const char *args = NULL; + + if (!prefix) + prefix = ""; + if (!suffix) + suffix = ""; + + type = ActionTypeText(action->type); + + switch (action->type) { + case ACTION_TYPE_MOD_SET: + case ACTION_TYPE_MOD_LATCH: + case ACTION_TYPE_MOD_LOCK: + if (action->mods.flags & ACTION_MODS_LOOKUP_MODMAP) + args = "modMapMods"; + else + args = ModMaskText(keymap->ctx, &keymap->mods, + action->mods.mods.mods); + write_buf(buf, "%s%s(modifiers=%s%s%s%s)%s", prefix, type, args, + (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", + (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", + (action->type == ACTION_TYPE_MOD_LOCK) ? affect_lock_text(action->mods.flags) : "", + suffix); + break; + + case ACTION_TYPE_GROUP_SET: + case ACTION_TYPE_GROUP_LATCH: + case ACTION_TYPE_GROUP_LOCK: + write_buf(buf, "%s%s(group=%s%d%s%s)%s", prefix, type, + (!(action->group.flags & ACTION_ABSOLUTE_SWITCH) && action->group.group > 0) ? "+" : "", + (action->group.flags & ACTION_ABSOLUTE_SWITCH) ? action->group.group + 1 : action->group.group, + (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", + (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", + suffix); + break; + + case ACTION_TYPE_TERMINATE: + write_buf(buf, "%s%s()%s", prefix, type, suffix); + break; + + case ACTION_TYPE_PTR_MOVE: + write_buf(buf, "%s%s(x=%s%d,y=%s%d%s)%s", prefix, type, + (!(action->ptr.flags & ACTION_ABSOLUTE_X) && action->ptr.x >= 0) ? "+" : "", + action->ptr.x, + (!(action->ptr.flags & ACTION_ABSOLUTE_Y) && action->ptr.y >= 0) ? "+" : "", + action->ptr.y, + (action->ptr.flags & ACTION_ACCEL) ? "" : ",!accel", + suffix); + break; + + case ACTION_TYPE_PTR_LOCK: + args = affect_lock_text(action->btn.flags); + /* fallthrough */ + case ACTION_TYPE_PTR_BUTTON: + write_buf(buf, "%s%s(button=", prefix, type); + if (action->btn.button > 0 && action->btn.button <= 5) + write_buf(buf, "%d", action->btn.button); + else + write_buf(buf, "default"); + if (action->btn.count) + write_buf(buf, ",count=%d", action->btn.count); + if (args) + write_buf(buf, "%s", args); + write_buf(buf, ")%s", suffix); + break; + + case ACTION_TYPE_PTR_DEFAULT: + write_buf(buf, "%s%s(", prefix, type); + write_buf(buf, "affect=button,button=%s%d", + (!(action->dflt.flags & ACTION_ABSOLUTE_SWITCH) && action->dflt.value >= 0) ? "+" : "", + action->dflt.value); + write_buf(buf, ")%s", suffix); + break; + + case ACTION_TYPE_SWITCH_VT: + write_buf(buf, "%s%s(screen=%s%d,%ssame)%s", prefix, type, + (!(action->screen.flags & ACTION_ABSOLUTE_SWITCH) && action->screen.screen >= 0) ? "+" : "", + action->screen.screen, + (action->screen.flags & ACTION_SAME_SCREEN) ? "" : "!", + suffix); + break; + + case ACTION_TYPE_CTRL_SET: + case ACTION_TYPE_CTRL_LOCK: + write_buf(buf, "%s%s(controls=%s%s)%s", prefix, type, + ControlMaskText(keymap->ctx, action->ctrls.ctrls), + (action->type == ACTION_TYPE_CTRL_LOCK) ? affect_lock_text(action->ctrls.flags) : "", + suffix); + break; + + case ACTION_TYPE_NONE: + write_buf(buf, "%sNoAction()%s", prefix, suffix); + break; + + default: + write_buf(buf, + "%s%s(type=0x%02x,data[0]=0x%02x,data[1]=0x%02x,data[2]=0x%02x,data[3]=0x%02x,data[4]=0x%02x,data[5]=0x%02x,data[6]=0x%02x)%s", + prefix, type, action->type, action->priv.data[0], + action->priv.data[1], action->priv.data[2], + action->priv.data[3], action->priv.data[4], + action->priv.data[5], action->priv.data[6], + suffix); + break; + } + + return true; +} + +static bool +write_compat(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_led *led; + + if (keymap->compat_section_name) + write_buf(buf, "xkb_compatibility \"%s\" {\n", + keymap->compat_section_name); + else + write_buf(buf, "xkb_compatibility {\n"); + + write_vmods(keymap, buf); + + write_buf(buf, "\tinterpret.useModMapMods= AnyLevel;\n"); + write_buf(buf, "\tinterpret.repeat= False;\n"); + + for (unsigned i = 0; i < keymap->num_sym_interprets; i++) { + const struct xkb_sym_interpret *si = &keymap->sym_interprets[i]; + + write_buf(buf, "\tinterpret %s+%s(%s) {\n", + si->sym ? KeysymText(keymap->ctx, si->sym) : "Any", + SIMatchText(si->match), + ModMaskText(keymap->ctx, &keymap->mods, si->mods)); + + if (si->virtual_mod != XKB_MOD_INVALID) + write_buf(buf, "\t\tvirtualModifier= %s;\n", + ModIndexText(keymap->ctx, &keymap->mods, + si->virtual_mod)); + + if (si->level_one_only) + write_buf(buf, "\t\tuseModMapMods=level1;\n"); + + if (si->repeat) + write_buf(buf, "\t\trepeat= True;\n"); + + write_action(keymap, buf, &si->action, "\t\taction= ", ";\n"); + write_buf(buf, "\t};\n"); + } + + xkb_leds_foreach(led, keymap) + if (led->which_groups || led->groups || led->which_mods || + led->mods.mods || led->ctrls) + write_led_map(keymap, buf, led); + + write_buf(buf, "};\n\n"); + + return true; +} + +static bool +write_keysyms(struct xkb_keymap *keymap, struct buf *buf, + const struct xkb_key *key, xkb_layout_index_t group) +{ + for (xkb_level_index_t level = 0; level < XkbKeyNumLevels(key, group); + level++) { + const xkb_keysym_t *syms; + int num_syms; + + if (level != 0) + write_buf(buf, ", "); + + num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, + group, level, &syms); + if (num_syms == 0) { + write_buf(buf, "%15s", "NoSymbol"); + } + else if (num_syms == 1) { + write_buf(buf, "%15s", KeysymText(keymap->ctx, syms[0])); + } + else { + write_buf(buf, "{ "); + for (int s = 0; s < num_syms; s++) { + if (s != 0) + write_buf(buf, ", "); + write_buf(buf, "%s", KeysymText(keymap->ctx, syms[s])); + } + write_buf(buf, " }"); + } + } + + return true; +} + +static bool +write_key(struct xkb_keymap *keymap, struct buf *buf, + const struct xkb_key *key) +{ + xkb_layout_index_t group; + bool simple = true; + bool explicit_types = false; + bool multi_type = false; + bool show_actions; + + write_buf(buf, "\tkey %-20s {", KeyNameText(keymap->ctx, key->name)); + + for (group = 0; group < key->num_groups; group++) { + if (key->groups[group].explicit_type) + explicit_types = true; + + if (group != 0 && key->groups[group].type != key->groups[0].type) + multi_type = true; + } + + if (explicit_types) { + const struct xkb_key_type *type; + simple = false; + + if (multi_type) { + for (group = 0; group < key->num_groups; group++) { + if (!key->groups[group].explicit_type) + continue; + + type = key->groups[group].type; + write_buf(buf, "\n\t\ttype[Group%u]= \"%s\",", + group + 1, + xkb_atom_text(keymap->ctx, type->name)); + } + } + else { + type = key->groups[0].type; + write_buf(buf, "\n\t\ttype= \"%s\",", + xkb_atom_text(keymap->ctx, type->name)); + } + } + + if (key->explicit & EXPLICIT_REPEAT) { + if (key->repeats) + write_buf(buf, "\n\t\trepeat= Yes,"); + else + write_buf(buf, "\n\t\trepeat= No,"); + simple = false; + } + + if (key->vmodmap && (key->explicit & EXPLICIT_VMODMAP)) + write_buf(buf, "\n\t\tvirtualMods= %s,", + ModMaskText(keymap->ctx, &keymap->mods, key->vmodmap)); + + switch (key->out_of_range_group_action) { + case RANGE_SATURATE: + write_buf(buf, "\n\t\tgroupsClamp,"); + break; + + case RANGE_REDIRECT: + write_buf(buf, "\n\t\tgroupsRedirect= Group%u,", + key->out_of_range_group_number + 1); + break; + + default: + break; + } + + show_actions = (key->explicit & EXPLICIT_INTERP); + + if (key->num_groups > 1 || show_actions) + simple = false; + + if (simple) { + write_buf(buf, "\t[ "); + if (!write_keysyms(keymap, buf, key, 0)) + return false; + write_buf(buf, " ] };\n"); + } + else { + xkb_level_index_t level; + + for (group = 0; group < key->num_groups; group++) { + if (group != 0) + write_buf(buf, ","); + write_buf(buf, "\n\t\tsymbols[Group%u]= [ ", group + 1); + if (!write_keysyms(keymap, buf, key, group)) + return false; + write_buf(buf, " ]"); + if (show_actions) { + write_buf(buf, ",\n\t\tactions[Group%u]= [ ", group + 1); + for (level = 0; level < XkbKeyNumLevels(key, group); level++) { + if (level != 0) + write_buf(buf, ", "); + write_action(keymap, buf, + &key->groups[group].levels[level].action, + NULL, NULL); + } + write_buf(buf, " ]"); + } + } + write_buf(buf, "\n\t};\n"); + } + + return true; +} + +static bool +write_symbols(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_key *key; + xkb_layout_index_t group; + xkb_mod_index_t i; + const struct xkb_mod *mod; + + if (keymap->symbols_section_name) + write_buf(buf, "xkb_symbols \"%s\" {\n", + keymap->symbols_section_name); + else + write_buf(buf, "xkb_symbols {\n"); + + for (group = 0; group < keymap->num_group_names; group++) + if (keymap->group_names[group]) + write_buf(buf, + "\tname[Group%u]=\"%s\";\n", group + 1, + xkb_atom_text(keymap->ctx, keymap->group_names[group])); + if (group > 0) + write_buf(buf, "\n"); + + xkb_keys_foreach(key, keymap) + if (key->num_groups > 0) + write_key(keymap, buf, key); + + xkb_mods_enumerate(i, mod, &keymap->mods) { + bool had_any = false; + xkb_keys_foreach(key, keymap) { + if (key->modmap & (1u << i)) { + if (!had_any) + write_buf(buf, "\tmodifier_map %s { ", + xkb_atom_text(keymap->ctx, mod->name)); + write_buf(buf, "%s%s", + had_any ? ", " : "", + KeyNameText(keymap->ctx, key->name)); + had_any = true; + } + } + if (had_any) + write_buf(buf, " };\n"); + } + + write_buf(buf, "};\n\n"); + return true; +} + +static bool +write_keymap(struct xkb_keymap *keymap, struct buf *buf) +{ + return (check_write_buf(buf, "xkb_keymap {\n") && + write_keycodes(keymap, buf) && + write_types(keymap, buf) && + write_compat(keymap, buf) && + write_symbols(keymap, buf) && + check_write_buf(buf, "};\n")); +} + +char * +text_v1_keymap_get_as_string(struct xkb_keymap *keymap) +{ + struct buf buf = { NULL, 0, 0 }; + + if (!write_keymap(keymap, &buf)) { + free(buf.buf); + return NULL; + } + + return buf.buf; +} diff --git a/src/xkbcomp/keymap.c b/src/xkbcomp/keymap.c new file mode 100644 index 0000000..0d14913 --- /dev/null +++ b/src/xkbcomp/keymap.c @@ -0,0 +1,300 @@ +/* + * Copyright © 2009 Dan Nicholson + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Dan Nicholson <dbn.lists@gmail.com> + * Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" + +static void +ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods) +{ + mods->mask = mod_mask_get_effective(keymap, mods->mods); +} + +static void +UpdateActionMods(struct xkb_keymap *keymap, union xkb_action *act, + xkb_mod_mask_t modmap) +{ + switch (act->type) { + case ACTION_TYPE_MOD_SET: + case ACTION_TYPE_MOD_LATCH: + case ACTION_TYPE_MOD_LOCK: + if (act->mods.flags & ACTION_MODS_LOOKUP_MODMAP) + act->mods.mods.mods = modmap; + ComputeEffectiveMask(keymap, &act->mods.mods); + break; + default: + break; + } +} + +static const struct xkb_sym_interpret default_interpret = { + .sym = XKB_KEY_NoSymbol, + .repeat = true, + .match = MATCH_ANY_OR_NONE, + .mods = 0, + .virtual_mod = XKB_MOD_INVALID, + .action = { .type = ACTION_TYPE_NONE }, +}; + +/** + * Find an interpretation which applies to this particular level, either by + * finding an exact match for the symbol and modifier combination, or a + * generic XKB_KEY_NoSymbol match. + */ +static const struct xkb_sym_interpret * +FindInterpForKey(struct xkb_keymap *keymap, const struct xkb_key *key, + xkb_layout_index_t group, xkb_level_index_t level) +{ + const xkb_keysym_t *syms; + int num_syms; + + num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, group, + level, &syms); + if (num_syms == 0) + return NULL; + + /* + * There may be multiple matchings interprets; we should always return + * the most specific. Here we rely on compat.c to set up the + * sym_interprets array from the most specific to the least specific, + * such that when we find a match we return immediately. + */ + for (unsigned i = 0; i < keymap->num_sym_interprets; i++) { + const struct xkb_sym_interpret *interp = &keymap->sym_interprets[i]; + + xkb_mod_mask_t mods; + bool found = false; + + if ((num_syms > 1 || interp->sym != syms[0]) && + interp->sym != XKB_KEY_NoSymbol) + continue; + + if (interp->level_one_only && level != 0) + mods = 0; + else + mods = key->modmap; + + switch (interp->match) { + case MATCH_NONE: + found = !(interp->mods & mods); + break; + case MATCH_ANY_OR_NONE: + found = (!mods || (interp->mods & mods)); + break; + case MATCH_ANY: + found = (interp->mods & mods); + break; + case MATCH_ALL: + found = ((interp->mods & mods) == interp->mods); + break; + case MATCH_EXACTLY: + found = (interp->mods == mods); + break; + } + + if (found) + return interp; + } + + return &default_interpret; +} + +static bool +ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key) +{ + xkb_mod_mask_t vmodmap = 0; + xkb_layout_index_t group; + xkb_level_index_t level; + + /* If we've been told not to bind interps to this key, then don't. */ + if (key->explicit & EXPLICIT_INTERP) + return true; + + for (group = 0; group < key->num_groups; group++) { + for (level = 0; level < XkbKeyNumLevels(key, group); level++) { + const struct xkb_sym_interpret *interp; + + interp = FindInterpForKey(keymap, key, group, level); + if (!interp) + continue; + + /* Infer default key behaviours from the base level. */ + if (group == 0 && level == 0) + if (!(key->explicit & EXPLICIT_REPEAT) && interp->repeat) + key->repeats = true; + + if ((group == 0 && level == 0) || !interp->level_one_only) + if (interp->virtual_mod != XKB_MOD_INVALID) + vmodmap |= (1u << interp->virtual_mod); + + if (interp->action.type != ACTION_TYPE_NONE) + key->groups[group].levels[level].action = interp->action; + } + } + + if (!(key->explicit & EXPLICIT_VMODMAP)) + key->vmodmap = vmodmap; + + return true; +} + +/** + * This collects a bunch of disparate functions which was done in the server + * at various points that really should've been done within xkbcomp. Turns out + * your actions and types are a lot more useful when any of your modifiers + * other than Shift actually do something ... + */ +static bool +UpdateDerivedKeymapFields(struct xkb_keymap *keymap) +{ + struct xkb_key *key; + struct xkb_mod *mod; + struct xkb_led *led; + unsigned int i, j; + + /* Find all the interprets for the key and bind them to actions, + * which will also update the vmodmap. */ + xkb_keys_foreach(key, keymap) + if (!ApplyInterpsToKey(keymap, key)) + return false; + + /* Update keymap->mods, the virtual -> real mod mapping. */ + xkb_keys_foreach(key, keymap) + xkb_mods_enumerate(i, mod, &keymap->mods) + if (key->vmodmap & (1u << i)) + mod->mapping |= key->modmap; + + /* Now update the level masks for all the types to reflect the vmods. */ + for (i = 0; i < keymap->num_types; i++) { + ComputeEffectiveMask(keymap, &keymap->types[i].mods); + + for (j = 0; j < keymap->types[i].num_entries; j++) { + ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].mods); + ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].preserve); + } + } + + /* Update action modifiers. */ + xkb_keys_foreach(key, keymap) + for (i = 0; i < key->num_groups; i++) + for (j = 0; j < XkbKeyNumLevels(key, i); j++) + UpdateActionMods(keymap, &key->groups[i].levels[j].action, + key->modmap); + + /* Update vmod -> led maps. */ + xkb_leds_foreach(led, keymap) + ComputeEffectiveMask(keymap, &led->mods); + + /* Find maximum number of groups out of all keys in the keymap. */ + xkb_keys_foreach(key, keymap) + keymap->num_groups = MAX(keymap->num_groups, key->num_groups); + + return true; +} + +typedef bool (*compile_file_fn)(XkbFile *file, + struct xkb_keymap *keymap, + enum merge_mode merge); + +static const compile_file_fn compile_file_fns[LAST_KEYMAP_FILE_TYPE + 1] = { + [FILE_TYPE_KEYCODES] = CompileKeycodes, + [FILE_TYPE_TYPES] = CompileKeyTypes, + [FILE_TYPE_COMPAT] = CompileCompatMap, + [FILE_TYPE_SYMBOLS] = CompileSymbols, +}; + +bool +CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, enum merge_mode merge) +{ + bool ok; + XkbFile *files[LAST_KEYMAP_FILE_TYPE + 1] = { NULL }; + enum xkb_file_type type; + struct xkb_context *ctx = keymap->ctx; + + /* Collect section files and check for duplicates. */ + for (file = (XkbFile *) file->defs; file; + file = (XkbFile *) file->common.next) { + if (file->file_type < FIRST_KEYMAP_FILE_TYPE || + file->file_type > LAST_KEYMAP_FILE_TYPE) { + if (file->file_type == FILE_TYPE_GEOMETRY) { + log_vrb(ctx, 1, + "Geometry sections are not supported; ignoring\n"); + } else { + log_err(ctx, "Cannot define %s in a keymap file\n", + xkb_file_type_to_string(file->file_type)); + } + continue; + } + + if (files[file->file_type]) { + log_err(ctx, + "More than one %s section in keymap file; " + "All sections after the first ignored\n", + xkb_file_type_to_string(file->file_type)); + continue; + } + + files[file->file_type] = file; + } + + /* + * Check that all required section were provided. + * Report everything before failing. + */ + ok = true; + for (type = FIRST_KEYMAP_FILE_TYPE; + type <= LAST_KEYMAP_FILE_TYPE; + type++) { + if (files[type] == NULL) { + log_err(ctx, "Required section %s missing from keymap\n", + xkb_file_type_to_string(type)); + ok = false; + } + } + if (!ok) + return false; + + /* Compile sections. */ + for (type = FIRST_KEYMAP_FILE_TYPE; + type <= LAST_KEYMAP_FILE_TYPE; + type++) { + log_dbg(ctx, "Compiling %s \"%s\"\n", + xkb_file_type_to_string(type), files[type]->name); + + ok = compile_file_fns[type](files[type], keymap, merge); + if (!ok) { + log_err(ctx, "Failed to compile %s\n", + xkb_file_type_to_string(type)); + return false; + } + } + + return UpdateDerivedKeymapFields(keymap); +} diff --git a/src/xkbcomp/keywords.c b/src/xkbcomp/keywords.c new file mode 100644 index 0000000..c8a48f1 --- /dev/null +++ b/src/xkbcomp/keywords.c @@ -0,0 +1,389 @@ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf src/xkbcomp/keywords.gperf */ +/* Computed positions: -k'1-2,5' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." +#endif + +#line 1 "src/xkbcomp/keywords.gperf" + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "parser-priv.h" + +static const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register size_t len); +#line 11 "src/xkbcomp/keywords.gperf" +struct keyword_tok { int name; int tok; }; +#include <string.h> +/* maximum key range = 70, duplicates = 0 */ + +#ifndef GPERF_DOWNCASE +#define GPERF_DOWNCASE 1 +static unsigned char gperf_downcase[256] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 + }; +#endif + +#ifndef GPERF_CASE_STRCMP +#define GPERF_CASE_STRCMP 1 +static int +gperf_case_strcmp (register const char *s1, register const char *s2) +{ + for (;;) + { + unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; + unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; + if (c1 != 0 && c1 == c2) + continue; + return (int)c1 - (int)c2; + } +} +#endif + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +keyword_gperf_hash (register const char *str, register size_t len) +{ + static const unsigned char asso_values[] = + { + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 0, 73, 5, 36, 0, + 10, 1, 15, 15, 73, 0, 10, 20, 35, 20, + 50, 73, 10, 10, 5, 0, 15, 73, 0, 15, + 73, 73, 73, 73, 73, 73, 73, 0, 73, 5, + 36, 0, 10, 1, 15, 15, 73, 0, 10, 20, + 35, 20, 50, 73, 10, 10, 5, 0, 15, 73, + 0, 15, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73 + }; + register unsigned int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + case 3: + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +struct stringpool_t + { + char stringpool_str3[sizeof("key")]; + char stringpool_str4[sizeof("keys")]; + char stringpool_str7[sizeof("augment")]; + char stringpool_str9[sizeof("text")]; + char stringpool_str10[sizeof("xkb_keymap")]; + char stringpool_str11[sizeof("keypad_keys")]; + char stringpool_str12[sizeof("xkb_keycodes")]; + char stringpool_str13[sizeof("xkb_geometry")]; + char stringpool_str14[sizeof("xkb_types")]; + char stringpool_str15[sizeof("xkb_compat")]; + char stringpool_str17[sizeof("replace")]; + char stringpool_str19[sizeof("xkb_compat_map")]; + char stringpool_str20[sizeof("xkb_layout")]; + char stringpool_str21[sizeof("xkb_symbols")]; + char stringpool_str22[sizeof("xkb_compatibility")]; + char stringpool_str23[sizeof("xkb_semantics")]; + char stringpool_str24[sizeof("type")]; + char stringpool_str25[sizeof("alias")]; + char stringpool_str26[sizeof("xkb_compatibility_map")]; + char stringpool_str27[sizeof("alphanumeric_keys")]; + char stringpool_str28[sizeof("function_keys")]; + char stringpool_str29[sizeof("alternate")]; + char stringpool_str30[sizeof("shape")]; + char stringpool_str31[sizeof("action")]; + char stringpool_str32[sizeof("section")]; + char stringpool_str33[sizeof("row")]; + char stringpool_str34[sizeof("logo")]; + char stringpool_str35[sizeof("alternate_group")]; + char stringpool_str36[sizeof("hidden")]; + char stringpool_str37[sizeof("virtual")]; + char stringpool_str42[sizeof("outline")]; + char stringpool_str43[sizeof("default")]; + char stringpool_str46[sizeof("modmap")]; + char stringpool_str47[sizeof("virtual_modifiers")]; + char stringpool_str52[sizeof("overlay")]; + char stringpool_str53[sizeof("override")]; + char stringpool_str57[sizeof("include")]; + char stringpool_str62[sizeof("modifier_map")]; + char stringpool_str63[sizeof("modifier_keys")]; + char stringpool_str64[sizeof("indicator")]; + char stringpool_str66[sizeof("group")]; + char stringpool_str67[sizeof("mod_map")]; + char stringpool_str69[sizeof("interpret")]; + char stringpool_str71[sizeof("solid")]; + char stringpool_str72[sizeof("partial")]; + }; +static const struct stringpool_t stringpool_contents = + { + "key", + "keys", + "augment", + "text", + "xkb_keymap", + "keypad_keys", + "xkb_keycodes", + "xkb_geometry", + "xkb_types", + "xkb_compat", + "replace", + "xkb_compat_map", + "xkb_layout", + "xkb_symbols", + "xkb_compatibility", + "xkb_semantics", + "type", + "alias", + "xkb_compatibility_map", + "alphanumeric_keys", + "function_keys", + "alternate", + "shape", + "action", + "section", + "row", + "logo", + "alternate_group", + "hidden", + "virtual", + "outline", + "default", + "modmap", + "virtual_modifiers", + "overlay", + "override", + "include", + "modifier_map", + "modifier_keys", + "indicator", + "group", + "mod_map", + "interpret", + "solid", + "partial" + }; +#define stringpool ((const char *) &stringpool_contents) +const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register size_t len) +{ + enum + { + TOTAL_KEYWORDS = 45, + MIN_WORD_LENGTH = 3, + MAX_WORD_LENGTH = 21, + MIN_HASH_VALUE = 3, + MAX_HASH_VALUE = 72 + }; + + static const struct keyword_tok wordlist[] = + { + {-1}, {-1}, {-1}, +#line 37 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3, KEY}, +#line 38 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str4, KEYS}, + {-1}, {-1}, +#line 28 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str7, AUGMENT}, + {-1}, +#line 53 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str9, TEXT}, +#line 63 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str10, XKB_KEYMAP}, +#line 36 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str11, KEYPAD_KEYS}, +#line 62 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, XKB_KEYCODES}, +#line 61 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, XKB_GEOMETRY}, +#line 67 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, XKB_TYPES}, +#line 60 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, XKB_COMPATMAP}, + {-1}, +#line 48 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, REPLACE}, + {-1}, +#line 59 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, XKB_COMPATMAP}, +#line 64 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, XKB_LAYOUT}, +#line 66 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, XKB_SYMBOLS}, +#line 58 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, XKB_COMPATMAP}, +#line 65 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, XKB_SEMANTICS}, +#line 54 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, TYPE}, +#line 24 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, ALIAS}, +#line 57 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, XKB_COMPATMAP}, +#line 25 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str27, ALPHANUMERIC_KEYS}, +#line 30 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, FUNCTION_KEYS}, +#line 27 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, ALTERNATE}, +#line 51 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, SHAPE}, +#line 23 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, ACTION_TOK}, +#line 50 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, SECTION}, +#line 49 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, ROW}, +#line 39 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, LOGO}, +#line 26 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, ALTERNATE_GROUP}, +#line 32 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, HIDDEN}, +#line 56 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VIRTUAL}, + {-1}, {-1}, {-1}, {-1}, +#line 44 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, OUTLINE}, +#line 29 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, DEFAULT}, + {-1}, {-1}, +#line 43 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, MODIFIER_MAP}, +#line 55 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VIRTUAL_MODS}, + {-1}, {-1}, {-1}, {-1}, +#line 45 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, OVERLAY}, +#line 46 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, OVERRIDE}, + {-1}, {-1}, {-1}, +#line 33 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, INCLUDE}, + {-1}, {-1}, {-1}, {-1}, +#line 41 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, MODIFIER_MAP}, +#line 40 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, MODIFIER_KEYS}, +#line 34 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, INDICATOR}, + {-1}, +#line 31 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, GROUP}, +#line 42 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, MODIFIER_MAP}, + {-1}, +#line 35 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str69, INTERPRET}, + {-1}, +#line 52 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, SOLID}, +#line 47 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, PARTIAL} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register unsigned int key = keyword_gperf_hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + register int o = wordlist[key].name; + if (o >= 0) + { + register const char *s = o + stringpool; + + if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strcmp (str, s)) + return &wordlist[key]; + } + } + } + return 0; +} +#line 68 "src/xkbcomp/keywords.gperf" + + +int +keyword_to_token(const char *string, size_t len) +{ + const struct keyword_tok *kt = keyword_gperf_lookup(string, len); + if (!kt) + return -1; + return kt->tok; +} diff --git a/src/xkbcomp/keywords.gperf b/src/xkbcomp/keywords.gperf new file mode 100644 index 0000000..dc6a13c --- /dev/null +++ b/src/xkbcomp/keywords.gperf @@ -0,0 +1,77 @@ +%{ +#include "config.h" + +#include "xkbcomp-priv.h" +#include "parser-priv.h" + +static const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register size_t len); +%} + +struct keyword_tok { int name; int tok; }; +%language=ANSI-C +%define hash-function-name keyword_gperf_hash +%define lookup-function-name keyword_gperf_lookup +%readonly-tables +%enum +%includes +%struct-type +%pic +%ignore-case + +%% +action, ACTION_TOK +alias, ALIAS +alphanumeric_keys, ALPHANUMERIC_KEYS +alternate_group, ALTERNATE_GROUP +alternate, ALTERNATE +augment, AUGMENT +default, DEFAULT +function_keys, FUNCTION_KEYS +group, GROUP +hidden, HIDDEN +include, INCLUDE +indicator, INDICATOR +interpret, INTERPRET +keypad_keys, KEYPAD_KEYS +key, KEY +keys, KEYS +logo, LOGO +modifier_keys, MODIFIER_KEYS +modifier_map, MODIFIER_MAP +mod_map, MODIFIER_MAP +modmap, MODIFIER_MAP +outline, OUTLINE +overlay, OVERLAY +override, OVERRIDE +partial, PARTIAL +replace, REPLACE +row, ROW +section, SECTION +shape, SHAPE +solid, SOLID +text, TEXT +type, TYPE +virtual_modifiers, VIRTUAL_MODS +virtual, VIRTUAL +xkb_compatibility_map, XKB_COMPATMAP +xkb_compatibility, XKB_COMPATMAP +xkb_compat_map, XKB_COMPATMAP +xkb_compat, XKB_COMPATMAP +xkb_geometry, XKB_GEOMETRY +xkb_keycodes, XKB_KEYCODES +xkb_keymap, XKB_KEYMAP +xkb_layout, XKB_LAYOUT +xkb_semantics, XKB_SEMANTICS +xkb_symbols, XKB_SYMBOLS +xkb_types, XKB_TYPES +%% + +int +keyword_to_token(const char *string, size_t len) +{ + const struct keyword_tok *kt = keyword_gperf_lookup(string, len); + if (!kt) + return -1; + return kt->tok; +} diff --git a/src/xkbcomp/parser-priv.h b/src/xkbcomp/parser-priv.h new file mode 100644 index 0000000..f10351a --- /dev/null +++ b/src/xkbcomp/parser-priv.h @@ -0,0 +1,44 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_PARSER_PRIV_H +#define XKBCOMP_PARSER_PRIV_H + +struct parser_param; +struct scanner; + +#include "parser.h" + +int +_xkbcommon_lex(YYSTYPE *yylval, struct scanner *scanner); + +XkbFile * +parse(struct xkb_context *ctx, struct scanner *scanner, const char *map); + +int +keyword_to_token(const char *string, size_t len); + +#endif diff --git a/src/xkbcomp/parser.c b/src/xkbcomp/parser.c new file mode 100644 index 0000000..26d7a57 --- /dev/null +++ b/src/xkbcomp/parser.c @@ -0,0 +1,3403 @@ +/* A Bison parser, made by GNU Bison 3.6.3. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.6.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse _xkbcommon_parse +#define yylex _xkbcommon_lex +#define yyerror _xkbcommon_error +#define yydebug _xkbcommon_debug +#define yynerrs _xkbcommon_nerrs + +/* First part of user prologue. */ +#line 33 "../src/xkbcomp/parser.y" + +#include "config.h" + +#include "xkbcomp/xkbcomp-priv.h" +#include "xkbcomp/ast-build.h" +#include "xkbcomp/parser-priv.h" +#include "scanner-utils.h" + +struct parser_param { + struct xkb_context *ctx; + struct scanner *scanner; + XkbFile *rtrn; + bool more_maps; +}; + +#define parser_err(param, fmt, ...) \ + scanner_err((param)->scanner, fmt, ##__VA_ARGS__) + +#define parser_warn(param, fmt, ...) \ + scanner_warn((param)->scanner, fmt, ##__VA_ARGS__) + +static void +_xkbcommon_error(struct parser_param *param, const char *msg) +{ + parser_err(param, "%s", msg); +} + +static bool +resolve_keysym(const char *name, xkb_keysym_t *sym_rtrn) +{ + xkb_keysym_t sym; + + if (!name || istreq(name, "any") || istreq(name, "nosymbol")) { + *sym_rtrn = XKB_KEY_NoSymbol; + return true; + } + + if (istreq(name, "none") || istreq(name, "voidsymbol")) { + *sym_rtrn = XKB_KEY_VoidSymbol; + return true; + } + + sym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); + if (sym != XKB_KEY_NoSymbol) { + *sym_rtrn = sym; + return true; + } + + return false; +} + +#define param_scanner param->scanner + +#line 130 "xkbcommon@sha/parser.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +# define YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int _xkbcommon_debug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + END_OF_FILE = 0, /* END_OF_FILE */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + ERROR_TOK = 255, /* ERROR_TOK */ + XKB_KEYMAP = 1, /* XKB_KEYMAP */ + XKB_KEYCODES = 2, /* XKB_KEYCODES */ + XKB_TYPES = 3, /* XKB_TYPES */ + XKB_SYMBOLS = 4, /* XKB_SYMBOLS */ + XKB_COMPATMAP = 5, /* XKB_COMPATMAP */ + XKB_GEOMETRY = 6, /* XKB_GEOMETRY */ + XKB_SEMANTICS = 7, /* XKB_SEMANTICS */ + XKB_LAYOUT = 8, /* XKB_LAYOUT */ + INCLUDE = 10, /* INCLUDE */ + OVERRIDE = 11, /* OVERRIDE */ + AUGMENT = 12, /* AUGMENT */ + REPLACE = 13, /* REPLACE */ + ALTERNATE = 14, /* ALTERNATE */ + VIRTUAL_MODS = 20, /* VIRTUAL_MODS */ + TYPE = 21, /* TYPE */ + INTERPRET = 22, /* INTERPRET */ + ACTION_TOK = 23, /* ACTION_TOK */ + KEY = 24, /* KEY */ + ALIAS = 25, /* ALIAS */ + GROUP = 26, /* GROUP */ + MODIFIER_MAP = 27, /* MODIFIER_MAP */ + INDICATOR = 28, /* INDICATOR */ + SHAPE = 29, /* SHAPE */ + KEYS = 30, /* KEYS */ + ROW = 31, /* ROW */ + SECTION = 32, /* SECTION */ + OVERLAY = 33, /* OVERLAY */ + TEXT = 34, /* TEXT */ + OUTLINE = 35, /* OUTLINE */ + SOLID = 36, /* SOLID */ + LOGO = 37, /* LOGO */ + VIRTUAL = 38, /* VIRTUAL */ + EQUALS = 40, /* EQUALS */ + PLUS = 41, /* PLUS */ + MINUS = 42, /* MINUS */ + DIVIDE = 43, /* DIVIDE */ + TIMES = 44, /* TIMES */ + OBRACE = 45, /* OBRACE */ + CBRACE = 46, /* CBRACE */ + OPAREN = 47, /* OPAREN */ + CPAREN = 48, /* CPAREN */ + OBRACKET = 49, /* OBRACKET */ + CBRACKET = 50, /* CBRACKET */ + DOT = 51, /* DOT */ + COMMA = 52, /* COMMA */ + SEMI = 53, /* SEMI */ + EXCLAM = 54, /* EXCLAM */ + INVERT = 55, /* INVERT */ + STRING = 60, /* STRING */ + INTEGER = 61, /* INTEGER */ + FLOAT = 62, /* FLOAT */ + IDENT = 63, /* IDENT */ + KEYNAME = 64, /* KEYNAME */ + PARTIAL = 70, /* PARTIAL */ + DEFAULT = 71, /* DEFAULT */ + HIDDEN = 72, /* HIDDEN */ + ALPHANUMERIC_KEYS = 73, /* ALPHANUMERIC_KEYS */ + MODIFIER_KEYS = 74, /* MODIFIER_KEYS */ + KEYPAD_KEYS = 75, /* KEYPAD_KEYS */ + FUNCTION_KEYS = 76, /* FUNCTION_KEYS */ + ALTERNATE_GROUP = 77 /* ALTERNATE_GROUP */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 164 "../src/xkbcomp/parser.y" + + int64_t num; + enum xkb_file_type file_type; + char *str; + xkb_atom_t atom; + enum merge_mode merge; + enum xkb_map_flags mapFlags; + xkb_keysym_t keysym; + ParseCommon *any; + struct { ParseCommon *head; ParseCommon *last; } anyList; + ExprDef *expr; + struct { ExprDef *head; ExprDef *last; } exprList; + VarDef *var; + struct { VarDef *head; VarDef *last; } varList; + VModDef *vmod; + struct { VModDef *head; VModDef *last; } vmodList; + InterpDef *interp; + KeyTypeDef *keyType; + SymbolsDef *syms; + ModMapDef *modMask; + GroupCompatDef *groupCompat; + LedMapDef *ledMap; + LedNameDef *ledName; + KeycodeDef *keyCode; + KeyAliasDef *keyAlias; + void *geom; + XkbFile *file; + struct { XkbFile *head; XkbFile *last; } fileList; + +#line 274 "xkbcommon@sha/parser.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int _xkbcommon_parse (struct parser_param *param); + +#endif /* !YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED */ +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* END_OF_FILE */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_ERROR_TOK = 3, /* ERROR_TOK */ + YYSYMBOL_XKB_KEYMAP = 4, /* XKB_KEYMAP */ + YYSYMBOL_XKB_KEYCODES = 5, /* XKB_KEYCODES */ + YYSYMBOL_XKB_TYPES = 6, /* XKB_TYPES */ + YYSYMBOL_XKB_SYMBOLS = 7, /* XKB_SYMBOLS */ + YYSYMBOL_XKB_COMPATMAP = 8, /* XKB_COMPATMAP */ + YYSYMBOL_XKB_GEOMETRY = 9, /* XKB_GEOMETRY */ + YYSYMBOL_XKB_SEMANTICS = 10, /* XKB_SEMANTICS */ + YYSYMBOL_XKB_LAYOUT = 11, /* XKB_LAYOUT */ + YYSYMBOL_INCLUDE = 12, /* INCLUDE */ + YYSYMBOL_OVERRIDE = 13, /* OVERRIDE */ + YYSYMBOL_AUGMENT = 14, /* AUGMENT */ + YYSYMBOL_REPLACE = 15, /* REPLACE */ + YYSYMBOL_ALTERNATE = 16, /* ALTERNATE */ + YYSYMBOL_VIRTUAL_MODS = 17, /* VIRTUAL_MODS */ + YYSYMBOL_TYPE = 18, /* TYPE */ + YYSYMBOL_INTERPRET = 19, /* INTERPRET */ + YYSYMBOL_ACTION_TOK = 20, /* ACTION_TOK */ + YYSYMBOL_KEY = 21, /* KEY */ + YYSYMBOL_ALIAS = 22, /* ALIAS */ + YYSYMBOL_GROUP = 23, /* GROUP */ + YYSYMBOL_MODIFIER_MAP = 24, /* MODIFIER_MAP */ + YYSYMBOL_INDICATOR = 25, /* INDICATOR */ + YYSYMBOL_SHAPE = 26, /* SHAPE */ + YYSYMBOL_KEYS = 27, /* KEYS */ + YYSYMBOL_ROW = 28, /* ROW */ + YYSYMBOL_SECTION = 29, /* SECTION */ + YYSYMBOL_OVERLAY = 30, /* OVERLAY */ + YYSYMBOL_TEXT = 31, /* TEXT */ + YYSYMBOL_OUTLINE = 32, /* OUTLINE */ + YYSYMBOL_SOLID = 33, /* SOLID */ + YYSYMBOL_LOGO = 34, /* LOGO */ + YYSYMBOL_VIRTUAL = 35, /* VIRTUAL */ + YYSYMBOL_EQUALS = 36, /* EQUALS */ + YYSYMBOL_PLUS = 37, /* PLUS */ + YYSYMBOL_MINUS = 38, /* MINUS */ + YYSYMBOL_DIVIDE = 39, /* DIVIDE */ + YYSYMBOL_TIMES = 40, /* TIMES */ + YYSYMBOL_OBRACE = 41, /* OBRACE */ + YYSYMBOL_CBRACE = 42, /* CBRACE */ + YYSYMBOL_OPAREN = 43, /* OPAREN */ + YYSYMBOL_CPAREN = 44, /* CPAREN */ + YYSYMBOL_OBRACKET = 45, /* OBRACKET */ + YYSYMBOL_CBRACKET = 46, /* CBRACKET */ + YYSYMBOL_DOT = 47, /* DOT */ + YYSYMBOL_COMMA = 48, /* COMMA */ + YYSYMBOL_SEMI = 49, /* SEMI */ + YYSYMBOL_EXCLAM = 50, /* EXCLAM */ + YYSYMBOL_INVERT = 51, /* INVERT */ + YYSYMBOL_STRING = 52, /* STRING */ + YYSYMBOL_INTEGER = 53, /* INTEGER */ + YYSYMBOL_FLOAT = 54, /* FLOAT */ + YYSYMBOL_IDENT = 55, /* IDENT */ + YYSYMBOL_KEYNAME = 56, /* KEYNAME */ + YYSYMBOL_PARTIAL = 57, /* PARTIAL */ + YYSYMBOL_DEFAULT = 58, /* DEFAULT */ + YYSYMBOL_HIDDEN = 59, /* HIDDEN */ + YYSYMBOL_ALPHANUMERIC_KEYS = 60, /* ALPHANUMERIC_KEYS */ + YYSYMBOL_MODIFIER_KEYS = 61, /* MODIFIER_KEYS */ + YYSYMBOL_KEYPAD_KEYS = 62, /* KEYPAD_KEYS */ + YYSYMBOL_FUNCTION_KEYS = 63, /* FUNCTION_KEYS */ + YYSYMBOL_ALTERNATE_GROUP = 64, /* ALTERNATE_GROUP */ + YYSYMBOL_YYACCEPT = 65, /* $accept */ + YYSYMBOL_XkbFile = 66, /* XkbFile */ + YYSYMBOL_XkbCompositeMap = 67, /* XkbCompositeMap */ + YYSYMBOL_XkbCompositeType = 68, /* XkbCompositeType */ + YYSYMBOL_XkbMapConfigList = 69, /* XkbMapConfigList */ + YYSYMBOL_XkbMapConfig = 70, /* XkbMapConfig */ + YYSYMBOL_FileType = 71, /* FileType */ + YYSYMBOL_OptFlags = 72, /* OptFlags */ + YYSYMBOL_Flags = 73, /* Flags */ + YYSYMBOL_Flag = 74, /* Flag */ + YYSYMBOL_DeclList = 75, /* DeclList */ + YYSYMBOL_Decl = 76, /* Decl */ + YYSYMBOL_VarDecl = 77, /* VarDecl */ + YYSYMBOL_KeyNameDecl = 78, /* KeyNameDecl */ + YYSYMBOL_KeyAliasDecl = 79, /* KeyAliasDecl */ + YYSYMBOL_VModDecl = 80, /* VModDecl */ + YYSYMBOL_VModDefList = 81, /* VModDefList */ + YYSYMBOL_VModDef = 82, /* VModDef */ + YYSYMBOL_InterpretDecl = 83, /* InterpretDecl */ + YYSYMBOL_InterpretMatch = 84, /* InterpretMatch */ + YYSYMBOL_VarDeclList = 85, /* VarDeclList */ + YYSYMBOL_KeyTypeDecl = 86, /* KeyTypeDecl */ + YYSYMBOL_SymbolsDecl = 87, /* SymbolsDecl */ + YYSYMBOL_SymbolsBody = 88, /* SymbolsBody */ + YYSYMBOL_SymbolsVarDecl = 89, /* SymbolsVarDecl */ + YYSYMBOL_ArrayInit = 90, /* ArrayInit */ + YYSYMBOL_GroupCompatDecl = 91, /* GroupCompatDecl */ + YYSYMBOL_ModMapDecl = 92, /* ModMapDecl */ + YYSYMBOL_LedMapDecl = 93, /* LedMapDecl */ + YYSYMBOL_LedNameDecl = 94, /* LedNameDecl */ + YYSYMBOL_ShapeDecl = 95, /* ShapeDecl */ + YYSYMBOL_SectionDecl = 96, /* SectionDecl */ + YYSYMBOL_SectionBody = 97, /* SectionBody */ + YYSYMBOL_SectionBodyItem = 98, /* SectionBodyItem */ + YYSYMBOL_RowBody = 99, /* RowBody */ + YYSYMBOL_RowBodyItem = 100, /* RowBodyItem */ + YYSYMBOL_Keys = 101, /* Keys */ + YYSYMBOL_Key = 102, /* Key */ + YYSYMBOL_OverlayDecl = 103, /* OverlayDecl */ + YYSYMBOL_OverlayKeyList = 104, /* OverlayKeyList */ + YYSYMBOL_OverlayKey = 105, /* OverlayKey */ + YYSYMBOL_OutlineList = 106, /* OutlineList */ + YYSYMBOL_OutlineInList = 107, /* OutlineInList */ + YYSYMBOL_CoordList = 108, /* CoordList */ + YYSYMBOL_Coord = 109, /* Coord */ + YYSYMBOL_DoodadDecl = 110, /* DoodadDecl */ + YYSYMBOL_DoodadType = 111, /* DoodadType */ + YYSYMBOL_FieldSpec = 112, /* FieldSpec */ + YYSYMBOL_Element = 113, /* Element */ + YYSYMBOL_OptMergeMode = 114, /* OptMergeMode */ + YYSYMBOL_MergeMode = 115, /* MergeMode */ + YYSYMBOL_OptExprList = 116, /* OptExprList */ + YYSYMBOL_ExprList = 117, /* ExprList */ + YYSYMBOL_Expr = 118, /* Expr */ + YYSYMBOL_Term = 119, /* Term */ + YYSYMBOL_ActionList = 120, /* ActionList */ + YYSYMBOL_Action = 121, /* Action */ + YYSYMBOL_Lhs = 122, /* Lhs */ + YYSYMBOL_Terminal = 123, /* Terminal */ + YYSYMBOL_OptKeySymList = 124, /* OptKeySymList */ + YYSYMBOL_KeySymList = 125, /* KeySymList */ + YYSYMBOL_KeySyms = 126, /* KeySyms */ + YYSYMBOL_KeySym = 127, /* KeySym */ + YYSYMBOL_SignedNumber = 128, /* SignedNumber */ + YYSYMBOL_Number = 129, /* Number */ + YYSYMBOL_Float = 130, /* Float */ + YYSYMBOL_Integer = 131, /* Integer */ + YYSYMBOL_KeyCode = 132, /* KeyCode */ + YYSYMBOL_Ident = 133, /* Ident */ + YYSYMBOL_String = 134, /* String */ + YYSYMBOL_OptMapName = 135, /* OptMapName */ + YYSYMBOL_MapName = 136 /* MapName */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 16 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 735 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 65 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 72 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 184 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 334 + +#define YYMAXUTOK 257 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 4, 5, 6, 7, 8, 9, 10, 11, 2, + 12, 13, 14, 15, 16, 2, 2, 2, 2, 2, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 2, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 2, 2, 2, 2, + 52, 53, 54, 55, 56, 2, 2, 2, 2, 2, + 57, 58, 59, 60, 61, 62, 63, 64, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 1, 2 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 254, 254, 256, 258, 262, 268, 269, 270, 273, + 275, 279, 287, 288, 289, 290, 291, 294, 295, 298, + 299, 302, 303, 304, 305, 306, 307, 308, 309, 312, + 327, 337, 340, 346, 351, 356, 361, 366, 371, 376, + 381, 386, 391, 392, 393, 394, 401, 403, 405, 409, + 413, 417, 421, 423, 427, 429, 433, 439, 441, 445, + 447, 451, 457, 463, 465, 467, 470, 471, 472, 473, + 474, 477, 479, 483, 487, 491, 495, 497, 501, 503, + 507, 511, 512, 515, 517, 519, 521, 523, 527, 528, + 531, 532, 536, 537, 540, 542, 546, 550, 551, 554, + 557, 559, 563, 565, 567, 571, 573, 577, 581, 585, + 586, 587, 588, 591, 592, 595, 597, 599, 601, 603, + 605, 607, 609, 611, 613, 615, 619, 620, 623, 624, + 625, 626, 627, 637, 638, 641, 643, 647, 649, 651, + 653, 655, 657, 661, 663, 665, 667, 669, 671, 673, + 675, 679, 681, 685, 689, 691, 693, 695, 699, 701, + 703, 705, 709, 710, 713, 715, 717, 719, 723, 727, + 733, 734, 754, 755, 758, 759, 762, 765, 768, 771, + 772, 775, 778, 779, 782 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "END_OF_FILE", "error", "\"invalid token\"", "ERROR_TOK", "XKB_KEYMAP", + "XKB_KEYCODES", "XKB_TYPES", "XKB_SYMBOLS", "XKB_COMPATMAP", + "XKB_GEOMETRY", "XKB_SEMANTICS", "XKB_LAYOUT", "INCLUDE", "OVERRIDE", + "AUGMENT", "REPLACE", "ALTERNATE", "VIRTUAL_MODS", "TYPE", "INTERPRET", + "ACTION_TOK", "KEY", "ALIAS", "GROUP", "MODIFIER_MAP", "INDICATOR", + "SHAPE", "KEYS", "ROW", "SECTION", "OVERLAY", "TEXT", "OUTLINE", "SOLID", + "LOGO", "VIRTUAL", "EQUALS", "PLUS", "MINUS", "DIVIDE", "TIMES", + "OBRACE", "CBRACE", "OPAREN", "CPAREN", "OBRACKET", "CBRACKET", "DOT", + "COMMA", "SEMI", "EXCLAM", "INVERT", "STRING", "INTEGER", "FLOAT", + "IDENT", "KEYNAME", "PARTIAL", "DEFAULT", "HIDDEN", "ALPHANUMERIC_KEYS", + "MODIFIER_KEYS", "KEYPAD_KEYS", "FUNCTION_KEYS", "ALTERNATE_GROUP", + "$accept", "XkbFile", "XkbCompositeMap", "XkbCompositeType", + "XkbMapConfigList", "XkbMapConfig", "FileType", "OptFlags", "Flags", + "Flag", "DeclList", "Decl", "VarDecl", "KeyNameDecl", "KeyAliasDecl", + "VModDecl", "VModDefList", "VModDef", "InterpretDecl", "InterpretMatch", + "VarDeclList", "KeyTypeDecl", "SymbolsDecl", "SymbolsBody", + "SymbolsVarDecl", "ArrayInit", "GroupCompatDecl", "ModMapDecl", + "LedMapDecl", "LedNameDecl", "ShapeDecl", "SectionDecl", "SectionBody", + "SectionBodyItem", "RowBody", "RowBodyItem", "Keys", "Key", + "OverlayDecl", "OverlayKeyList", "OverlayKey", "OutlineList", + "OutlineInList", "CoordList", "Coord", "DoodadDecl", "DoodadType", + "FieldSpec", "Element", "OptMergeMode", "MergeMode", "OptExprList", + "ExprList", "Expr", "Term", "ActionList", "Action", "Lhs", "Terminal", + "OptKeySymList", "KeySymList", "KeySyms", "KeySym", "SignedNumber", + "Number", "Float", "Integer", "KeyCode", "Ident", "String", "OptMapName", + "MapName", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 255, 1, 2, 3, 4, 5, 6, + 7, 8, 10, 11, 12, 13, 14, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 60, 61, 62, 63, 64, 70, 71, 72, + 73, 74, 75, 76, 77 +}; +#endif + +#define YYPACT_NINF (-182) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-180) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 176, -182, -182, -182, -182, -182, -182, -182, -182, -182, + 6, -182, -182, 271, 227, -182, -182, -182, -182, -182, + -182, -182, -182, -182, -182, -38, -38, -182, -182, -24, + -182, 33, 227, -182, 210, -182, 353, 44, 5, -182, + -182, -182, -182, -182, -182, 32, -182, 13, 41, -182, + -182, -48, 55, 11, -182, 79, 87, 58, -48, -2, + 55, -182, 55, 72, -182, -182, -182, 107, -48, -182, + 110, -182, -182, -182, -182, -182, -182, -182, -182, -182, + -182, -182, -182, -182, -182, -182, 55, -18, -182, 127, + 121, -182, 66, -182, 138, -182, 136, -182, -182, -182, + 144, 147, -182, 152, 180, 182, 178, 184, 187, 188, + 190, 58, 198, 201, 214, 367, 677, 367, -182, -48, + -182, 367, 663, 663, 367, 494, 200, 367, 367, 367, + 663, 68, 449, 223, -182, -182, 212, 663, -182, -182, + -182, -182, -182, -182, -182, -182, -182, 367, 367, 367, + 367, 367, -182, -182, 57, 157, -182, 224, -182, -182, + -182, -182, -182, 218, 91, -182, 333, -182, 509, 537, + 333, 552, -48, 1, -182, -182, 228, 40, 216, 143, + 70, 333, 150, 593, 247, -30, 97, -182, 105, -182, + 261, 55, 259, 55, -182, -182, 408, -182, -182, -182, + 367, -182, 608, -182, -182, -182, 287, -182, -182, 367, + 367, 367, 367, 367, -182, 367, 367, -182, 252, -182, + 253, 264, 24, 269, 272, 163, -182, 273, 270, -182, + -182, -182, 280, 494, 285, -182, -182, 283, 367, -182, + 284, 112, 8, -182, -182, 294, -182, 299, -36, 304, + 247, 326, 649, 279, 307, -182, 204, 316, -182, 322, + 320, 111, 111, -182, -182, 333, 211, -182, -182, 116, + 367, -182, 677, -182, 24, -182, -182, -182, 333, -182, + 333, -182, -182, -182, -30, -182, -182, -182, -182, 247, + 333, 334, -182, 466, -182, 318, -182, -182, -182, -182, + -182, -182, 339, -182, -182, -182, 343, 120, 14, 345, + -182, 361, 124, -182, -182, -182, -182, 367, -182, 131, + -182, -182, 344, 350, 318, 166, 352, 14, -182, -182, + -182, -182, -182, -182 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 18, 4, 21, 22, 23, 24, 25, 26, 27, 28, + 0, 2, 3, 0, 17, 20, 1, 6, 12, 13, + 15, 14, 16, 7, 8, 183, 183, 19, 184, 0, + 182, 0, 18, 31, 18, 10, 0, 127, 0, 9, + 128, 130, 129, 131, 132, 0, 29, 0, 126, 5, + 11, 0, 117, 116, 115, 118, 0, 119, 120, 121, + 122, 123, 124, 125, 110, 111, 112, 0, 0, 179, + 0, 180, 32, 34, 35, 30, 33, 36, 37, 39, + 38, 40, 41, 42, 43, 44, 0, 154, 114, 0, + 113, 45, 0, 53, 54, 181, 0, 170, 177, 169, + 0, 58, 171, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, + 51, 0, 0, 0, 0, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 48, 178, 0, 0, 117, 116, + 118, 119, 120, 121, 122, 124, 125, 0, 0, 0, + 0, 0, 176, 161, 154, 0, 142, 147, 149, 160, + 159, 113, 158, 155, 0, 52, 55, 60, 0, 0, + 57, 163, 0, 0, 64, 70, 0, 113, 0, 0, + 0, 136, 0, 0, 0, 0, 0, 101, 0, 106, + 0, 121, 123, 0, 84, 86, 0, 82, 87, 85, + 0, 49, 0, 144, 147, 143, 0, 145, 146, 134, + 0, 0, 0, 0, 156, 0, 0, 46, 0, 59, + 0, 170, 0, 169, 0, 0, 152, 0, 162, 167, + 166, 69, 0, 0, 0, 50, 73, 0, 0, 76, + 0, 0, 0, 175, 174, 0, 173, 0, 0, 0, + 0, 0, 0, 0, 0, 81, 0, 0, 150, 0, + 133, 138, 139, 137, 140, 141, 0, 61, 56, 0, + 134, 72, 0, 71, 0, 62, 63, 67, 66, 74, + 135, 75, 102, 172, 0, 78, 100, 79, 105, 0, + 104, 0, 91, 0, 89, 0, 80, 77, 108, 148, + 157, 168, 0, 151, 165, 164, 0, 0, 0, 0, + 88, 0, 0, 98, 153, 107, 103, 0, 94, 0, + 93, 83, 0, 0, 0, 0, 0, 0, 99, 96, + 97, 95, 90, 92 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -182, -182, -182, -182, -182, 181, -182, 402, -182, 389, + -182, -182, -35, -182, -182, -182, -182, 288, -182, -182, + -50, -182, -182, -182, 173, 174, -182, -182, 362, -182, + -182, -182, -182, 215, -182, 119, -182, 86, -182, -182, + 90, -182, 167, -181, 185, 369, -182, -27, -182, -182, + -182, 154, -126, 83, 76, -182, 158, -31, -182, -182, + 221, 170, -52, 161, 205, -182, -44, -182, -47, -34, + 420, -182 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 10, 11, 25, 34, 12, 26, 36, 14, 15, + 37, 46, 167, 73, 74, 75, 92, 93, 76, 100, + 168, 77, 78, 173, 174, 175, 79, 80, 195, 82, + 83, 84, 196, 197, 293, 294, 319, 320, 198, 312, + 313, 186, 187, 188, 189, 199, 86, 154, 88, 47, + 48, 259, 260, 181, 156, 225, 226, 157, 158, 227, + 228, 229, 230, 245, 246, 159, 160, 136, 161, 162, + 29, 30 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 90, 101, 180, 241, 94, 184, 16, 69, 242, 102, + 71, 106, 72, 105, 28, 107, 89, 32, 96, 69, + 87, 112, 71, 243, 244, 108, 109, 115, 110, 116, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 97, 61, 62, 232, 63, 64, 65, 66, 67, 233, + 95, 98, 114, 97, 49, 317, 40, 41, 42, 43, + 44, 243, 244, 68, 98, 222, 99, 133, 69, 70, + 318, 71, 94, 169, 33, 90, 90, 98, 177, 99, + 183, 50, -68, 90, 190, 90, 45, 202, -68, 163, + 90, 89, 89, 91, 176, 87, 87, 194, 87, 89, + 209, 89, 115, 87, 116, 87, 89, 95, 307, 184, + 87, 98, 237, 185, 119, 120, 204, 204, 238, 204, + 204, 90, 90, 69, -109, 231, 71, 102, 210, 211, + 212, 213, 111, 219, 219, 103, 90, 89, 89, 247, + 217, 87, 87, 104, 224, 248, 113, 249, 219, 90, + 212, 213, 89, 250, 282, 90, 87, 108, 301, 253, + 250, 194, 316, 117, 274, 89, 323, 219, 250, 87, + 118, 89, 324, 326, 121, 87, 1, 122, 102, 327, + 210, 211, 212, 213, 124, 123, 177, 210, 211, 212, + 213, 325, 236, 125, 210, 211, 212, 213, 155, 239, + 164, 190, 176, 214, 166, 90, 87, 170, 331, 271, + 179, 272, 182, 35, 238, 39, 126, 292, 127, 128, + 129, 89, 305, 203, 205, 87, 207, 208, 130, 131, + 102, 132, 206, 2, 3, 4, 5, 6, 7, 8, + 9, 210, 211, 212, 213, 224, 90, 134, 210, 211, + 212, 213, 38, 297, 135, 137, 178, 300, 292, 200, + 215, 201, 89, 216, 234, 235, 87, 2, 3, 4, + 5, 6, 7, 8, 9, 17, 18, 19, 20, 21, + 22, 23, 24, 256, 2, 3, 4, 5, 6, 7, + 8, 9, 185, 261, 262, 263, 264, 251, 265, 266, + 252, 267, 268, 138, 139, 54, 140, -124, 141, 142, + 143, 144, -179, 61, 145, 270, 146, 278, 274, 273, + 295, 280, 147, 148, 210, 211, 212, 213, 149, 275, + 171, 258, 279, 281, 290, 150, 151, 95, 98, 152, + 69, 153, 284, 71, 138, 139, 54, 140, 285, 141, + 142, 143, 144, 287, 61, 145, 296, 146, 18, 19, + 20, 21, 22, 147, 148, 298, 299, 289, 238, 149, + 210, 211, 212, 213, 311, 308, 150, 151, 95, 98, + 152, 69, 153, 314, 71, 138, 139, 54, 140, 315, + 141, 142, 143, 144, 321, 61, 145, 322, 146, 329, + 328, 332, 13, 27, 147, 148, 276, 165, 277, 81, + 149, 255, 310, 333, 330, 286, 85, 150, 151, 95, + 98, 152, 69, 153, 302, 71, 138, 139, 54, 140, + 303, 141, 142, 191, 144, 288, 192, 145, 193, 63, + 64, 65, 66, 269, 304, 306, 31, 283, 0, 0, + 254, 0, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 0, 0, 69, 0, 0, 71, 138, 139, 54, + 140, 0, 141, 142, 191, 144, 0, 192, 145, 193, + 63, 64, 65, 66, 138, 139, 54, 140, 0, 141, + 142, 143, 144, 291, 61, 145, 0, 146, 0, 68, + 0, 0, 0, 0, 69, 0, 0, 71, 309, 0, + 0, 0, 138, 139, 54, 140, 68, 141, 142, 143, + 144, 69, 61, 145, 71, 146, 0, 138, 139, 54, + 140, 0, 141, 142, 143, 144, 0, 61, 145, 171, + 146, 0, 0, 0, 172, 0, 0, 0, 0, 69, + 0, 218, 71, 0, 0, 138, 139, 54, 140, 68, + 141, 142, 143, 144, 69, 61, 145, 71, 146, 0, + 138, 139, 54, 140, 0, 141, 142, 143, 144, 220, + 61, 221, 0, 146, 0, 0, 0, 68, 0, 0, + 0, 0, 69, 222, 0, 71, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 98, 0, 223, 0, 0, + 71, 138, 139, 54, 140, 0, 141, 142, 143, 144, + 0, 61, 145, 0, 146, 0, 138, 139, 54, 140, + 0, 141, 142, 143, 144, 240, 61, 145, 0, 146, + 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, + 257, 71, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 0, 0, 69, 0, 0, 71, 138, 139, 54, + 140, 0, 141, 142, 143, 144, 291, 61, 145, 0, + 146, 138, 139, 54, 140, 0, 141, 142, 143, 144, + 0, 61, 145, 0, 146, 138, 139, 54, 140, 68, + 141, 142, 143, 144, 69, 61, 145, 71, 146, 0, + 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, + 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 69, 0, 0, 71 +}; + +static const yytype_int16 yycheck[] = +{ + 47, 53, 128, 184, 51, 41, 0, 55, 38, 53, + 58, 58, 47, 57, 52, 59, 47, 41, 52, 55, + 47, 68, 58, 53, 54, 59, 60, 45, 62, 47, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 29, 28, 29, 42, 31, 32, 33, 34, 35, 48, + 52, 53, 86, 29, 49, 41, 12, 13, 14, 15, + 16, 53, 54, 50, 53, 41, 55, 111, 55, 56, + 56, 58, 119, 123, 41, 122, 123, 53, 125, 55, + 130, 49, 42, 130, 131, 132, 42, 137, 48, 116, + 137, 122, 123, 52, 125, 122, 123, 132, 125, 130, + 43, 132, 45, 130, 47, 132, 137, 52, 289, 41, + 137, 53, 42, 45, 48, 49, 147, 148, 48, 150, + 151, 168, 169, 55, 52, 172, 58, 171, 37, 38, + 39, 40, 25, 168, 169, 56, 183, 168, 169, 42, + 49, 168, 169, 56, 171, 48, 36, 42, 183, 196, + 39, 40, 183, 48, 42, 202, 183, 191, 42, 193, + 48, 196, 42, 36, 48, 196, 42, 202, 48, 196, + 49, 202, 48, 42, 36, 202, 0, 41, 222, 48, + 37, 38, 39, 40, 37, 41, 233, 37, 38, 39, + 40, 317, 49, 41, 37, 38, 39, 40, 115, 49, + 117, 248, 233, 46, 121, 252, 233, 124, 42, 46, + 127, 48, 129, 32, 48, 34, 36, 252, 36, 41, + 36, 252, 274, 147, 148, 252, 150, 151, 41, 41, + 274, 41, 149, 57, 58, 59, 60, 61, 62, 63, + 64, 37, 38, 39, 40, 272, 293, 49, 37, 38, + 39, 40, 42, 49, 53, 41, 56, 46, 293, 36, + 36, 49, 293, 45, 36, 49, 293, 57, 58, 59, + 60, 61, 62, 63, 64, 4, 5, 6, 7, 8, + 9, 10, 11, 200, 57, 58, 59, 60, 61, 62, + 63, 64, 45, 210, 211, 212, 213, 36, 215, 216, + 41, 49, 49, 18, 19, 20, 21, 43, 23, 24, + 25, 26, 43, 28, 29, 43, 31, 234, 48, 46, + 41, 238, 37, 38, 37, 38, 39, 40, 43, 49, + 45, 44, 49, 49, 251, 50, 51, 52, 53, 54, + 55, 56, 48, 58, 18, 19, 20, 21, 49, 23, + 24, 25, 26, 49, 28, 29, 49, 31, 5, 6, + 7, 8, 9, 37, 38, 49, 44, 41, 48, 43, + 37, 38, 39, 40, 56, 41, 50, 51, 52, 53, + 54, 55, 56, 44, 58, 18, 19, 20, 21, 46, + 23, 24, 25, 26, 49, 28, 29, 36, 31, 49, + 56, 49, 0, 14, 37, 38, 233, 119, 234, 47, + 43, 196, 293, 327, 324, 248, 47, 50, 51, 52, + 53, 54, 55, 56, 270, 58, 18, 19, 20, 21, + 272, 23, 24, 25, 26, 250, 28, 29, 30, 31, + 32, 33, 34, 222, 274, 284, 26, 242, -1, -1, + 42, -1, -1, -1, -1, -1, -1, -1, 50, -1, + -1, -1, -1, 55, -1, -1, 58, 18, 19, 20, + 21, -1, 23, 24, 25, 26, -1, 28, 29, 30, + 31, 32, 33, 34, 18, 19, 20, 21, -1, 23, + 24, 25, 26, 27, 28, 29, -1, 31, -1, 50, + -1, -1, -1, -1, 55, -1, -1, 58, 42, -1, + -1, -1, 18, 19, 20, 21, 50, 23, 24, 25, + 26, 55, 28, 29, 58, 31, -1, 18, 19, 20, + 21, -1, 23, 24, 25, 26, -1, 28, 29, 45, + 31, -1, -1, -1, 50, -1, -1, -1, -1, 55, + -1, 42, 58, -1, -1, 18, 19, 20, 21, 50, + 23, 24, 25, 26, 55, 28, 29, 58, 31, -1, + 18, 19, 20, 21, -1, 23, 24, 25, 26, 42, + 28, 29, -1, 31, -1, -1, -1, 50, -1, -1, + -1, -1, 55, 41, -1, 58, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 53, -1, 55, -1, -1, + 58, 18, 19, 20, 21, -1, 23, 24, 25, 26, + -1, 28, 29, -1, 31, -1, 18, 19, 20, 21, + -1, 23, 24, 25, 26, 42, 28, 29, -1, 31, + -1, -1, -1, 50, -1, -1, -1, -1, 55, -1, + 42, 58, -1, -1, -1, -1, -1, -1, 50, -1, + -1, -1, -1, 55, -1, -1, 58, 18, 19, 20, + 21, -1, 23, 24, 25, 26, 27, 28, 29, -1, + 31, 18, 19, 20, 21, -1, 23, 24, 25, 26, + -1, 28, 29, -1, 31, 18, 19, 20, 21, 50, + 23, 24, 25, 26, 55, 28, 29, 58, 31, -1, + -1, -1, -1, 50, -1, -1, -1, -1, 55, -1, + -1, 58, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 55, -1, -1, 58 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 0, 57, 58, 59, 60, 61, 62, 63, 64, + 66, 67, 70, 72, 73, 74, 0, 4, 5, 6, + 7, 8, 9, 10, 11, 68, 71, 74, 52, 135, + 136, 135, 41, 41, 69, 70, 72, 75, 42, 70, + 12, 13, 14, 15, 16, 42, 76, 114, 115, 49, + 49, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 28, 29, 31, 32, 33, 34, 35, 50, 55, + 56, 58, 77, 78, 79, 80, 83, 86, 87, 91, + 92, 93, 94, 95, 96, 110, 111, 112, 113, 122, + 133, 52, 81, 82, 133, 52, 134, 29, 53, 55, + 84, 127, 131, 56, 56, 131, 133, 131, 134, 134, + 134, 25, 133, 36, 134, 45, 47, 36, 49, 48, + 49, 36, 41, 41, 37, 41, 36, 36, 41, 36, + 41, 41, 41, 131, 49, 53, 132, 41, 18, 19, + 21, 23, 24, 25, 26, 29, 31, 37, 38, 43, + 50, 51, 54, 56, 112, 118, 119, 122, 123, 130, + 131, 133, 134, 112, 118, 82, 118, 77, 85, 85, + 118, 45, 50, 88, 89, 90, 122, 133, 56, 118, + 117, 118, 118, 85, 41, 45, 106, 107, 108, 109, + 133, 25, 28, 30, 77, 93, 97, 98, 103, 110, + 36, 49, 85, 119, 122, 119, 118, 119, 119, 43, + 37, 38, 39, 40, 46, 36, 45, 49, 42, 77, + 42, 29, 41, 55, 112, 120, 121, 124, 125, 126, + 127, 133, 42, 48, 36, 49, 49, 42, 48, 49, + 42, 108, 38, 53, 54, 128, 129, 42, 48, 42, + 48, 36, 41, 134, 42, 98, 118, 42, 44, 116, + 117, 118, 118, 118, 118, 118, 118, 49, 49, 125, + 43, 46, 48, 46, 48, 49, 89, 90, 118, 49, + 118, 49, 42, 129, 48, 49, 107, 49, 109, 41, + 118, 27, 77, 99, 100, 41, 49, 49, 49, 44, + 46, 42, 116, 121, 126, 127, 128, 108, 41, 42, + 100, 56, 104, 105, 44, 46, 42, 41, 56, 101, + 102, 49, 36, 42, 48, 117, 42, 48, 56, 49, + 105, 42, 49, 102 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 65, 66, 66, 66, 67, 68, 68, 68, 69, + 69, 70, 71, 71, 71, 71, 71, 72, 72, 73, + 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, + 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 77, 77, 77, 78, + 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, + 85, 86, 87, 88, 88, 88, 89, 89, 89, 89, + 89, 90, 90, 91, 92, 93, 94, 94, 95, 95, + 96, 97, 97, 98, 98, 98, 98, 98, 99, 99, + 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, + 106, 106, 107, 107, 107, 108, 108, 109, 110, 111, + 111, 111, 111, 112, 112, 113, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 114, 114, 115, 115, + 115, 115, 115, 116, 116, 117, 117, 118, 118, 118, + 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, + 119, 120, 120, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 125, 125, 125, 125, 126, 127, + 127, 127, 128, 128, 129, 129, 130, 131, 132, 133, + 133, 134, 135, 135, 136 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 1, 7, 1, 1, 1, 2, + 1, 7, 1, 1, 1, 1, 1, 1, 0, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 3, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 3, 4, + 5, 3, 3, 1, 1, 3, 6, 3, 1, 2, + 1, 6, 6, 3, 1, 0, 3, 3, 1, 2, + 1, 3, 3, 5, 6, 6, 5, 6, 6, 6, + 6, 2, 1, 5, 1, 1, 1, 1, 2, 1, + 5, 1, 3, 1, 1, 3, 6, 3, 1, 3, + 3, 1, 3, 5, 3, 3, 1, 5, 6, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 0, 3, 1, 3, 3, 3, + 3, 3, 1, 2, 2, 2, 2, 1, 4, 1, + 3, 3, 1, 4, 1, 3, 4, 6, 1, 1, + 1, 1, 1, 0, 3, 3, 1, 1, 3, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (param, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, param); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct parser_param *param) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (param); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct parser_param *param) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, param); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, struct parser_param *param) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], param); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, param); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, struct parser_param *param) +{ + YYUSE (yyvaluep); + YYUSE (param); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yykind) + { + case 52: /* STRING */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1453 "xkbcommon@sha/parser.c" + break; + + case 55: /* IDENT */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1459 "xkbcommon@sha/parser.c" + break; + + case 66: /* XkbFile */ +#line 236 "../src/xkbcomp/parser.y" + { if (!param->rtrn) FreeXkbFile(((*yyvaluep).file)); } +#line 1465 "xkbcommon@sha/parser.c" + break; + + case 67: /* XkbCompositeMap */ +#line 236 "../src/xkbcomp/parser.y" + { if (!param->rtrn) FreeXkbFile(((*yyvaluep).file)); } +#line 1471 "xkbcommon@sha/parser.c" + break; + + case 69: /* XkbMapConfigList */ +#line 237 "../src/xkbcomp/parser.y" + { FreeXkbFile(((*yyvaluep).fileList).head); } +#line 1477 "xkbcommon@sha/parser.c" + break; + + case 70: /* XkbMapConfig */ +#line 236 "../src/xkbcomp/parser.y" + { if (!param->rtrn) FreeXkbFile(((*yyvaluep).file)); } +#line 1483 "xkbcommon@sha/parser.c" + break; + + case 75: /* DeclList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).anyList).head); } +#line 1489 "xkbcommon@sha/parser.c" + break; + + case 76: /* Decl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).any)); } +#line 1495 "xkbcommon@sha/parser.c" + break; + + case 77: /* VarDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).var)); } +#line 1501 "xkbcommon@sha/parser.c" + break; + + case 78: /* KeyNameDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).keyCode)); } +#line 1507 "xkbcommon@sha/parser.c" + break; + + case 79: /* KeyAliasDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).keyAlias)); } +#line 1513 "xkbcommon@sha/parser.c" + break; + + case 80: /* VModDecl */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).vmodList).head); } +#line 1519 "xkbcommon@sha/parser.c" + break; + + case 81: /* VModDefList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).vmodList).head); } +#line 1525 "xkbcommon@sha/parser.c" + break; + + case 82: /* VModDef */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).vmod)); } +#line 1531 "xkbcommon@sha/parser.c" + break; + + case 83: /* InterpretDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).interp)); } +#line 1537 "xkbcommon@sha/parser.c" + break; + + case 84: /* InterpretMatch */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).interp)); } +#line 1543 "xkbcommon@sha/parser.c" + break; + + case 85: /* VarDeclList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).varList).head); } +#line 1549 "xkbcommon@sha/parser.c" + break; + + case 86: /* KeyTypeDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).keyType)); } +#line 1555 "xkbcommon@sha/parser.c" + break; + + case 87: /* SymbolsDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).syms)); } +#line 1561 "xkbcommon@sha/parser.c" + break; + + case 88: /* SymbolsBody */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).varList).head); } +#line 1567 "xkbcommon@sha/parser.c" + break; + + case 89: /* SymbolsVarDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).var)); } +#line 1573 "xkbcommon@sha/parser.c" + break; + + case 90: /* ArrayInit */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1579 "xkbcommon@sha/parser.c" + break; + + case 91: /* GroupCompatDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).groupCompat)); } +#line 1585 "xkbcommon@sha/parser.c" + break; + + case 92: /* ModMapDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).modMask)); } +#line 1591 "xkbcommon@sha/parser.c" + break; + + case 93: /* LedMapDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).ledMap)); } +#line 1597 "xkbcommon@sha/parser.c" + break; + + case 94: /* LedNameDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).ledName)); } +#line 1603 "xkbcommon@sha/parser.c" + break; + + case 108: /* CoordList */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1609 "xkbcommon@sha/parser.c" + break; + + case 109: /* Coord */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1615 "xkbcommon@sha/parser.c" + break; + + case 116: /* OptExprList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).exprList).head); } +#line 1621 "xkbcommon@sha/parser.c" + break; + + case 117: /* ExprList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).exprList).head); } +#line 1627 "xkbcommon@sha/parser.c" + break; + + case 118: /* Expr */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1633 "xkbcommon@sha/parser.c" + break; + + case 119: /* Term */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1639 "xkbcommon@sha/parser.c" + break; + + case 120: /* ActionList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).exprList).head); } +#line 1645 "xkbcommon@sha/parser.c" + break; + + case 121: /* Action */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1651 "xkbcommon@sha/parser.c" + break; + + case 122: /* Lhs */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1657 "xkbcommon@sha/parser.c" + break; + + case 123: /* Terminal */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1663 "xkbcommon@sha/parser.c" + break; + + case 124: /* OptKeySymList */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1669 "xkbcommon@sha/parser.c" + break; + + case 125: /* KeySymList */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1675 "xkbcommon@sha/parser.c" + break; + + case 126: /* KeySyms */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1681 "xkbcommon@sha/parser.c" + break; + + case 135: /* OptMapName */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1687 "xkbcommon@sha/parser.c" + break; + + case 136: /* MapName */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1693 "xkbcommon@sha/parser.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (struct parser_param *param) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize; + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yynerrs = 0; + yystate = 0; + yyerrstatus = 0; + + yystacksize = YYINITDEPTH; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, param_scanner); + } + + if (yychar <= END_OF_FILE) + { + yychar = END_OF_FILE; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 255 "../src/xkbcomp/parser.y" + { (yyval.file) = param->rtrn = (yyvsp[0].file); param->more_maps = !!param->rtrn; } +#line 1980 "xkbcommon@sha/parser.c" + break; + + case 3: +#line 257 "../src/xkbcomp/parser.y" + { (yyval.file) = param->rtrn = (yyvsp[0].file); param->more_maps = !!param->rtrn; YYACCEPT; } +#line 1986 "xkbcommon@sha/parser.c" + break; + + case 4: +#line 259 "../src/xkbcomp/parser.y" + { (yyval.file) = param->rtrn = NULL; param->more_maps = false; } +#line 1992 "xkbcommon@sha/parser.c" + break; + + case 5: +#line 265 "../src/xkbcomp/parser.y" + { (yyval.file) = XkbFileCreate((yyvsp[-5].file_type), (yyvsp[-4].str), (ParseCommon *) (yyvsp[-2].fileList).head, (yyvsp[-6].mapFlags)); } +#line 1998 "xkbcommon@sha/parser.c" + break; + + case 6: +#line 268 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYMAP; } +#line 2004 "xkbcommon@sha/parser.c" + break; + + case 7: +#line 269 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYMAP; } +#line 2010 "xkbcommon@sha/parser.c" + break; + + case 8: +#line 270 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYMAP; } +#line 2016 "xkbcommon@sha/parser.c" + break; + + case 9: +#line 274 "../src/xkbcomp/parser.y" + { (yyval.fileList).head = (yyvsp[-1].fileList).head; (yyval.fileList).last->common.next = &(yyvsp[0].file)->common; (yyval.fileList).last = (yyvsp[0].file); } +#line 2022 "xkbcommon@sha/parser.c" + break; + + case 10: +#line 276 "../src/xkbcomp/parser.y" + { (yyval.fileList).head = (yyval.fileList).last = (yyvsp[0].file); } +#line 2028 "xkbcommon@sha/parser.c" + break; + + case 11: +#line 282 "../src/xkbcomp/parser.y" + { + (yyval.file) = XkbFileCreate((yyvsp[-5].file_type), (yyvsp[-4].str), (yyvsp[-2].anyList).head, (yyvsp[-6].mapFlags)); + } +#line 2036 "xkbcommon@sha/parser.c" + break; + + case 12: +#line 287 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYCODES; } +#line 2042 "xkbcommon@sha/parser.c" + break; + + case 13: +#line 288 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_TYPES; } +#line 2048 "xkbcommon@sha/parser.c" + break; + + case 14: +#line 289 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_COMPAT; } +#line 2054 "xkbcommon@sha/parser.c" + break; + + case 15: +#line 290 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_SYMBOLS; } +#line 2060 "xkbcommon@sha/parser.c" + break; + + case 16: +#line 291 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_GEOMETRY; } +#line 2066 "xkbcommon@sha/parser.c" + break; + + case 17: +#line 294 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = (yyvsp[0].mapFlags); } +#line 2072 "xkbcommon@sha/parser.c" + break; + + case 18: +#line 295 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = 0; } +#line 2078 "xkbcommon@sha/parser.c" + break; + + case 19: +#line 298 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = ((yyvsp[-1].mapFlags) | (yyvsp[0].mapFlags)); } +#line 2084 "xkbcommon@sha/parser.c" + break; + + case 20: +#line 299 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = (yyvsp[0].mapFlags); } +#line 2090 "xkbcommon@sha/parser.c" + break; + + case 21: +#line 302 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_PARTIAL; } +#line 2096 "xkbcommon@sha/parser.c" + break; + + case 22: +#line 303 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_DEFAULT; } +#line 2102 "xkbcommon@sha/parser.c" + break; + + case 23: +#line 304 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_HIDDEN; } +#line 2108 "xkbcommon@sha/parser.c" + break; + + case 24: +#line 305 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_ALPHANUMERIC; } +#line 2114 "xkbcommon@sha/parser.c" + break; + + case 25: +#line 306 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_MODIFIER; } +#line 2120 "xkbcommon@sha/parser.c" + break; + + case 26: +#line 307 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_KEYPAD; } +#line 2126 "xkbcommon@sha/parser.c" + break; + + case 27: +#line 308 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_FN; } +#line 2132 "xkbcommon@sha/parser.c" + break; + + case 28: +#line 309 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_ALTGR; } +#line 2138 "xkbcommon@sha/parser.c" + break; + + case 29: +#line 313 "../src/xkbcomp/parser.y" + { + if ((yyvsp[0].any)) { + if ((yyvsp[-1].anyList).head) { + (yyval.anyList).head = (yyvsp[-1].anyList).head; (yyvsp[-1].anyList).last->next = (yyvsp[0].any); (yyval.anyList).last = (yyvsp[0].any); + } else { + (yyval.anyList).head = (yyval.anyList).last = (yyvsp[0].any); + } + } + } +#line 2152 "xkbcommon@sha/parser.c" + break; + + case 30: +#line 328 "../src/xkbcomp/parser.y" + { + for (VModDef *vmod = (yyvsp[0].vmodList).head; vmod; vmod = (VModDef *) vmod->common.next) + vmod->merge = (yyvsp[-1].merge); + if ((yyvsp[-2].anyList).head) { + (yyval.anyList).head = (yyvsp[-2].anyList).head; (yyvsp[-2].anyList).last->next = &(yyvsp[0].vmodList).head->common; (yyval.anyList).last = &(yyvsp[0].vmodList).last->common; + } else { + (yyval.anyList).head = &(yyvsp[0].vmodList).head->common; (yyval.anyList).last = &(yyvsp[0].vmodList).last->common; + } + } +#line 2166 "xkbcommon@sha/parser.c" + break; + + case 31: +#line 337 "../src/xkbcomp/parser.y" + { (yyval.anyList).head = (yyval.anyList).last = NULL; } +#line 2172 "xkbcommon@sha/parser.c" + break; + + case 32: +#line 341 "../src/xkbcomp/parser.y" + { + (yyvsp[0].var)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].var); + } +#line 2181 "xkbcommon@sha/parser.c" + break; + + case 33: +#line 347 "../src/xkbcomp/parser.y" + { + (yyvsp[0].interp)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].interp); + } +#line 2190 "xkbcommon@sha/parser.c" + break; + + case 34: +#line 352 "../src/xkbcomp/parser.y" + { + (yyvsp[0].keyCode)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].keyCode); + } +#line 2199 "xkbcommon@sha/parser.c" + break; + + case 35: +#line 357 "../src/xkbcomp/parser.y" + { + (yyvsp[0].keyAlias)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].keyAlias); + } +#line 2208 "xkbcommon@sha/parser.c" + break; + + case 36: +#line 362 "../src/xkbcomp/parser.y" + { + (yyvsp[0].keyType)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].keyType); + } +#line 2217 "xkbcommon@sha/parser.c" + break; + + case 37: +#line 367 "../src/xkbcomp/parser.y" + { + (yyvsp[0].syms)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].syms); + } +#line 2226 "xkbcommon@sha/parser.c" + break; + + case 38: +#line 372 "../src/xkbcomp/parser.y" + { + (yyvsp[0].modMask)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].modMask); + } +#line 2235 "xkbcommon@sha/parser.c" + break; + + case 39: +#line 377 "../src/xkbcomp/parser.y" + { + (yyvsp[0].groupCompat)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].groupCompat); + } +#line 2244 "xkbcommon@sha/parser.c" + break; + + case 40: +#line 382 "../src/xkbcomp/parser.y" + { + (yyvsp[0].ledMap)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].ledMap); + } +#line 2253 "xkbcommon@sha/parser.c" + break; + + case 41: +#line 387 "../src/xkbcomp/parser.y" + { + (yyvsp[0].ledName)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].ledName); + } +#line 2262 "xkbcommon@sha/parser.c" + break; + + case 42: +#line 391 "../src/xkbcomp/parser.y" + { (yyval.any) = NULL; } +#line 2268 "xkbcommon@sha/parser.c" + break; + + case 43: +#line 392 "../src/xkbcomp/parser.y" + { (yyval.any) = NULL; } +#line 2274 "xkbcommon@sha/parser.c" + break; + + case 44: +#line 393 "../src/xkbcomp/parser.y" + { (yyval.any) = NULL; } +#line 2280 "xkbcommon@sha/parser.c" + break; + + case 45: +#line 395 "../src/xkbcomp/parser.y" + { + (yyval.any) = (ParseCommon *) IncludeCreate(param->ctx, (yyvsp[0].str), (yyvsp[-1].merge)); + free((yyvsp[0].str)); + } +#line 2289 "xkbcommon@sha/parser.c" + break; + + case 46: +#line 402 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate((yyvsp[-3].expr), (yyvsp[-1].expr)); } +#line 2295 "xkbcommon@sha/parser.c" + break; + + case 47: +#line 404 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[-1].atom), true); } +#line 2301 "xkbcommon@sha/parser.c" + break; + + case 48: +#line 406 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[-1].atom), false); } +#line 2307 "xkbcommon@sha/parser.c" + break; + + case 49: +#line 410 "../src/xkbcomp/parser.y" + { (yyval.keyCode) = KeycodeCreate((yyvsp[-3].atom), (yyvsp[-1].num)); } +#line 2313 "xkbcommon@sha/parser.c" + break; + + case 50: +#line 414 "../src/xkbcomp/parser.y" + { (yyval.keyAlias) = KeyAliasCreate((yyvsp[-3].atom), (yyvsp[-1].atom)); } +#line 2319 "xkbcommon@sha/parser.c" + break; + + case 51: +#line 418 "../src/xkbcomp/parser.y" + { (yyval.vmodList) = (yyvsp[-1].vmodList); } +#line 2325 "xkbcommon@sha/parser.c" + break; + + case 52: +#line 422 "../src/xkbcomp/parser.y" + { (yyval.vmodList).head = (yyvsp[-2].vmodList).head; (yyval.vmodList).last->common.next = &(yyvsp[0].vmod)->common; (yyval.vmodList).last = (yyvsp[0].vmod); } +#line 2331 "xkbcommon@sha/parser.c" + break; + + case 53: +#line 424 "../src/xkbcomp/parser.y" + { (yyval.vmodList).head = (yyval.vmodList).last = (yyvsp[0].vmod); } +#line 2337 "xkbcommon@sha/parser.c" + break; + + case 54: +#line 428 "../src/xkbcomp/parser.y" + { (yyval.vmod) = VModCreate((yyvsp[0].atom), NULL); } +#line 2343 "xkbcommon@sha/parser.c" + break; + + case 55: +#line 430 "../src/xkbcomp/parser.y" + { (yyval.vmod) = VModCreate((yyvsp[-2].atom), (yyvsp[0].expr)); } +#line 2349 "xkbcommon@sha/parser.c" + break; + + case 56: +#line 436 "../src/xkbcomp/parser.y" + { (yyvsp[-4].interp)->def = (yyvsp[-2].varList).head; (yyval.interp) = (yyvsp[-4].interp); } +#line 2355 "xkbcommon@sha/parser.c" + break; + + case 57: +#line 440 "../src/xkbcomp/parser.y" + { (yyval.interp) = InterpCreate((yyvsp[-2].keysym), (yyvsp[0].expr)); } +#line 2361 "xkbcommon@sha/parser.c" + break; + + case 58: +#line 442 "../src/xkbcomp/parser.y" + { (yyval.interp) = InterpCreate((yyvsp[0].keysym), NULL); } +#line 2367 "xkbcommon@sha/parser.c" + break; + + case 59: +#line 446 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyvsp[-1].varList).head; (yyval.varList).last->common.next = &(yyvsp[0].var)->common; (yyval.varList).last = (yyvsp[0].var); } +#line 2373 "xkbcommon@sha/parser.c" + break; + + case 60: +#line 448 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyval.varList).last = (yyvsp[0].var); } +#line 2379 "xkbcommon@sha/parser.c" + break; + + case 61: +#line 454 "../src/xkbcomp/parser.y" + { (yyval.keyType) = KeyTypeCreate((yyvsp[-4].atom), (yyvsp[-2].varList).head); } +#line 2385 "xkbcommon@sha/parser.c" + break; + + case 62: +#line 460 "../src/xkbcomp/parser.y" + { (yyval.syms) = SymbolsCreate((yyvsp[-4].atom), (yyvsp[-2].varList).head); } +#line 2391 "xkbcommon@sha/parser.c" + break; + + case 63: +#line 464 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyvsp[-2].varList).head; (yyval.varList).last->common.next = &(yyvsp[0].var)->common; (yyval.varList).last = (yyvsp[0].var); } +#line 2397 "xkbcommon@sha/parser.c" + break; + + case 64: +#line 466 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyval.varList).last = (yyvsp[0].var); } +#line 2403 "xkbcommon@sha/parser.c" + break; + + case 65: +#line 467 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyval.varList).last = NULL; } +#line 2409 "xkbcommon@sha/parser.c" + break; + + case 66: +#line 470 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate((yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2415 "xkbcommon@sha/parser.c" + break; + + case 67: +#line 471 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate((yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2421 "xkbcommon@sha/parser.c" + break; + + case 68: +#line 472 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[0].atom), true); } +#line 2427 "xkbcommon@sha/parser.c" + break; + + case 69: +#line 473 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[0].atom), false); } +#line 2433 "xkbcommon@sha/parser.c" + break; + + case 70: +#line 474 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate(NULL, (yyvsp[0].expr)); } +#line 2439 "xkbcommon@sha/parser.c" + break; + + case 71: +#line 478 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2445 "xkbcommon@sha/parser.c" + break; + + case 72: +#line 480 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateActionList((yyvsp[-1].exprList).head); } +#line 2451 "xkbcommon@sha/parser.c" + break; + + case 73: +#line 484 "../src/xkbcomp/parser.y" + { (yyval.groupCompat) = GroupCompatCreate((yyvsp[-3].num), (yyvsp[-1].expr)); } +#line 2457 "xkbcommon@sha/parser.c" + break; + + case 74: +#line 488 "../src/xkbcomp/parser.y" + { (yyval.modMask) = ModMapCreate((yyvsp[-4].atom), (yyvsp[-2].exprList).head); } +#line 2463 "xkbcommon@sha/parser.c" + break; + + case 75: +#line 492 "../src/xkbcomp/parser.y" + { (yyval.ledMap) = LedMapCreate((yyvsp[-4].atom), (yyvsp[-2].varList).head); } +#line 2469 "xkbcommon@sha/parser.c" + break; + + case 76: +#line 496 "../src/xkbcomp/parser.y" + { (yyval.ledName) = LedNameCreate((yyvsp[-3].num), (yyvsp[-1].expr), false); } +#line 2475 "xkbcommon@sha/parser.c" + break; + + case 77: +#line 498 "../src/xkbcomp/parser.y" + { (yyval.ledName) = LedNameCreate((yyvsp[-3].num), (yyvsp[-1].expr), true); } +#line 2481 "xkbcommon@sha/parser.c" + break; + + case 78: +#line 502 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2487 "xkbcommon@sha/parser.c" + break; + + case 79: +#line 504 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-2].expr); (yyval.geom) = NULL; } +#line 2493 "xkbcommon@sha/parser.c" + break; + + case 80: +#line 508 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2499 "xkbcommon@sha/parser.c" + break; + + case 81: +#line 511 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL;} +#line 2505 "xkbcommon@sha/parser.c" + break; + + case 82: +#line 512 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2511 "xkbcommon@sha/parser.c" + break; + + case 83: +#line 516 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2517 "xkbcommon@sha/parser.c" + break; + + case 84: +#line 518 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].var)); (yyval.geom) = NULL; } +#line 2523 "xkbcommon@sha/parser.c" + break; + + case 85: +#line 520 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2529 "xkbcommon@sha/parser.c" + break; + + case 86: +#line 522 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].ledMap)); (yyval.geom) = NULL; } +#line 2535 "xkbcommon@sha/parser.c" + break; + + case 87: +#line 524 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2541 "xkbcommon@sha/parser.c" + break; + + case 88: +#line 527 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL;} +#line 2547 "xkbcommon@sha/parser.c" + break; + + case 89: +#line 528 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2553 "xkbcommon@sha/parser.c" + break; + + case 90: +#line 531 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2559 "xkbcommon@sha/parser.c" + break; + + case 91: +#line 533 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].var)); (yyval.geom) = NULL; } +#line 2565 "xkbcommon@sha/parser.c" + break; + + case 92: +#line 536 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2571 "xkbcommon@sha/parser.c" + break; + + case 93: +#line 537 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2577 "xkbcommon@sha/parser.c" + break; + + case 94: +#line 541 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2583 "xkbcommon@sha/parser.c" + break; + + case 95: +#line 543 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[-1].exprList).head); (yyval.geom) = NULL; } +#line 2589 "xkbcommon@sha/parser.c" + break; + + case 96: +#line 547 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2595 "xkbcommon@sha/parser.c" + break; + + case 97: +#line 550 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2601 "xkbcommon@sha/parser.c" + break; + + case 98: +#line 551 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2607 "xkbcommon@sha/parser.c" + break; + + case 99: +#line 554 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2613 "xkbcommon@sha/parser.c" + break; + + case 100: +#line 558 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL;} +#line 2619 "xkbcommon@sha/parser.c" + break; + + case 101: +#line 560 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2625 "xkbcommon@sha/parser.c" + break; + + case 102: +#line 564 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-1].expr); (yyval.geom) = NULL; } +#line 2631 "xkbcommon@sha/parser.c" + break; + + case 103: +#line 566 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-1].expr); (yyval.geom) = NULL; } +#line 2637 "xkbcommon@sha/parser.c" + break; + + case 104: +#line 568 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].expr)); (yyval.geom) = NULL; } +#line 2643 "xkbcommon@sha/parser.c" + break; + + case 105: +#line 572 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-2].expr); (void) (yyvsp[0].expr); (yyval.expr) = NULL; } +#line 2649 "xkbcommon@sha/parser.c" + break; + + case 106: +#line 574 "../src/xkbcomp/parser.y" + { (void) (yyvsp[0].expr); (yyval.expr) = NULL; } +#line 2655 "xkbcommon@sha/parser.c" + break; + + case 107: +#line 578 "../src/xkbcomp/parser.y" + { (yyval.expr) = NULL; } +#line 2661 "xkbcommon@sha/parser.c" + break; + + case 108: +#line 582 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[-2].varList).head); (yyval.geom) = NULL; } +#line 2667 "xkbcommon@sha/parser.c" + break; + + case 109: +#line 585 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2673 "xkbcommon@sha/parser.c" + break; + + case 110: +#line 586 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2679 "xkbcommon@sha/parser.c" + break; + + case 111: +#line 587 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2685 "xkbcommon@sha/parser.c" + break; + + case 112: +#line 588 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2691 "xkbcommon@sha/parser.c" + break; + + case 113: +#line 591 "../src/xkbcomp/parser.y" + { (yyval.atom) = (yyvsp[0].atom); } +#line 2697 "xkbcommon@sha/parser.c" + break; + + case 114: +#line 592 "../src/xkbcomp/parser.y" + { (yyval.atom) = (yyvsp[0].atom); } +#line 2703 "xkbcommon@sha/parser.c" + break; + + case 115: +#line 596 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "action"); } +#line 2709 "xkbcommon@sha/parser.c" + break; + + case 116: +#line 598 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "interpret"); } +#line 2715 "xkbcommon@sha/parser.c" + break; + + case 117: +#line 600 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "type"); } +#line 2721 "xkbcommon@sha/parser.c" + break; + + case 118: +#line 602 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "key"); } +#line 2727 "xkbcommon@sha/parser.c" + break; + + case 119: +#line 604 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "group"); } +#line 2733 "xkbcommon@sha/parser.c" + break; + + case 120: +#line 606 "../src/xkbcomp/parser.y" + {(yyval.atom) = xkb_atom_intern_literal(param->ctx, "modifier_map");} +#line 2739 "xkbcommon@sha/parser.c" + break; + + case 121: +#line 608 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "indicator"); } +#line 2745 "xkbcommon@sha/parser.c" + break; + + case 122: +#line 610 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "shape"); } +#line 2751 "xkbcommon@sha/parser.c" + break; + + case 123: +#line 612 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "row"); } +#line 2757 "xkbcommon@sha/parser.c" + break; + + case 124: +#line 614 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "section"); } +#line 2763 "xkbcommon@sha/parser.c" + break; + + case 125: +#line 616 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "text"); } +#line 2769 "xkbcommon@sha/parser.c" + break; + + case 126: +#line 619 "../src/xkbcomp/parser.y" + { (yyval.merge) = (yyvsp[0].merge); } +#line 2775 "xkbcommon@sha/parser.c" + break; + + case 127: +#line 620 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_DEFAULT; } +#line 2781 "xkbcommon@sha/parser.c" + break; + + case 128: +#line 623 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_DEFAULT; } +#line 2787 "xkbcommon@sha/parser.c" + break; + + case 129: +#line 624 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_AUGMENT; } +#line 2793 "xkbcommon@sha/parser.c" + break; + + case 130: +#line 625 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_OVERRIDE; } +#line 2799 "xkbcommon@sha/parser.c" + break; + + case 131: +#line 626 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_REPLACE; } +#line 2805 "xkbcommon@sha/parser.c" + break; + + case 132: +#line 628 "../src/xkbcomp/parser.y" + { + /* + * This used to be MERGE_ALT_FORM. This functionality was + * unused and has been removed. + */ + (yyval.merge) = MERGE_DEFAULT; + } +#line 2817 "xkbcommon@sha/parser.c" + break; + + case 133: +#line 637 "../src/xkbcomp/parser.y" + { (yyval.exprList) = (yyvsp[0].exprList); } +#line 2823 "xkbcommon@sha/parser.c" + break; + + case 134: +#line 638 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyval.exprList).last = NULL; } +#line 2829 "xkbcommon@sha/parser.c" + break; + + case 135: +#line 642 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyvsp[-2].exprList).head; (yyval.exprList).last->common.next = &(yyvsp[0].expr)->common; (yyval.exprList).last = (yyvsp[0].expr); } +#line 2835 "xkbcommon@sha/parser.c" + break; + + case 136: +#line 644 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyval.exprList).last = (yyvsp[0].expr); } +#line 2841 "xkbcommon@sha/parser.c" + break; + + case 137: +#line 648 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_DIVIDE, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2847 "xkbcommon@sha/parser.c" + break; + + case 138: +#line 650 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_ADD, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2853 "xkbcommon@sha/parser.c" + break; + + case 139: +#line 652 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_SUBTRACT, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2859 "xkbcommon@sha/parser.c" + break; + + case 140: +#line 654 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_MULTIPLY, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2865 "xkbcommon@sha/parser.c" + break; + + case 141: +#line 656 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_ASSIGN, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2871 "xkbcommon@sha/parser.c" + break; + + case 142: +#line 658 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2877 "xkbcommon@sha/parser.c" + break; + + case 143: +#line 662 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_NEGATE, (yyvsp[0].expr)->expr.value_type, (yyvsp[0].expr)); } +#line 2883 "xkbcommon@sha/parser.c" + break; + + case 144: +#line 664 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_UNARY_PLUS, (yyvsp[0].expr)->expr.value_type, (yyvsp[0].expr)); } +#line 2889 "xkbcommon@sha/parser.c" + break; + + case 145: +#line 666 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_NOT, EXPR_TYPE_BOOLEAN, (yyvsp[0].expr)); } +#line 2895 "xkbcommon@sha/parser.c" + break; + + case 146: +#line 668 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_INVERT, (yyvsp[0].expr)->expr.value_type, (yyvsp[0].expr)); } +#line 2901 "xkbcommon@sha/parser.c" + break; + + case 147: +#line 670 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2907 "xkbcommon@sha/parser.c" + break; + + case 148: +#line 672 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateAction((yyvsp[-3].atom), (yyvsp[-1].exprList).head); } +#line 2913 "xkbcommon@sha/parser.c" + break; + + case 149: +#line 674 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2919 "xkbcommon@sha/parser.c" + break; + + case 150: +#line 676 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2925 "xkbcommon@sha/parser.c" + break; + + case 151: +#line 680 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyvsp[-2].exprList).head; (yyval.exprList).last->common.next = &(yyvsp[0].expr)->common; (yyval.exprList).last = (yyvsp[0].expr); } +#line 2931 "xkbcommon@sha/parser.c" + break; + + case 152: +#line 682 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyval.exprList).last = (yyvsp[0].expr); } +#line 2937 "xkbcommon@sha/parser.c" + break; + + case 153: +#line 686 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateAction((yyvsp[-3].atom), (yyvsp[-1].exprList).head); } +#line 2943 "xkbcommon@sha/parser.c" + break; + + case 154: +#line 690 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateIdent((yyvsp[0].atom)); } +#line 2949 "xkbcommon@sha/parser.c" + break; + + case 155: +#line 692 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateFieldRef((yyvsp[-2].atom), (yyvsp[0].atom)); } +#line 2955 "xkbcommon@sha/parser.c" + break; + + case 156: +#line 694 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateArrayRef(XKB_ATOM_NONE, (yyvsp[-3].atom), (yyvsp[-1].expr)); } +#line 2961 "xkbcommon@sha/parser.c" + break; + + case 157: +#line 696 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateArrayRef((yyvsp[-5].atom), (yyvsp[-3].atom), (yyvsp[-1].expr)); } +#line 2967 "xkbcommon@sha/parser.c" + break; + + case 158: +#line 700 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateString((yyvsp[0].atom)); } +#line 2973 "xkbcommon@sha/parser.c" + break; + + case 159: +#line 702 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateInteger((yyvsp[0].num)); } +#line 2979 "xkbcommon@sha/parser.c" + break; + + case 160: +#line 704 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateFloat(/* Discard $1 */); } +#line 2985 "xkbcommon@sha/parser.c" + break; + + case 161: +#line 706 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateKeyName((yyvsp[0].atom)); } +#line 2991 "xkbcommon@sha/parser.c" + break; + + case 162: +#line 709 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2997 "xkbcommon@sha/parser.c" + break; + + case 163: +#line 710 "../src/xkbcomp/parser.y" + { (yyval.expr) = NULL; } +#line 3003 "xkbcommon@sha/parser.c" + break; + + case 164: +#line 714 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprAppendKeysymList((yyvsp[-2].expr), (yyvsp[0].keysym)); } +#line 3009 "xkbcommon@sha/parser.c" + break; + + case 165: +#line 716 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprAppendMultiKeysymList((yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 3015 "xkbcommon@sha/parser.c" + break; + + case 166: +#line 718 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateKeysymList((yyvsp[0].keysym)); } +#line 3021 "xkbcommon@sha/parser.c" + break; + + case 167: +#line 720 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateMultiKeysymList((yyvsp[0].expr)); } +#line 3027 "xkbcommon@sha/parser.c" + break; + + case 168: +#line 724 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 3033 "xkbcommon@sha/parser.c" + break; + + case 169: +#line 728 "../src/xkbcomp/parser.y" + { + if (!resolve_keysym((yyvsp[0].str), &(yyval.keysym))) + parser_warn(param, "unrecognized keysym \"%s\"", (yyvsp[0].str)); + free((yyvsp[0].str)); + } +#line 3043 "xkbcommon@sha/parser.c" + break; + + case 170: +#line 733 "../src/xkbcomp/parser.y" + { (yyval.keysym) = XKB_KEY_section; } +#line 3049 "xkbcommon@sha/parser.c" + break; + + case 171: +#line 735 "../src/xkbcomp/parser.y" + { + if ((yyvsp[0].num) < 0) { + parser_warn(param, "unrecognized keysym \"%" PRId64 "\"", (yyvsp[0].num)); + (yyval.keysym) = XKB_KEY_NoSymbol; + } + else if ((yyvsp[0].num) < 10) { /* XKB_KEY_0 .. XKB_KEY_9 */ + (yyval.keysym) = XKB_KEY_0 + (xkb_keysym_t) (yyvsp[0].num); + } + else { + char buf[32]; + snprintf(buf, sizeof(buf), "0x%"PRIx64, (yyvsp[0].num)); + if (!resolve_keysym(buf, &(yyval.keysym))) { + parser_warn(param, "unrecognized keysym \"%s\"", buf); + (yyval.keysym) = XKB_KEY_NoSymbol; + } + } + } +#line 3071 "xkbcommon@sha/parser.c" + break; + + case 172: +#line 754 "../src/xkbcomp/parser.y" + { (yyval.num) = -(yyvsp[0].num); } +#line 3077 "xkbcommon@sha/parser.c" + break; + + case 173: +#line 755 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3083 "xkbcommon@sha/parser.c" + break; + + case 174: +#line 758 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3089 "xkbcommon@sha/parser.c" + break; + + case 175: +#line 759 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3095 "xkbcommon@sha/parser.c" + break; + + case 176: +#line 762 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 3101 "xkbcommon@sha/parser.c" + break; + + case 177: +#line 765 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3107 "xkbcommon@sha/parser.c" + break; + + case 178: +#line 768 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3113 "xkbcommon@sha/parser.c" + break; + + case 179: +#line 771 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern(param->ctx, (yyvsp[0].str), strlen((yyvsp[0].str))); free((yyvsp[0].str)); } +#line 3119 "xkbcommon@sha/parser.c" + break; + + case 180: +#line 772 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "default"); } +#line 3125 "xkbcommon@sha/parser.c" + break; + + case 181: +#line 775 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern(param->ctx, (yyvsp[0].str), strlen((yyvsp[0].str))); free((yyvsp[0].str)); } +#line 3131 "xkbcommon@sha/parser.c" + break; + + case 182: +#line 778 "../src/xkbcomp/parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 3137 "xkbcommon@sha/parser.c" + break; + + case 183: +#line 779 "../src/xkbcomp/parser.y" + { (yyval.str) = NULL; } +#line 3143 "xkbcommon@sha/parser.c" + break; + + case 184: +#line 782 "../src/xkbcomp/parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 3149 "xkbcommon@sha/parser.c" + break; + + +#line 3153 "xkbcommon@sha/parser.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (param, YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= END_OF_FILE) + { + /* Return failure if at end of input. */ + if (yychar == END_OF_FILE) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, param); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, param); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (param, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, param); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, param); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 785 "../src/xkbcomp/parser.y" + + +XkbFile * +parse(struct xkb_context *ctx, struct scanner *scanner, const char *map) +{ + int ret; + XkbFile *first = NULL; + struct parser_param param = { + .scanner = scanner, + .ctx = ctx, + .rtrn = NULL, + .more_maps = false, + }; + + /* + * If we got a specific map, we look for it exclusively and return + * immediately upon finding it. Otherwise, we need to get the + * default map. If we find a map marked as default, we return it + * immediately. If there are no maps marked as default, we return + * the first map in the file. + */ + + while ((ret = yyparse(¶m)) == 0 && param.more_maps) { + if (map) { + if (streq_not_null(map, param.rtrn->name)) + return param.rtrn; + else + FreeXkbFile(param.rtrn); + } + else { + if (param.rtrn->flags & MAP_IS_DEFAULT) { + FreeXkbFile(first); + return param.rtrn; + } + else if (!first) { + first = param.rtrn; + } + else { + FreeXkbFile(param.rtrn); + } + } + param.rtrn = NULL; + } + + if (ret != 0) { + FreeXkbFile(first); + return NULL; + } + + if (first) + log_vrb(ctx, 5, + "No map in include statement, but \"%s\" contains several; " + "Using first defined map, \"%s\"\n", + scanner->file_name, first->name); + + return first; +} diff --git a/src/xkbcomp/parser.h b/src/xkbcomp/parser.h new file mode 100644 index 0000000..e158fa4 --- /dev/null +++ b/src/xkbcomp/parser.h @@ -0,0 +1,169 @@ +/* A Bison parser, made by GNU Bison 3.6.3. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +# define YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int _xkbcommon_debug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + END_OF_FILE = 0, /* END_OF_FILE */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + ERROR_TOK = 255, /* ERROR_TOK */ + XKB_KEYMAP = 1, /* XKB_KEYMAP */ + XKB_KEYCODES = 2, /* XKB_KEYCODES */ + XKB_TYPES = 3, /* XKB_TYPES */ + XKB_SYMBOLS = 4, /* XKB_SYMBOLS */ + XKB_COMPATMAP = 5, /* XKB_COMPATMAP */ + XKB_GEOMETRY = 6, /* XKB_GEOMETRY */ + XKB_SEMANTICS = 7, /* XKB_SEMANTICS */ + XKB_LAYOUT = 8, /* XKB_LAYOUT */ + INCLUDE = 10, /* INCLUDE */ + OVERRIDE = 11, /* OVERRIDE */ + AUGMENT = 12, /* AUGMENT */ + REPLACE = 13, /* REPLACE */ + ALTERNATE = 14, /* ALTERNATE */ + VIRTUAL_MODS = 20, /* VIRTUAL_MODS */ + TYPE = 21, /* TYPE */ + INTERPRET = 22, /* INTERPRET */ + ACTION_TOK = 23, /* ACTION_TOK */ + KEY = 24, /* KEY */ + ALIAS = 25, /* ALIAS */ + GROUP = 26, /* GROUP */ + MODIFIER_MAP = 27, /* MODIFIER_MAP */ + INDICATOR = 28, /* INDICATOR */ + SHAPE = 29, /* SHAPE */ + KEYS = 30, /* KEYS */ + ROW = 31, /* ROW */ + SECTION = 32, /* SECTION */ + OVERLAY = 33, /* OVERLAY */ + TEXT = 34, /* TEXT */ + OUTLINE = 35, /* OUTLINE */ + SOLID = 36, /* SOLID */ + LOGO = 37, /* LOGO */ + VIRTUAL = 38, /* VIRTUAL */ + EQUALS = 40, /* EQUALS */ + PLUS = 41, /* PLUS */ + MINUS = 42, /* MINUS */ + DIVIDE = 43, /* DIVIDE */ + TIMES = 44, /* TIMES */ + OBRACE = 45, /* OBRACE */ + CBRACE = 46, /* CBRACE */ + OPAREN = 47, /* OPAREN */ + CPAREN = 48, /* CPAREN */ + OBRACKET = 49, /* OBRACKET */ + CBRACKET = 50, /* CBRACKET */ + DOT = 51, /* DOT */ + COMMA = 52, /* COMMA */ + SEMI = 53, /* SEMI */ + EXCLAM = 54, /* EXCLAM */ + INVERT = 55, /* INVERT */ + STRING = 60, /* STRING */ + INTEGER = 61, /* INTEGER */ + FLOAT = 62, /* FLOAT */ + IDENT = 63, /* IDENT */ + KEYNAME = 64, /* KEYNAME */ + PARTIAL = 70, /* PARTIAL */ + DEFAULT = 71, /* DEFAULT */ + HIDDEN = 72, /* HIDDEN */ + ALPHANUMERIC_KEYS = 73, /* ALPHANUMERIC_KEYS */ + MODIFIER_KEYS = 74, /* MODIFIER_KEYS */ + KEYPAD_KEYS = 75, /* KEYPAD_KEYS */ + FUNCTION_KEYS = 76, /* FUNCTION_KEYS */ + ALTERNATE_GROUP = 77 /* ALTERNATE_GROUP */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 164 "../src/xkbcomp/parser.y" + + int64_t num; + enum xkb_file_type file_type; + char *str; + xkb_atom_t atom; + enum merge_mode merge; + enum xkb_map_flags mapFlags; + xkb_keysym_t keysym; + ParseCommon *any; + struct { ParseCommon *head; ParseCommon *last; } anyList; + ExprDef *expr; + struct { ExprDef *head; ExprDef *last; } exprList; + VarDef *var; + struct { VarDef *head; VarDef *last; } varList; + VModDef *vmod; + struct { VModDef *head; VModDef *last; } vmodList; + InterpDef *interp; + KeyTypeDef *keyType; + SymbolsDef *syms; + ModMapDef *modMask; + GroupCompatDef *groupCompat; + LedMapDef *ledMap; + LedNameDef *ledName; + KeycodeDef *keyCode; + KeyAliasDef *keyAlias; + void *geom; + XkbFile *file; + struct { XkbFile *head; XkbFile *last; } fileList; + +#line 158 "xkbcommon@sha/parser.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int _xkbcommon_parse (struct parser_param *param); + +#endif /* !YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED */ diff --git a/src/xkbcomp/parser.y b/src/xkbcomp/parser.y new file mode 100644 index 0000000..b1e4188 --- /dev/null +++ b/src/xkbcomp/parser.y @@ -0,0 +1,841 @@ +/************************************************************ + Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + + Permission to use, copy, modify, and distribute this + software and its documentation for any purpose and without + fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright + notice and this permission notice appear in supporting + documentation, and that the name of Silicon Graphics not be + used in advertising or publicity pertaining to distribution + of the software without specific prior written permission. + Silicon Graphics makes no representation about the suitability + of this software for any purpose. It is provided "as is" + without any express or implied warranty. + + SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ********************************************************/ + +/* + * The parser should work with reasonably recent versions of either + * bison or byacc. So if you make changes, try to make sure it works + * in both! + */ + +%{ +#include "config.h" + +#include "xkbcomp/xkbcomp-priv.h" +#include "xkbcomp/ast-build.h" +#include "xkbcomp/parser-priv.h" +#include "scanner-utils.h" + +struct parser_param { + struct xkb_context *ctx; + struct scanner *scanner; + XkbFile *rtrn; + bool more_maps; +}; + +#define parser_err(param, fmt, ...) \ + scanner_err((param)->scanner, fmt, ##__VA_ARGS__) + +#define parser_warn(param, fmt, ...) \ + scanner_warn((param)->scanner, fmt, ##__VA_ARGS__) + +static void +_xkbcommon_error(struct parser_param *param, const char *msg) +{ + parser_err(param, "%s", msg); +} + +static bool +resolve_keysym(const char *name, xkb_keysym_t *sym_rtrn) +{ + xkb_keysym_t sym; + + if (!name || istreq(name, "any") || istreq(name, "nosymbol")) { + *sym_rtrn = XKB_KEY_NoSymbol; + return true; + } + + if (istreq(name, "none") || istreq(name, "voidsymbol")) { + *sym_rtrn = XKB_KEY_VoidSymbol; + return true; + } + + sym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); + if (sym != XKB_KEY_NoSymbol) { + *sym_rtrn = sym; + return true; + } + + return false; +} + +#define param_scanner param->scanner +%} + +%pure-parser +%lex-param { struct scanner *param_scanner } +%parse-param { struct parser_param *param } + +%token + END_OF_FILE 0 + ERROR_TOK 255 + XKB_KEYMAP 1 + XKB_KEYCODES 2 + XKB_TYPES 3 + XKB_SYMBOLS 4 + XKB_COMPATMAP 5 + XKB_GEOMETRY 6 + XKB_SEMANTICS 7 + XKB_LAYOUT 8 + INCLUDE 10 + OVERRIDE 11 + AUGMENT 12 + REPLACE 13 + ALTERNATE 14 + VIRTUAL_MODS 20 + TYPE 21 + INTERPRET 22 + ACTION_TOK 23 + KEY 24 + ALIAS 25 + GROUP 26 + MODIFIER_MAP 27 + INDICATOR 28 + SHAPE 29 + KEYS 30 + ROW 31 + SECTION 32 + OVERLAY 33 + TEXT 34 + OUTLINE 35 + SOLID 36 + LOGO 37 + VIRTUAL 38 + EQUALS 40 + PLUS 41 + MINUS 42 + DIVIDE 43 + TIMES 44 + OBRACE 45 + CBRACE 46 + OPAREN 47 + CPAREN 48 + OBRACKET 49 + CBRACKET 50 + DOT 51 + COMMA 52 + SEMI 53 + EXCLAM 54 + INVERT 55 + STRING 60 + INTEGER 61 + FLOAT 62 + IDENT 63 + KEYNAME 64 + PARTIAL 70 + DEFAULT 71 + HIDDEN 72 + ALPHANUMERIC_KEYS 73 + MODIFIER_KEYS 74 + KEYPAD_KEYS 75 + FUNCTION_KEYS 76 + ALTERNATE_GROUP 77 + +%right EQUALS +%left PLUS MINUS +%left TIMES DIVIDE +%left EXCLAM INVERT +%left OPAREN + +%start XkbFile + +%union { + int64_t num; + enum xkb_file_type file_type; + char *str; + xkb_atom_t atom; + enum merge_mode merge; + enum xkb_map_flags mapFlags; + xkb_keysym_t keysym; + ParseCommon *any; + struct { ParseCommon *head; ParseCommon *last; } anyList; + ExprDef *expr; + struct { ExprDef *head; ExprDef *last; } exprList; + VarDef *var; + struct { VarDef *head; VarDef *last; } varList; + VModDef *vmod; + struct { VModDef *head; VModDef *last; } vmodList; + InterpDef *interp; + KeyTypeDef *keyType; + SymbolsDef *syms; + ModMapDef *modMask; + GroupCompatDef *groupCompat; + LedMapDef *ledMap; + LedNameDef *ledName; + KeycodeDef *keyCode; + KeyAliasDef *keyAlias; + void *geom; + XkbFile *file; + struct { XkbFile *head; XkbFile *last; } fileList; +} + +%type <num> INTEGER FLOAT +%type <str> IDENT STRING +%type <atom> KEYNAME +%type <num> KeyCode Number Integer Float SignedNumber DoodadType +%type <merge> MergeMode OptMergeMode +%type <file_type> XkbCompositeType FileType +%type <mapFlags> Flag Flags OptFlags +%type <str> MapName OptMapName +%type <atom> FieldSpec Ident Element String +%type <keysym> KeySym +%type <any> Decl +%type <anyList> DeclList +%type <expr> Expr Term Lhs Terminal ArrayInit KeySyms +%type <expr> OptKeySymList KeySymList Action Coord CoordList +%type <exprList> OptExprList ExprList ActionList +%type <var> VarDecl SymbolsVarDecl +%type <varList> VarDeclList SymbolsBody +%type <vmod> VModDef +%type <vmodList> VModDefList VModDecl +%type <interp> InterpretDecl InterpretMatch +%type <keyType> KeyTypeDecl +%type <syms> SymbolsDecl +%type <modMask> ModMapDecl +%type <groupCompat> GroupCompatDecl +%type <ledMap> LedMapDecl +%type <ledName> LedNameDecl +%type <keyCode> KeyNameDecl +%type <keyAlias> KeyAliasDecl +%type <geom> ShapeDecl SectionDecl SectionBody SectionBodyItem RowBody RowBodyItem +%type <geom> Keys Key OverlayDecl OverlayKeyList OverlayKey OutlineList OutlineInList +%type <geom> DoodadDecl +%type <file> XkbFile XkbMapConfig +%type <fileList> XkbMapConfigList +%type <file> XkbCompositeMap + +%destructor { FreeStmt((ParseCommon *) $$); } + <any> <expr> <var> <vmod> <interp> <keyType> <syms> <modMask> <groupCompat> + <ledMap> <ledName> <keyCode> <keyAlias> +%destructor { FreeStmt((ParseCommon *) $$.head); } + <anyList> <exprList> <varList> <vmodList> +/* The destructor also runs on the start symbol when the parser *succeeds*. + * The `if` here catches this case. */ +%destructor { if (!param->rtrn) FreeXkbFile($$); } <file> +%destructor { FreeXkbFile($$.head); } <fileList> +%destructor { free($$); } <str> + +%% + +/* + * An actual file may contain more than one map. However, if we do things + * in the normal yacc way, i.e. aggregate all of the maps into a list and + * let the caller find the map it wants, we end up scanning and parsing a + * lot of unneeded maps (in the end we always just need one). + * Instead of doing that, we make yyparse return one map at a time, and + * then call it repeatedly until we find the map we need. Once we find it, + * we don't need to parse everything that follows in the file. + * This does mean that if we e.g. always use the first map, the file may + * contain complete garbage after that. But it's worth it. + */ + +XkbFile : XkbCompositeMap + { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; } + | XkbMapConfig + { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; YYACCEPT; } + | END_OF_FILE + { $$ = param->rtrn = NULL; param->more_maps = false; } + ; + +XkbCompositeMap : OptFlags XkbCompositeType OptMapName OBRACE + XkbMapConfigList + CBRACE SEMI + { $$ = XkbFileCreate($2, $3, (ParseCommon *) $5.head, $1); } + ; + +XkbCompositeType: XKB_KEYMAP { $$ = FILE_TYPE_KEYMAP; } + | XKB_SEMANTICS { $$ = FILE_TYPE_KEYMAP; } + | XKB_LAYOUT { $$ = FILE_TYPE_KEYMAP; } + ; + +XkbMapConfigList : XkbMapConfigList XkbMapConfig + { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; } + | XkbMapConfig + { $$.head = $$.last = $1; } + ; + +XkbMapConfig : OptFlags FileType OptMapName OBRACE + DeclList + CBRACE SEMI + { + $$ = XkbFileCreate($2, $3, $5.head, $1); + } + ; + +FileType : XKB_KEYCODES { $$ = FILE_TYPE_KEYCODES; } + | XKB_TYPES { $$ = FILE_TYPE_TYPES; } + | XKB_COMPATMAP { $$ = FILE_TYPE_COMPAT; } + | XKB_SYMBOLS { $$ = FILE_TYPE_SYMBOLS; } + | XKB_GEOMETRY { $$ = FILE_TYPE_GEOMETRY; } + ; + +OptFlags : Flags { $$ = $1; } + | { $$ = 0; } + ; + +Flags : Flags Flag { $$ = ($1 | $2); } + | Flag { $$ = $1; } + ; + +Flag : PARTIAL { $$ = MAP_IS_PARTIAL; } + | DEFAULT { $$ = MAP_IS_DEFAULT; } + | HIDDEN { $$ = MAP_IS_HIDDEN; } + | ALPHANUMERIC_KEYS { $$ = MAP_HAS_ALPHANUMERIC; } + | MODIFIER_KEYS { $$ = MAP_HAS_MODIFIER; } + | KEYPAD_KEYS { $$ = MAP_HAS_KEYPAD; } + | FUNCTION_KEYS { $$ = MAP_HAS_FN; } + | ALTERNATE_GROUP { $$ = MAP_IS_ALTGR; } + ; + +DeclList : DeclList Decl + { + if ($2) { + if ($1.head) { + $$.head = $1.head; $1.last->next = $2; $$.last = $2; + } else { + $$.head = $$.last = $2; + } + } + } + /* + * VModDecl is "inlined" directly into DeclList, i.e. + * each VModDef in the VModDecl is a separate Decl in + * the File. + */ + | DeclList OptMergeMode VModDecl + { + for (VModDef *vmod = $3.head; vmod; vmod = (VModDef *) vmod->common.next) + vmod->merge = $2; + if ($1.head) { + $$.head = $1.head; $1.last->next = &$3.head->common; $$.last = &$3.last->common; + } else { + $$.head = &$3.head->common; $$.last = &$3.last->common; + } + } + | { $$.head = $$.last = NULL; } + ; + +Decl : OptMergeMode VarDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + /* OptMergeMode VModDecl - see above. */ + | OptMergeMode InterpretDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode KeyNameDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode KeyAliasDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode KeyTypeDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode SymbolsDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode ModMapDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode GroupCompatDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode LedMapDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode LedNameDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode ShapeDecl { $$ = NULL; } + | OptMergeMode SectionDecl { $$ = NULL; } + | OptMergeMode DoodadDecl { $$ = NULL; } + | MergeMode STRING + { + $$ = (ParseCommon *) IncludeCreate(param->ctx, $2, $1); + free($2); + } + ; + +VarDecl : Lhs EQUALS Expr SEMI + { $$ = VarCreate($1, $3); } + | Ident SEMI + { $$ = BoolVarCreate($1, true); } + | EXCLAM Ident SEMI + { $$ = BoolVarCreate($2, false); } + ; + +KeyNameDecl : KEYNAME EQUALS KeyCode SEMI + { $$ = KeycodeCreate($1, $3); } + ; + +KeyAliasDecl : ALIAS KEYNAME EQUALS KEYNAME SEMI + { $$ = KeyAliasCreate($2, $4); } + ; + +VModDecl : VIRTUAL_MODS VModDefList SEMI + { $$ = $2; } + ; + +VModDefList : VModDefList COMMA VModDef + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | VModDef + { $$.head = $$.last = $1; } + ; + +VModDef : Ident + { $$ = VModCreate($1, NULL); } + | Ident EQUALS Expr + { $$ = VModCreate($1, $3); } + ; + +InterpretDecl : INTERPRET InterpretMatch OBRACE + VarDeclList + CBRACE SEMI + { $2->def = $4.head; $$ = $2; } + ; + +InterpretMatch : KeySym PLUS Expr + { $$ = InterpCreate($1, $3); } + | KeySym + { $$ = InterpCreate($1, NULL); } + ; + +VarDeclList : VarDeclList VarDecl + { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; } + | VarDecl + { $$.head = $$.last = $1; } + ; + +KeyTypeDecl : TYPE String OBRACE + VarDeclList + CBRACE SEMI + { $$ = KeyTypeCreate($2, $4.head); } + ; + +SymbolsDecl : KEY KEYNAME OBRACE + SymbolsBody + CBRACE SEMI + { $$ = SymbolsCreate($2, $4.head); } + ; + +SymbolsBody : SymbolsBody COMMA SymbolsVarDecl + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | SymbolsVarDecl + { $$.head = $$.last = $1; } + | { $$.head = $$.last = NULL; } + ; + +SymbolsVarDecl : Lhs EQUALS Expr { $$ = VarCreate($1, $3); } + | Lhs EQUALS ArrayInit { $$ = VarCreate($1, $3); } + | Ident { $$ = BoolVarCreate($1, true); } + | EXCLAM Ident { $$ = BoolVarCreate($2, false); } + | ArrayInit { $$ = VarCreate(NULL, $1); } + ; + +ArrayInit : OBRACKET OptKeySymList CBRACKET + { $$ = $2; } + | OBRACKET ActionList CBRACKET + { $$ = ExprCreateActionList($2.head); } + ; + +GroupCompatDecl : GROUP Integer EQUALS Expr SEMI + { $$ = GroupCompatCreate($2, $4); } + ; + +ModMapDecl : MODIFIER_MAP Ident OBRACE ExprList CBRACE SEMI + { $$ = ModMapCreate($2, $4.head); } + ; + +LedMapDecl: INDICATOR String OBRACE VarDeclList CBRACE SEMI + { $$ = LedMapCreate($2, $4.head); } + ; + +LedNameDecl: INDICATOR Integer EQUALS Expr SEMI + { $$ = LedNameCreate($2, $4, false); } + | VIRTUAL INDICATOR Integer EQUALS Expr SEMI + { $$ = LedNameCreate($3, $5, true); } + ; + +ShapeDecl : SHAPE String OBRACE OutlineList CBRACE SEMI + { $$ = NULL; } + | SHAPE String OBRACE CoordList CBRACE SEMI + { (void) $4; $$ = NULL; } + ; + +SectionDecl : SECTION String OBRACE SectionBody CBRACE SEMI + { $$ = NULL; } + ; + +SectionBody : SectionBody SectionBodyItem { $$ = NULL;} + | SectionBodyItem { $$ = NULL; } + ; + +SectionBodyItem : ROW OBRACE RowBody CBRACE SEMI + { $$ = NULL; } + | VarDecl + { FreeStmt((ParseCommon *) $1); $$ = NULL; } + | DoodadDecl + { $$ = NULL; } + | LedMapDecl + { FreeStmt((ParseCommon *) $1); $$ = NULL; } + | OverlayDecl + { $$ = NULL; } + ; + +RowBody : RowBody RowBodyItem { $$ = NULL;} + | RowBodyItem { $$ = NULL; } + ; + +RowBodyItem : KEYS OBRACE Keys CBRACE SEMI { $$ = NULL; } + | VarDecl + { FreeStmt((ParseCommon *) $1); $$ = NULL; } + ; + +Keys : Keys COMMA Key { $$ = NULL; } + | Key { $$ = NULL; } + ; + +Key : KEYNAME + { $$ = NULL; } + | OBRACE ExprList CBRACE + { FreeStmt((ParseCommon *) $2.head); $$ = NULL; } + ; + +OverlayDecl : OVERLAY String OBRACE OverlayKeyList CBRACE SEMI + { $$ = NULL; } + ; + +OverlayKeyList : OverlayKeyList COMMA OverlayKey { $$ = NULL; } + | OverlayKey { $$ = NULL; } + ; + +OverlayKey : KEYNAME EQUALS KEYNAME { $$ = NULL; } + ; + +OutlineList : OutlineList COMMA OutlineInList + { $$ = NULL;} + | OutlineInList + { $$ = NULL; } + ; + +OutlineInList : OBRACE CoordList CBRACE + { (void) $2; $$ = NULL; } + | Ident EQUALS OBRACE CoordList CBRACE + { (void) $4; $$ = NULL; } + | Ident EQUALS Expr + { FreeStmt((ParseCommon *) $3); $$ = NULL; } + ; + +CoordList : CoordList COMMA Coord + { (void) $1; (void) $3; $$ = NULL; } + | Coord + { (void) $1; $$ = NULL; } + ; + +Coord : OBRACKET SignedNumber COMMA SignedNumber CBRACKET + { $$ = NULL; } + ; + +DoodadDecl : DoodadType String OBRACE VarDeclList CBRACE SEMI + { FreeStmt((ParseCommon *) $4.head); $$ = NULL; } + ; + +DoodadType : TEXT { $$ = 0; } + | OUTLINE { $$ = 0; } + | SOLID { $$ = 0; } + | LOGO { $$ = 0; } + ; + +FieldSpec : Ident { $$ = $1; } + | Element { $$ = $1; } + ; + +Element : ACTION_TOK + { $$ = xkb_atom_intern_literal(param->ctx, "action"); } + | INTERPRET + { $$ = xkb_atom_intern_literal(param->ctx, "interpret"); } + | TYPE + { $$ = xkb_atom_intern_literal(param->ctx, "type"); } + | KEY + { $$ = xkb_atom_intern_literal(param->ctx, "key"); } + | GROUP + { $$ = xkb_atom_intern_literal(param->ctx, "group"); } + | MODIFIER_MAP + {$$ = xkb_atom_intern_literal(param->ctx, "modifier_map");} + | INDICATOR + { $$ = xkb_atom_intern_literal(param->ctx, "indicator"); } + | SHAPE + { $$ = xkb_atom_intern_literal(param->ctx, "shape"); } + | ROW + { $$ = xkb_atom_intern_literal(param->ctx, "row"); } + | SECTION + { $$ = xkb_atom_intern_literal(param->ctx, "section"); } + | TEXT + { $$ = xkb_atom_intern_literal(param->ctx, "text"); } + ; + +OptMergeMode : MergeMode { $$ = $1; } + | { $$ = MERGE_DEFAULT; } + ; + +MergeMode : INCLUDE { $$ = MERGE_DEFAULT; } + | AUGMENT { $$ = MERGE_AUGMENT; } + | OVERRIDE { $$ = MERGE_OVERRIDE; } + | REPLACE { $$ = MERGE_REPLACE; } + | ALTERNATE + { + /* + * This used to be MERGE_ALT_FORM. This functionality was + * unused and has been removed. + */ + $$ = MERGE_DEFAULT; + } + ; + +OptExprList : ExprList { $$ = $1; } + | { $$.head = $$.last = NULL; } + ; + +ExprList : ExprList COMMA Expr + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | Expr + { $$.head = $$.last = $1; } + ; + +Expr : Expr DIVIDE Expr + { $$ = ExprCreateBinary(EXPR_DIVIDE, $1, $3); } + | Expr PLUS Expr + { $$ = ExprCreateBinary(EXPR_ADD, $1, $3); } + | Expr MINUS Expr + { $$ = ExprCreateBinary(EXPR_SUBTRACT, $1, $3); } + | Expr TIMES Expr + { $$ = ExprCreateBinary(EXPR_MULTIPLY, $1, $3); } + | Lhs EQUALS Expr + { $$ = ExprCreateBinary(EXPR_ASSIGN, $1, $3); } + | Term + { $$ = $1; } + ; + +Term : MINUS Term + { $$ = ExprCreateUnary(EXPR_NEGATE, $2->expr.value_type, $2); } + | PLUS Term + { $$ = ExprCreateUnary(EXPR_UNARY_PLUS, $2->expr.value_type, $2); } + | EXCLAM Term + { $$ = ExprCreateUnary(EXPR_NOT, EXPR_TYPE_BOOLEAN, $2); } + | INVERT Term + { $$ = ExprCreateUnary(EXPR_INVERT, $2->expr.value_type, $2); } + | Lhs + { $$ = $1; } + | FieldSpec OPAREN OptExprList CPAREN %prec OPAREN + { $$ = ExprCreateAction($1, $3.head); } + | Terminal + { $$ = $1; } + | OPAREN Expr CPAREN + { $$ = $2; } + ; + +ActionList : ActionList COMMA Action + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | Action + { $$.head = $$.last = $1; } + ; + +Action : FieldSpec OPAREN OptExprList CPAREN + { $$ = ExprCreateAction($1, $3.head); } + ; + +Lhs : FieldSpec + { $$ = ExprCreateIdent($1); } + | FieldSpec DOT FieldSpec + { $$ = ExprCreateFieldRef($1, $3); } + | FieldSpec OBRACKET Expr CBRACKET + { $$ = ExprCreateArrayRef(XKB_ATOM_NONE, $1, $3); } + | FieldSpec DOT FieldSpec OBRACKET Expr CBRACKET + { $$ = ExprCreateArrayRef($1, $3, $5); } + ; + +Terminal : String + { $$ = ExprCreateString($1); } + | Integer + { $$ = ExprCreateInteger($1); } + | Float + { $$ = ExprCreateFloat(/* Discard $1 */); } + | KEYNAME + { $$ = ExprCreateKeyName($1); } + ; + +OptKeySymList : KeySymList { $$ = $1; } + | { $$ = NULL; } + ; + +KeySymList : KeySymList COMMA KeySym + { $$ = ExprAppendKeysymList($1, $3); } + | KeySymList COMMA KeySyms + { $$ = ExprAppendMultiKeysymList($1, $3); } + | KeySym + { $$ = ExprCreateKeysymList($1); } + | KeySyms + { $$ = ExprCreateMultiKeysymList($1); } + ; + +KeySyms : OBRACE KeySymList CBRACE + { $$ = $2; } + ; + +KeySym : IDENT + { + if (!resolve_keysym($1, &$$)) + parser_warn(param, "unrecognized keysym \"%s\"", $1); + free($1); + } + | SECTION { $$ = XKB_KEY_section; } + | Integer + { + if ($1 < 0) { + parser_warn(param, "unrecognized keysym \"%" PRId64 "\"", $1); + $$ = XKB_KEY_NoSymbol; + } + else if ($1 < 10) { /* XKB_KEY_0 .. XKB_KEY_9 */ + $$ = XKB_KEY_0 + (xkb_keysym_t) $1; + } + else { + char buf[32]; + snprintf(buf, sizeof(buf), "0x%"PRIx64, $1); + if (!resolve_keysym(buf, &$$)) { + parser_warn(param, "unrecognized keysym \"%s\"", buf); + $$ = XKB_KEY_NoSymbol; + } + } + } + ; + +SignedNumber : MINUS Number { $$ = -$2; } + | Number { $$ = $1; } + ; + +Number : FLOAT { $$ = $1; } + | INTEGER { $$ = $1; } + ; + +Float : FLOAT { $$ = 0; } + ; + +Integer : INTEGER { $$ = $1; } + ; + +KeyCode : INTEGER { $$ = $1; } + ; + +Ident : IDENT { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); } + | DEFAULT { $$ = xkb_atom_intern_literal(param->ctx, "default"); } + ; + +String : STRING { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); } + ; + +OptMapName : MapName { $$ = $1; } + | { $$ = NULL; } + ; + +MapName : STRING { $$ = $1; } + ; + +%% + +XkbFile * +parse(struct xkb_context *ctx, struct scanner *scanner, const char *map) +{ + int ret; + XkbFile *first = NULL; + struct parser_param param = { + .scanner = scanner, + .ctx = ctx, + .rtrn = NULL, + .more_maps = false, + }; + + /* + * If we got a specific map, we look for it exclusively and return + * immediately upon finding it. Otherwise, we need to get the + * default map. If we find a map marked as default, we return it + * immediately. If there are no maps marked as default, we return + * the first map in the file. + */ + + while ((ret = yyparse(¶m)) == 0 && param.more_maps) { + if (map) { + if (streq_not_null(map, param.rtrn->name)) + return param.rtrn; + else + FreeXkbFile(param.rtrn); + } + else { + if (param.rtrn->flags & MAP_IS_DEFAULT) { + FreeXkbFile(first); + return param.rtrn; + } + else if (!first) { + first = param.rtrn; + } + else { + FreeXkbFile(param.rtrn); + } + } + param.rtrn = NULL; + } + + if (ret != 0) { + FreeXkbFile(first); + return NULL; + } + + if (first) + log_vrb(ctx, 5, + "No map in include statement, but \"%s\" contains several; " + "Using first defined map, \"%s\"\n", + scanner->file_name, first->name); + + return first; +} diff --git a/src/xkbcomp/rules.c b/src/xkbcomp/rules.c new file mode 100644 index 0000000..099500a --- /dev/null +++ b/src/xkbcomp/rules.c @@ -0,0 +1,1163 @@ +/************************************************************ + * Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "rules.h" +#include "include.h" +#include "scanner-utils.h" + +#define MAX_INCLUDE_DEPTH 5 + +/* Scanner / Lexer */ + +/* Values returned with some tokens, like yylval. */ +union lvalue { + struct sval string; +}; + +enum rules_token { + TOK_END_OF_FILE = 0, + TOK_END_OF_LINE, + TOK_IDENTIFIER, + TOK_GROUP_NAME, + TOK_BANG, + TOK_EQUALS, + TOK_STAR, + TOK_INCLUDE, + TOK_ERROR +}; + +static inline bool +is_ident(char ch) +{ + return is_graph(ch) && ch != '\\'; +} + +static enum rules_token +lex(struct scanner *s, union lvalue *val) +{ +skip_more_whitespace_and_comments: + /* Skip spaces. */ + while (chr(s, ' ') || chr(s, '\t') || chr(s, '\r')); + + /* Skip comments. */ + if (lit(s, "//")) { + skip_to_eol(s); + } + + /* New line. */ + if (eol(s)) { + while (eol(s)) next(s); + return TOK_END_OF_LINE; + } + + /* Escaped line continuation. */ + if (chr(s, '\\')) { + /* Optional \r. */ + chr(s, '\r'); + if (!eol(s)) { + scanner_err(s, "illegal new line escape; must appear at end of line"); + return TOK_ERROR; + } + next(s); + goto skip_more_whitespace_and_comments; + } + + /* See if we're done. */ + if (eof(s)) return TOK_END_OF_FILE; + + /* New token. */ + s->token_line = s->line; + s->token_column = s->column; + + /* Operators and punctuation. */ + if (chr(s, '!')) return TOK_BANG; + if (chr(s, '=')) return TOK_EQUALS; + if (chr(s, '*')) return TOK_STAR; + + /* Group name. */ + if (chr(s, '$')) { + val->string.start = s->s + s->pos; + val->string.len = 0; + while (is_ident(peek(s))) { + next(s); + val->string.len++; + } + if (val->string.len == 0) { + scanner_err(s, "unexpected character after \'$\'; expected name"); + return TOK_ERROR; + } + return TOK_GROUP_NAME; + } + + /* Include statement. */ + if (lit(s, "include")) + return TOK_INCLUDE; + + /* Identifier. */ + if (is_ident(peek(s))) { + val->string.start = s->s + s->pos; + val->string.len = 0; + while (is_ident(peek(s))) { + next(s); + val->string.len++; + } + return TOK_IDENTIFIER; + } + + scanner_err(s, "unrecognized token"); + return TOK_ERROR; +} + +/***====================================================================***/ + +enum rules_mlvo { + MLVO_MODEL, + MLVO_LAYOUT, + MLVO_VARIANT, + MLVO_OPTION, + _MLVO_NUM_ENTRIES +}; + +#define SVAL_LIT(literal) { literal, sizeof(literal) - 1 } + +static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = { + [MLVO_MODEL] = SVAL_LIT("model"), + [MLVO_LAYOUT] = SVAL_LIT("layout"), + [MLVO_VARIANT] = SVAL_LIT("variant"), + [MLVO_OPTION] = SVAL_LIT("option"), +}; + +enum rules_kccgst { + KCCGST_KEYCODES, + KCCGST_TYPES, + KCCGST_COMPAT, + KCCGST_SYMBOLS, + KCCGST_GEOMETRY, + _KCCGST_NUM_ENTRIES +}; + +static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = { + [KCCGST_KEYCODES] = SVAL_LIT("keycodes"), + [KCCGST_TYPES] = SVAL_LIT("types"), + [KCCGST_COMPAT] = SVAL_LIT("compat"), + [KCCGST_SYMBOLS] = SVAL_LIT("symbols"), + [KCCGST_GEOMETRY] = SVAL_LIT("geometry"), +}; + +/* We use this to keep score whether an mlvo was matched or not; if not, + * we warn the user that his preference was ignored. */ +struct matched_sval { + struct sval sval; + bool matched; +}; +typedef darray(struct matched_sval) darray_matched_sval; + +/* + * A broken-down version of xkb_rule_names (without the rules, + * obviously). + */ +struct rule_names { + struct matched_sval model; + darray_matched_sval layouts; + darray_matched_sval variants; + darray_matched_sval options; +}; + +struct group { + struct sval name; + darray_sval elements; +}; + +struct mapping { + int mlvo_at_pos[_MLVO_NUM_ENTRIES]; + unsigned int num_mlvo; + unsigned int defined_mlvo_mask; + xkb_layout_index_t layout_idx, variant_idx; + int kccgst_at_pos[_KCCGST_NUM_ENTRIES]; + unsigned int num_kccgst; + unsigned int defined_kccgst_mask; + bool skip; +}; + +enum mlvo_match_type { + MLVO_MATCH_NORMAL = 0, + MLVO_MATCH_WILDCARD, + MLVO_MATCH_GROUP, +}; + +struct rule { + struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES]; + enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES]; + unsigned int num_mlvo_values; + struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES]; + unsigned int num_kccgst_values; + bool skip; +}; + +/* + * This is the main object used to match a given RMLVO against a rules + * file and aggragate the results in a KcCGST. It goes through a simple + * matching state machine, with tokens as transitions (see + * matcher_match()). + */ +struct matcher { + struct xkb_context *ctx; + /* Input.*/ + struct rule_names rmlvo; + union lvalue val; + darray(struct group) groups; + /* Current mapping. */ + struct mapping mapping; + /* Current rule. */ + struct rule rule; + /* Output. */ + darray_char kccgst[_KCCGST_NUM_ENTRIES]; +}; + +static struct sval +strip_spaces(struct sval v) +{ + while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; } + while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--; + return v; +} + +static darray_matched_sval +split_comma_separated_mlvo(const char *s) +{ + darray_matched_sval arr = darray_new(); + + /* + * Make sure the array returned by this function always includes at + * least one value, e.g. "" -> { "" } and "," -> { "", "" }. + */ + + if (!s) { + struct matched_sval val = { .sval = { NULL, 0 } }; + darray_append(arr, val); + return arr; + } + + while (true) { + struct matched_sval val = { .sval = { s, 0 } }; + while (*s != '\0' && *s != ',') { s++; val.sval.len++; } + val.sval = strip_spaces(val.sval); + darray_append(arr, val); + if (*s == '\0') break; + if (*s == ',') s++; + } + + return arr; +} + +static struct matcher * +matcher_new(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo) +{ + struct matcher *m = calloc(1, sizeof(*m)); + if (!m) + return NULL; + + m->ctx = ctx; + m->rmlvo.model.sval.start = rmlvo->model; + m->rmlvo.model.sval.len = strlen_safe(rmlvo->model); + m->rmlvo.layouts = split_comma_separated_mlvo(rmlvo->layout); + m->rmlvo.variants = split_comma_separated_mlvo(rmlvo->variant); + m->rmlvo.options = split_comma_separated_mlvo(rmlvo->options); + + return m; +} + +static void +matcher_free(struct matcher *m) +{ + struct group *group; + if (!m) + return; + darray_free(m->rmlvo.layouts); + darray_free(m->rmlvo.variants); + darray_free(m->rmlvo.options); + darray_foreach(group, m->groups) + darray_free(group->elements); + for (int i = 0; i < _KCCGST_NUM_ENTRIES; i++) + darray_free(m->kccgst[i]); + darray_free(m->groups); + free(m); +} + +static void +matcher_group_start_new(struct matcher *m, struct sval name) +{ + struct group group = { .name = name, .elements = darray_new() }; + darray_append(m->groups, group); +} + +static void +matcher_group_add_element(struct matcher *m, struct scanner *s, + struct sval element) +{ + darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements, + element); +} + +static bool +read_rules_file(struct xkb_context *ctx, + struct matcher *matcher, + unsigned include_depth, + FILE *file, + const char *path); + +static void +matcher_include(struct matcher *m, struct scanner *parent_scanner, + unsigned include_depth, + struct sval inc) +{ + struct scanner s; /* parses the !include value */ + FILE *file; + + scanner_init(&s, m->ctx, inc.start, inc.len, + parent_scanner->file_name, NULL); + s.token_line = parent_scanner->token_line; + s.token_column = parent_scanner->token_column; + s.buf_pos = 0; + + if (include_depth >= MAX_INCLUDE_DEPTH) { + scanner_err(&s, "maximum include depth (%d) exceeded; maybe there is an include loop?", + MAX_INCLUDE_DEPTH); + return; + } + + while (!eof(&s) && !eol(&s)) { + if (chr(&s, '%')) { + if (chr(&s, '%')) { + buf_append(&s, '%'); + } + else if (chr(&s, 'H')) { + const char *home = secure_getenv("HOME"); + if (!home) { + scanner_err(&s, "%%H was used in an include statement, but the HOME environment variable is not set"); + return; + } + if (!buf_appends(&s, home)) { + scanner_err(&s, "include path after expanding %%H is too long"); + return; + } + } + else if (chr(&s, 'S')) { + const char *default_root = xkb_context_include_path_get_system_path(m->ctx); + if (!buf_appends(&s, default_root) || !buf_appends(&s, "/rules")) { + scanner_err(&s, "include path after expanding %%S is too long"); + return; + } + } + else if (chr(&s, 'E')) { + const char *default_root = xkb_context_include_path_get_extra_path(m->ctx); + if (!buf_appends(&s, default_root) || !buf_appends(&s, "/rules")) { + scanner_err(&s, "include path after expanding %%E is too long"); + return; + } + } + else { + scanner_err(&s, "unknown %% format (%c) in include statement", peek(&s)); + return; + } + } + else { + buf_append(&s, next(&s)); + } + } + if (!buf_append(&s, '\0')) { + scanner_err(&s, "include path is too long"); + return; + } + + file = fopen(s.buf, "rb"); + if (file) { + bool ret = read_rules_file(m->ctx, m, include_depth + 1, file, s.buf); + if (!ret) + log_err(m->ctx, "No components returned from included XKB rules \"%s\"\n", s.buf); + fclose(file); + } else { + log_err(m->ctx, "Failed to open included XKB rules \"%s\"\n", s.buf); + } +} + +static void +matcher_mapping_start_new(struct matcher *m) +{ + for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++) + m->mapping.mlvo_at_pos[i] = -1; + for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++) + m->mapping.kccgst_at_pos[i] = -1; + m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID; + m->mapping.num_mlvo = m->mapping.num_kccgst = 0; + m->mapping.defined_mlvo_mask = 0; + m->mapping.defined_kccgst_mask = 0; + m->mapping.skip = false; +} + +static int +extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out) +{ + /* This function is pretty stupid, but works for now. */ + *out = XKB_LAYOUT_INVALID; + if (max_len < 3) + return -1; + if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']') + return -1; + if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS) + return -1; + /* To zero-based index. */ + *out = s[1] - '0' - 1; + return 3; +} + +static void +matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s, + struct sval ident) +{ + enum rules_mlvo mlvo; + struct sval mlvo_sval; + + for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) { + mlvo_sval = rules_mlvo_svals[mlvo]; + + if (svaleq_prefix(mlvo_sval, ident)) + break; + } + + /* Not found. */ + if (mlvo >= _MLVO_NUM_ENTRIES) { + scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set", + ident.len, ident.start); + m->mapping.skip = true; + return; + } + + if (m->mapping.defined_mlvo_mask & (1u << mlvo)) { + scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set", + mlvo_sval.len, mlvo_sval.start); + m->mapping.skip = true; + return; + } + + /* If there are leftovers still, it must be an index. */ + if (mlvo_sval.len < ident.len) { + xkb_layout_index_t idx; + int consumed = extract_layout_index(ident.start + mlvo_sval.len, + ident.len - mlvo_sval.len, &idx); + if ((int) (ident.len - mlvo_sval.len) != consumed) { + scanner_err(s, "invalid mapping: \"%.*s\" may only be followed by a valid group index; ignoring rule set", + mlvo_sval.len, mlvo_sval.start); + m->mapping.skip = true; + return; + } + + if (mlvo == MLVO_LAYOUT) { + m->mapping.layout_idx = idx; + } + else if (mlvo == MLVO_VARIANT) { + m->mapping.variant_idx = idx; + } + else { + scanner_err(s, "invalid mapping: \"%.*s\" cannot be followed by a group index; ignoring rule set", + mlvo_sval.len, mlvo_sval.start); + m->mapping.skip = true; + return; + } + } + + m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo; + m->mapping.defined_mlvo_mask |= 1u << mlvo; + m->mapping.num_mlvo++; +} + +static void +matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident) +{ + enum rules_kccgst kccgst; + struct sval kccgst_sval; + + for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) { + kccgst_sval = rules_kccgst_svals[kccgst]; + + if (svaleq(rules_kccgst_svals[kccgst], ident)) + break; + } + + /* Not found. */ + if (kccgst >= _KCCGST_NUM_ENTRIES) { + scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set", + ident.len, ident.start); + m->mapping.skip = true; + return; + } + + if (m->mapping.defined_kccgst_mask & (1u << kccgst)) { + scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set", + kccgst_sval.len, kccgst_sval.start); + m->mapping.skip = true; + return; + } + + m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst; + m->mapping.defined_kccgst_mask |= 1u << kccgst; + m->mapping.num_kccgst++; +} + +static void +matcher_mapping_verify(struct matcher *m, struct scanner *s) +{ + if (m->mapping.num_mlvo == 0) { + scanner_err(s, "invalid mapping: must have at least one value on the left hand side; ignoring rule set"); + goto skip; + } + + if (m->mapping.num_kccgst == 0) { + scanner_err(s, "invalid mapping: must have at least one value on the right hand side; ignoring rule set"); + goto skip; + } + + /* + * This following is very stupid, but this is how it works. + * See the "Notes" section in the overview above. + */ + + if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) { + if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) { + if (darray_size(m->rmlvo.layouts) > 1) + goto skip; + } + else { + if (darray_size(m->rmlvo.layouts) == 1 || + m->mapping.layout_idx >= darray_size(m->rmlvo.layouts)) + goto skip; + } + } + + if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) { + if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) { + if (darray_size(m->rmlvo.variants) > 1) + goto skip; + } + else { + if (darray_size(m->rmlvo.variants) == 1 || + m->mapping.variant_idx >= darray_size(m->rmlvo.variants)) + goto skip; + } + } + + return; + +skip: + m->mapping.skip = true; +} + +static void +matcher_rule_start_new(struct matcher *m) +{ + memset(&m->rule, 0, sizeof(m->rule)); + m->rule.skip = m->mapping.skip; +} + +static void +matcher_rule_set_mlvo_common(struct matcher *m, struct scanner *s, + struct sval ident, + enum mlvo_match_type match_type) +{ + if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) { + scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule"); + m->rule.skip = true; + return; + } + m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type; + m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident; + m->rule.num_mlvo_values++; +} + +static void +matcher_rule_set_mlvo_wildcard(struct matcher *m, struct scanner *s) +{ + struct sval dummy = { NULL, 0 }; + matcher_rule_set_mlvo_common(m, s, dummy, MLVO_MATCH_WILDCARD); +} + +static void +matcher_rule_set_mlvo_group(struct matcher *m, struct scanner *s, + struct sval ident) +{ + matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_GROUP); +} + +static void +matcher_rule_set_mlvo(struct matcher *m, struct scanner *s, + struct sval ident) +{ + matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_NORMAL); +} + +static void +matcher_rule_set_kccgst(struct matcher *m, struct scanner *s, + struct sval ident) +{ + if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) { + scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule"); + m->rule.skip = true; + return; + } + m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident; + m->rule.num_kccgst_values++; +} + +static bool +match_group(struct matcher *m, struct sval group_name, struct sval to) +{ + struct group *group; + struct sval *element; + bool found = false; + + darray_foreach(group, m->groups) { + if (svaleq(group->name, group_name)) { + found = true; + break; + } + } + + if (!found) { + /* + * rules/evdev intentionally uses some undeclared group names + * in rules (e.g. commented group definitions which may be + * uncommented if needed). So we continue silently. + */ + return false; + } + + darray_foreach(element, group->elements) + if (svaleq(to, *element)) + return true; + + return false; +} + +static bool +match_value(struct matcher *m, struct sval val, struct sval to, + enum mlvo_match_type match_type) +{ + if (match_type == MLVO_MATCH_WILDCARD) + return true; + if (match_type == MLVO_MATCH_GROUP) + return match_group(m, val, to); + return svaleq(val, to); +} + +static bool +match_value_and_mark(struct matcher *m, struct sval val, + struct matched_sval *to, enum mlvo_match_type match_type) +{ + bool matched = match_value(m, val, to->sval, match_type); + if (matched) + to->matched = true; + return matched; +} + +/* + * This function performs %-expansion on @value (see overview above), + * and appends the result to @to. + */ +static bool +append_expanded_kccgst_value(struct matcher *m, struct scanner *s, + darray_char *to, struct sval value) +{ + const char *str = value.start; + darray_char expanded = darray_new(); + char ch; + bool expanded_plus, to_plus; + + /* + * Some ugly hand-lexing here, but going through the scanner is more + * trouble than it's worth, and the format is ugly on its own merit. + */ + for (unsigned i = 0; i < value.len; ) { + enum rules_mlvo mlv; + xkb_layout_index_t idx; + char pfx, sfx; + struct matched_sval *expanded_value; + + /* Check if that's a start of an expansion. */ + if (str[i] != '%') { + /* Just a normal character. */ + darray_appends_nullterminate(expanded, &str[i++], 1); + continue; + } + if (++i >= value.len) goto error; + + pfx = sfx = 0; + + /* Check for prefix. */ + if (str[i] == '(' || str[i] == '+' || str[i] == '|' || + str[i] == '_' || str[i] == '-') { + pfx = str[i]; + if (str[i] == '(') sfx = ')'; + if (++i >= value.len) goto error; + } + + /* Mandatory model/layout/variant specifier. */ + switch (str[i++]) { + case 'm': mlv = MLVO_MODEL; break; + case 'l': mlv = MLVO_LAYOUT; break; + case 'v': mlv = MLVO_VARIANT; break; + default: goto error; + } + + /* Check for index. */ + idx = XKB_LAYOUT_INVALID; + if (i < value.len && str[i] == '[') { + int consumed; + + if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) { + scanner_err(s, "invalid index in %%-expansion; may only index layout or variant"); + goto error; + } + + consumed = extract_layout_index(str + i, value.len - i, &idx); + if (consumed == -1) goto error; + i += consumed; + } + + /* Check for suffix, if there supposed to be one. */ + if (sfx != 0) { + if (i >= value.len) goto error; + if (str[i++] != sfx) goto error; + } + + /* Get the expanded value. */ + expanded_value = NULL; + + if (mlv == MLVO_LAYOUT) { + if (idx != XKB_LAYOUT_INVALID && + idx < darray_size(m->rmlvo.layouts) && + darray_size(m->rmlvo.layouts) > 1) + expanded_value = &darray_item(m->rmlvo.layouts, idx); + else if (idx == XKB_LAYOUT_INVALID && + darray_size(m->rmlvo.layouts) == 1) + expanded_value = &darray_item(m->rmlvo.layouts, 0); + } + else if (mlv == MLVO_VARIANT) { + if (idx != XKB_LAYOUT_INVALID && + idx < darray_size(m->rmlvo.variants) && + darray_size(m->rmlvo.variants) > 1) + expanded_value = &darray_item(m->rmlvo.variants, idx); + else if (idx == XKB_LAYOUT_INVALID && + darray_size(m->rmlvo.variants) == 1) + expanded_value = &darray_item(m->rmlvo.variants, 0); + } + else if (mlv == MLVO_MODEL) { + expanded_value = &m->rmlvo.model; + } + + /* If we didn't get one, skip silently. */ + if (!expanded_value || expanded_value->sval.len == 0) + continue; + + if (pfx != 0) + darray_appends_nullterminate(expanded, &pfx, 1); + darray_appends_nullterminate(expanded, + expanded_value->sval.start, + expanded_value->sval.len); + if (sfx != 0) + darray_appends_nullterminate(expanded, &sfx, 1); + expanded_value->matched = true; + } + + /* + * Appending bar to foo -> foo (not an error if this happens) + * Appending +bar to foo -> foo+bar + * Appending bar to +foo -> bar+foo + * Appending +bar to +foo -> +foo+bar + */ + + ch = (darray_empty(expanded) ? '\0' : darray_item(expanded, 0)); + expanded_plus = (ch == '+' || ch == '|'); + ch = (darray_empty(*to) ? '\0' : darray_item(*to, 0)); + to_plus = (ch == '+' || ch == '|'); + + if (expanded_plus || darray_empty(*to)) + darray_appends_nullterminate(*to, expanded.item, expanded.size); + else if (to_plus) + darray_prepends_nullterminate(*to, expanded.item, expanded.size); + + darray_free(expanded); + return true; + +error: + darray_free(expanded); + scanner_err(s, "invalid %%-expansion in value; not used"); + return false; +} + +static void +matcher_rule_verify(struct matcher *m, struct scanner *s) +{ + if (m->rule.num_mlvo_values != m->mapping.num_mlvo || + m->rule.num_kccgst_values != m->mapping.num_kccgst) { + scanner_err(s, "invalid rule: must have same number of values as mapping line; ignoring rule"); + m->rule.skip = true; + } +} + +static void +matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) +{ + for (unsigned i = 0; i < m->mapping.num_mlvo; i++) { + enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i]; + struct sval value = m->rule.mlvo_value_at_pos[i]; + enum mlvo_match_type match_type = m->rule.match_type_at_pos[i]; + struct matched_sval *to; + bool matched = false; + + if (mlvo == MLVO_MODEL) { + to = &m->rmlvo.model; + matched = match_value_and_mark(m, value, to, match_type); + } + else if (mlvo == MLVO_LAYOUT) { + xkb_layout_index_t idx = m->mapping.layout_idx; + idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx); + to = &darray_item(m->rmlvo.layouts, idx); + matched = match_value_and_mark(m, value, to, match_type); + } + else if (mlvo == MLVO_VARIANT) { + xkb_layout_index_t idx = m->mapping.layout_idx; + idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx); + to = &darray_item(m->rmlvo.variants, idx); + matched = match_value_and_mark(m, value, to, match_type); + } + else if (mlvo == MLVO_OPTION) { + darray_foreach(to, m->rmlvo.options) { + matched = match_value_and_mark(m, value, to, match_type); + if (matched) + break; + } + } + + if (!matched) + return; + } + + for (unsigned i = 0; i < m->mapping.num_kccgst; i++) { + enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i]; + struct sval value = m->rule.kccgst_value_at_pos[i]; + append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value); + } + + /* + * If a rule matches in a rule set, the rest of the set should be + * skipped. However, rule sets matching against options may contain + * several legitimate rules, so they are processed entirely. + */ + if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION))) + m->mapping.skip = true; +} + +static enum rules_token +gettok(struct matcher *m, struct scanner *s) +{ + return lex(s, &m->val); +} + +static bool +matcher_match(struct matcher *m, struct scanner *s, + unsigned include_depth, + const char *string, size_t len, + const char *file_name) +{ + enum rules_token tok; + + if (!m) + return false; + +initial: + switch (tok = gettok(m, s)) { + case TOK_BANG: + goto bang; + case TOK_END_OF_LINE: + goto initial; + case TOK_END_OF_FILE: + goto finish; + default: + goto unexpected; + } + +bang: + switch (tok = gettok(m, s)) { + case TOK_GROUP_NAME: + matcher_group_start_new(m, m->val.string); + goto group_name; + case TOK_INCLUDE: + goto include_statement; + case TOK_IDENTIFIER: + matcher_mapping_start_new(m); + matcher_mapping_set_mlvo(m, s, m->val.string); + goto mapping_mlvo; + default: + goto unexpected; + } + +group_name: + switch (tok = gettok(m, s)) { + case TOK_EQUALS: + goto group_element; + default: + goto unexpected; + } + +group_element: + switch (tok = gettok(m, s)) { + case TOK_IDENTIFIER: + matcher_group_add_element(m, s, m->val.string); + goto group_element; + case TOK_END_OF_LINE: + goto initial; + default: + goto unexpected; + } + +include_statement: + switch (tok = gettok(m, s)) { + case TOK_IDENTIFIER: + matcher_include(m, s, include_depth, m->val.string); + goto initial; + default: + goto unexpected; + } + +mapping_mlvo: + switch (tok = gettok(m, s)) { + case TOK_IDENTIFIER: + if (!m->mapping.skip) + matcher_mapping_set_mlvo(m, s, m->val.string); + goto mapping_mlvo; + case TOK_EQUALS: + goto mapping_kccgst; + default: + goto unexpected; + } + +mapping_kccgst: + switch (tok = gettok(m, s)) { + case TOK_IDENTIFIER: + if (!m->mapping.skip) + matcher_mapping_set_kccgst(m, s, m->val.string); + goto mapping_kccgst; + case TOK_END_OF_LINE: + if (!m->mapping.skip) + matcher_mapping_verify(m, s); + goto rule_mlvo_first; + default: + goto unexpected; + } + +rule_mlvo_first: + switch (tok = gettok(m, s)) { + case TOK_BANG: + goto bang; + case TOK_END_OF_LINE: + goto rule_mlvo_first; + case TOK_END_OF_FILE: + goto finish; + default: + matcher_rule_start_new(m); + goto rule_mlvo_no_tok; + } + +rule_mlvo: + tok = gettok(m, s); +rule_mlvo_no_tok: + switch (tok) { + case TOK_IDENTIFIER: + if (!m->rule.skip) + matcher_rule_set_mlvo(m, s, m->val.string); + goto rule_mlvo; + case TOK_STAR: + if (!m->rule.skip) + matcher_rule_set_mlvo_wildcard(m, s); + goto rule_mlvo; + case TOK_GROUP_NAME: + if (!m->rule.skip) + matcher_rule_set_mlvo_group(m, s, m->val.string); + goto rule_mlvo; + case TOK_EQUALS: + goto rule_kccgst; + default: + goto unexpected; + } + +rule_kccgst: + switch (tok = gettok(m, s)) { + case TOK_IDENTIFIER: + if (!m->rule.skip) + matcher_rule_set_kccgst(m, s, m->val.string); + goto rule_kccgst; + case TOK_END_OF_LINE: + if (!m->rule.skip) + matcher_rule_verify(m, s); + if (!m->rule.skip) + matcher_rule_apply_if_matches(m, s); + goto rule_mlvo_first; + default: + goto unexpected; + } + +unexpected: + switch (tok) { + case TOK_ERROR: + goto error; + default: + goto state_error; + } + +finish: + return true; + +state_error: + scanner_err(s, "unexpected token"); +error: + return false; +} + +static bool +read_rules_file(struct xkb_context *ctx, + struct matcher *matcher, + unsigned include_depth, + FILE *file, + const char *path) +{ + bool ret = false; + char *string; + size_t size; + struct scanner scanner; + + ret = map_file(file, &string, &size); + if (!ret) { + log_err(ctx, "Couldn't read rules file \"%s\": %s\n", + path, strerror(errno)); + goto out; + } + + scanner_init(&scanner, matcher->ctx, string, size, path, NULL); + + ret = matcher_match(matcher, &scanner, include_depth, string, size, path); + + unmap_file(string, size); +out: + return ret; +} + +bool +xkb_components_from_rules(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo, + struct xkb_component_names *out) +{ + bool ret = false; + FILE *file; + char *path = NULL; + struct matcher *matcher = NULL; + struct matched_sval *mval; + unsigned int offset = 0; + + file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path, &offset); + if (!file) + goto err_out; + + matcher = matcher_new(ctx, rmlvo); + + ret = read_rules_file(ctx, matcher, 0, file, path); + if (!ret || + darray_empty(matcher->kccgst[KCCGST_KEYCODES]) || + darray_empty(matcher->kccgst[KCCGST_TYPES]) || + darray_empty(matcher->kccgst[KCCGST_COMPAT]) || + /* darray_empty(matcher->kccgst[KCCGST_GEOMETRY]) || */ + darray_empty(matcher->kccgst[KCCGST_SYMBOLS])) { + log_err(ctx, "No components returned from XKB rules \"%s\"\n", path); + ret = false; + goto err_out; + } + + darray_steal(matcher->kccgst[KCCGST_KEYCODES], &out->keycodes, NULL); + darray_steal(matcher->kccgst[KCCGST_TYPES], &out->types, NULL); + darray_steal(matcher->kccgst[KCCGST_COMPAT], &out->compat, NULL); + darray_steal(matcher->kccgst[KCCGST_SYMBOLS], &out->symbols, NULL); + darray_free(matcher->kccgst[KCCGST_GEOMETRY]); + + mval = &matcher->rmlvo.model; + if (!mval->matched && mval->sval.len > 0) + log_err(matcher->ctx, "Unrecognized RMLVO model \"%.*s\" was ignored\n", + mval->sval.len, mval->sval.start); + darray_foreach(mval, matcher->rmlvo.layouts) + if (!mval->matched && mval->sval.len > 0) + log_err(matcher->ctx, "Unrecognized RMLVO layout \"%.*s\" was ignored\n", + mval->sval.len, mval->sval.start); + darray_foreach(mval, matcher->rmlvo.variants) + if (!mval->matched && mval->sval.len > 0) + log_err(matcher->ctx, "Unrecognized RMLVO variant \"%.*s\" was ignored\n", + mval->sval.len, mval->sval.start); + darray_foreach(mval, matcher->rmlvo.options) + if (!mval->matched && mval->sval.len > 0) + log_err(matcher->ctx, "Unrecognized RMLVO option \"%.*s\" was ignored\n", + mval->sval.len, mval->sval.start); + +err_out: + if (file) + fclose(file); + matcher_free(matcher); + free(path); + return ret; +} diff --git a/src/xkbcomp/rules.h b/src/xkbcomp/rules.h new file mode 100644 index 0000000..5381b15 --- /dev/null +++ b/src/xkbcomp/rules.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2009 Dan Nicholson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef XKBCOMP_RULES_H +#define XKBCOMP_RULES_H + +bool +xkb_components_from_rules(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo, + struct xkb_component_names *out); + +#endif diff --git a/src/xkbcomp/scanner.c b/src/xkbcomp/scanner.c new file mode 100644 index 0000000..b349499 --- /dev/null +++ b/src/xkbcomp/scanner.c @@ -0,0 +1,212 @@ +/* + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "parser-priv.h" +#include "scanner-utils.h" + +static bool +number(struct scanner *s, int64_t *out, int *out_tok) +{ + bool is_float = false, is_hex = false; + const char *start = s->s + s->pos; + char *end; + + if (lit(s, "0x")) { + while (is_xdigit(peek(s))) next(s); + is_hex = true; + } + else { + while (is_digit(peek(s))) next(s); + is_float = chr(s, '.'); + while (is_digit(peek(s))) next(s); + } + if (s->s + s->pos == start) + return false; + + errno = 0; + if (is_hex) + *out = strtoul(start, &end, 16); + else if (is_float) + /* The parser currently just ignores floats, so the cast is + * fine - the value doesn't matter. */ + *out = strtod(start, &end); + else + *out = strtoul(start, &end, 10); + if (errno != 0 || s->s + s->pos != end) + *out_tok = ERROR_TOK; + else + *out_tok = (is_float ? FLOAT : INTEGER); + return true; +} + +int +_xkbcommon_lex(YYSTYPE *yylval, struct scanner *s) +{ + int tok; + +skip_more_whitespace_and_comments: + /* Skip spaces. */ + while (is_space(peek(s))) next(s); + + /* Skip comments. */ + if (lit(s, "//") || chr(s, '#')) { + skip_to_eol(s); + goto skip_more_whitespace_and_comments; + } + + /* See if we're done. */ + if (eof(s)) return END_OF_FILE; + + /* New token. */ + s->token_line = s->line; + s->token_column = s->column; + s->buf_pos = 0; + + /* String literal. */ + if (chr(s, '\"')) { + while (!eof(s) && !eol(s) && peek(s) != '\"') { + if (chr(s, '\\')) { + uint8_t o; + if (chr(s, '\\')) buf_append(s, '\\'); + else if (chr(s, 'n')) buf_append(s, '\n'); + else if (chr(s, 't')) buf_append(s, '\t'); + else if (chr(s, 'r')) buf_append(s, '\r'); + else if (chr(s, 'b')) buf_append(s, '\b'); + else if (chr(s, 'f')) buf_append(s, '\f'); + else if (chr(s, 'v')) buf_append(s, '\v'); + else if (chr(s, 'e')) buf_append(s, '\033'); + else if (oct(s, &o)) buf_append(s, (char) o); + else { + scanner_warn(s, "unknown escape sequence in string literal"); + /* Ignore. */ + } + } else { + buf_append(s, next(s)); + } + } + if (!buf_append(s, '\0') || !chr(s, '\"')) { + scanner_err(s, "unterminated string literal"); + return ERROR_TOK; + } + yylval->str = strdup(s->buf); + if (!yylval->str) + return ERROR_TOK; + return STRING; + } + + /* Key name literal. */ + if (chr(s, '<')) { + while (is_graph(peek(s)) && peek(s) != '>') + buf_append(s, next(s)); + if (!buf_append(s, '\0') || !chr(s, '>')) { + scanner_err(s, "unterminated key name literal"); + return ERROR_TOK; + } + /* Empty key name literals are allowed. */ + yylval->atom = xkb_atom_intern(s->ctx, s->buf, s->buf_pos - 1); + return KEYNAME; + } + + /* Operators and punctuation. */ + if (chr(s, ';')) return SEMI; + if (chr(s, '{')) return OBRACE; + if (chr(s, '}')) return CBRACE; + if (chr(s, '=')) return EQUALS; + if (chr(s, '[')) return OBRACKET; + if (chr(s, ']')) return CBRACKET; + if (chr(s, '(')) return OPAREN; + if (chr(s, ')')) return CPAREN; + if (chr(s, '.')) return DOT; + if (chr(s, ',')) return COMMA; + if (chr(s, '+')) return PLUS; + if (chr(s, '-')) return MINUS; + if (chr(s, '*')) return TIMES; + if (chr(s, '/')) return DIVIDE; + if (chr(s, '!')) return EXCLAM; + if (chr(s, '~')) return INVERT; + + /* Identifier. */ + if (is_alpha(peek(s)) || peek(s) == '_') { + s->buf_pos = 0; + while (is_alnum(peek(s)) || peek(s) == '_') + buf_append(s, next(s)); + if (!buf_append(s, '\0')) { + scanner_err(s, "identifier too long"); + return ERROR_TOK; + } + + /* Keyword. */ + tok = keyword_to_token(s->buf, s->buf_pos - 1); + if (tok != -1) return tok; + + yylval->str = strdup(s->buf); + if (!yylval->str) + return ERROR_TOK; + return IDENT; + } + + /* Number literal (hexadecimal / decimal / float). */ + if (number(s, &yylval->num, &tok)) { + if (tok == ERROR_TOK) { + scanner_err(s, "malformed number literal"); + return ERROR_TOK; + } + return tok; + } + + scanner_err(s, "unrecognized token"); + return ERROR_TOK; +} + +XkbFile * +XkbParseString(struct xkb_context *ctx, const char *string, size_t len, + const char *file_name, const char *map) +{ + struct scanner scanner; + scanner_init(&scanner, ctx, string, len, file_name, NULL); + return parse(ctx, &scanner, map); +} + +XkbFile * +XkbParseFile(struct xkb_context *ctx, FILE *file, + const char *file_name, const char *map) +{ + bool ok; + XkbFile *xkb_file; + char *string; + size_t size; + + ok = map_file(file, &string, &size); + if (!ok) { + log_err(ctx, "Couldn't read XKB file %s: %s\n", + file_name, strerror(errno)); + return NULL; + } + + xkb_file = XkbParseString(ctx, string, size, file_name, map); + unmap_file(string, size); + return xkb_file; +} diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c new file mode 100644 index 0000000..eb78412 --- /dev/null +++ b/src/xkbcomp/symbols.c @@ -0,0 +1,1600 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "action.h" +#include "vmod.h" +#include "include.h" +#include "keysym.h" + +enum key_repeat { + KEY_REPEAT_UNDEFINED = 0, + KEY_REPEAT_YES = 1, + KEY_REPEAT_NO = 2, +}; + +enum group_field { + GROUP_FIELD_SYMS = (1 << 0), + GROUP_FIELD_ACTS = (1 << 1), + GROUP_FIELD_TYPE = (1 << 2), +}; + +enum key_field { + KEY_FIELD_REPEAT = (1 << 0), + KEY_FIELD_DEFAULT_TYPE = (1 << 1), + KEY_FIELD_GROUPINFO = (1 << 2), + KEY_FIELD_VMODMAP = (1 << 3), +}; + +typedef struct { + enum group_field defined; + darray(struct xkb_level) levels; + xkb_atom_t type; +} GroupInfo; + +typedef struct { + enum key_field defined; + enum merge_mode merge; + + xkb_atom_t name; + + darray(GroupInfo) groups; + + enum key_repeat repeat; + xkb_mod_mask_t vmodmap; + xkb_atom_t default_type; + + enum xkb_range_exceed_type out_of_range_group_action; + xkb_layout_index_t out_of_range_group_number; +} KeyInfo; + +static void +ClearLevelInfo(struct xkb_level *leveli) +{ + if (leveli->num_syms > 1) + free(leveli->u.syms); +} + +static void +InitGroupInfo(GroupInfo *groupi) +{ + memset(groupi, 0, sizeof(*groupi)); +} + +static void +ClearGroupInfo(GroupInfo *groupi) +{ + struct xkb_level *leveli; + darray_foreach(leveli, groupi->levels) + ClearLevelInfo(leveli); + darray_free(groupi->levels); +} + +static void +CopyGroupInfo(GroupInfo *to, const GroupInfo *from) +{ + to->defined = from->defined; + to->type = from->type; + darray_init(to->levels); + darray_copy(to->levels, from->levels); + for (xkb_level_index_t j = 0; j < darray_size(to->levels); j++) + if (darray_item(from->levels, j).num_syms > 1) + darray_item(to->levels, j).u.syms = + memdup(darray_item(from->levels, j).u.syms, + darray_item(from->levels, j).num_syms, + sizeof(xkb_keysym_t)); +} + +static void +InitKeyInfo(struct xkb_context *ctx, KeyInfo *keyi) +{ + memset(keyi, 0, sizeof(*keyi)); + keyi->merge = MERGE_OVERRIDE; + keyi->name = xkb_atom_intern_literal(ctx, "*"); + keyi->out_of_range_group_action = RANGE_WRAP; +} + +static void +ClearKeyInfo(KeyInfo *keyi) +{ + GroupInfo *groupi; + darray_foreach(groupi, keyi->groups) + ClearGroupInfo(groupi); + darray_free(keyi->groups); +} + +/***====================================================================***/ + +typedef struct { + enum merge_mode merge; + bool haveSymbol; + xkb_mod_index_t modifier; + union { + xkb_atom_t keyName; + xkb_keysym_t keySym; + } u; +} ModMapEntry; + +typedef struct { + char *name; /* e.g. pc+us+inet(evdev) */ + int errorCount; + enum merge_mode merge; + xkb_layout_index_t explicit_group; + darray(KeyInfo) keys; + KeyInfo default_key; + ActionsInfo *actions; + darray(xkb_atom_t) group_names; + darray(ModMapEntry) modmaps; + struct xkb_mod_set mods; + + struct xkb_context *ctx; + /* Needed for AddKeySymbols. */ + const struct xkb_keymap *keymap; +} SymbolsInfo; + +static void +InitSymbolsInfo(SymbolsInfo *info, const struct xkb_keymap *keymap, + ActionsInfo *actions, const struct xkb_mod_set *mods) +{ + memset(info, 0, sizeof(*info)); + info->ctx = keymap->ctx; + info->keymap = keymap; + info->merge = MERGE_OVERRIDE; + InitKeyInfo(keymap->ctx, &info->default_key); + info->actions = actions; + info->mods = *mods; + info->explicit_group = XKB_LAYOUT_INVALID; +} + +static void +ClearSymbolsInfo(SymbolsInfo *info) +{ + KeyInfo *keyi; + free(info->name); + darray_foreach(keyi, info->keys) + ClearKeyInfo(keyi); + darray_free(info->keys); + darray_free(info->group_names); + darray_free(info->modmaps); + ClearKeyInfo(&info->default_key); +} + +static const char * +KeyInfoText(SymbolsInfo *info, KeyInfo *keyi) +{ + return KeyNameText(info->ctx, keyi->name); +} + +static bool +MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber, + bool report, xkb_layout_index_t group, xkb_atom_t key_name) +{ + xkb_level_index_t i, levels_in_both; + struct xkb_level *level; + + /* First find the type of the merged group. */ + if (into->type != from->type) { + if (from->type == XKB_ATOM_NONE) { + /* it's empty for consistency with other comparisons */ + } + else if (into->type == XKB_ATOM_NONE) { + into->type = from->type; + } + else { + xkb_atom_t use = (clobber ? from->type : into->type); + xkb_atom_t ignore = (clobber ? into->type : from->type); + + if (report) + log_warn(info->ctx, + "Multiple definitions for group %d type of key %s; " + "Using %s, ignoring %s\n", + group + 1, KeyNameText(info->ctx, key_name), + xkb_atom_text(info->ctx, use), + xkb_atom_text(info->ctx, ignore)); + + into->type = use; + } + } + into->defined |= (from->defined & GROUP_FIELD_TYPE); + + /* Now look at the levels. */ + + if (darray_empty(from->levels)) { + InitGroupInfo(from); + return true; + } + + if (darray_empty(into->levels)) { + from->type = into->type; + *into = *from; + InitGroupInfo(from); + return true; + } + + /* Merge the actions and syms. */ + levels_in_both = MIN(darray_size(into->levels), darray_size(from->levels)); + for (i = 0; i < levels_in_both; i++) { + struct xkb_level *intoLevel = &darray_item(into->levels, i); + struct xkb_level *fromLevel = &darray_item(from->levels, i); + + if (fromLevel->action.type == ACTION_TYPE_NONE) { + /* it's empty for consistency with other comparisons */ + } + else if (intoLevel->action.type == ACTION_TYPE_NONE) { + intoLevel->action = fromLevel->action; + } + else { + union xkb_action *use, *ignore; + use = (clobber ? &fromLevel->action : &intoLevel->action); + ignore = (clobber ? &intoLevel->action : &fromLevel->action); + + if (report) + log_warn(info->ctx, + "Multiple actions for level %d/group %u on key %s; " + "Using %s, ignoring %s\n", + i + 1, group + 1, KeyNameText(info->ctx, key_name), + ActionTypeText(use->type), + ActionTypeText(ignore->type)); + + intoLevel->action = *use; + } + + if (fromLevel->num_syms == 0) { + /* it's empty for consistency with other comparisons */ + } + else if (intoLevel->num_syms == 0) { + intoLevel->num_syms = fromLevel->num_syms; + if (fromLevel->num_syms > 1) + intoLevel->u.syms = fromLevel->u.syms; + else + intoLevel->u.sym = fromLevel->u.sym; + fromLevel->num_syms = 0; + } + else if (!XkbLevelsSameSyms(fromLevel, intoLevel)) { + if (report) + log_warn(info->ctx, + "Multiple symbols for level %d/group %u on key %s; " + "Using %s, ignoring %s\n", + i + 1, group + 1, KeyNameText(info->ctx, key_name), + (clobber ? "from" : "to"), + (clobber ? "to" : "from")); + + if (clobber) { + ClearLevelInfo(intoLevel); + intoLevel->num_syms = fromLevel->num_syms; + if (fromLevel->num_syms > 1) + intoLevel->u.syms = fromLevel->u.syms; + else + intoLevel->u.sym = fromLevel->u.sym; + fromLevel->num_syms = 0; + } + } + } + /* If @from has extra levels, get them as well. */ + darray_foreach_from(level, from->levels, levels_in_both) { + darray_append(into->levels, *level); + level->num_syms = 0; + } + into->defined |= (from->defined & GROUP_FIELD_ACTS); + into->defined |= (from->defined & GROUP_FIELD_SYMS); + + return true; +} + +static bool +UseNewKeyField(enum key_field field, enum key_field old, enum key_field new, + bool clobber, bool report, enum key_field *collide) +{ + if (!(old & field)) + return (new & field); + + if (new & field) { + if (report) + *collide |= field; + + if (clobber) + return true; + } + + return false; +} + +static bool +MergeKeys(SymbolsInfo *info, KeyInfo *into, KeyInfo *from, bool same_file) +{ + xkb_layout_index_t i; + xkb_layout_index_t groups_in_both; + enum key_field collide = 0; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool clobber = (from->merge != MERGE_AUGMENT); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + + if (from->merge == MERGE_REPLACE) { + ClearKeyInfo(into); + *into = *from; + InitKeyInfo(info->ctx, from); + return true; + } + + groups_in_both = MIN(darray_size(into->groups), darray_size(from->groups)); + for (i = 0; i < groups_in_both; i++) + MergeGroups(info, + &darray_item(into->groups, i), + &darray_item(from->groups, i), + clobber, report, i, into->name); + /* If @from has extra groups, just move them to @into. */ + for (i = groups_in_both; i < darray_size(from->groups); i++) { + darray_append(into->groups, darray_item(from->groups, i)); + InitGroupInfo(&darray_item(from->groups, i)); + } + + if (UseNewKeyField(KEY_FIELD_VMODMAP, into->defined, from->defined, + clobber, report, &collide)) { + into->vmodmap = from->vmodmap; + into->defined |= KEY_FIELD_VMODMAP; + } + if (UseNewKeyField(KEY_FIELD_REPEAT, into->defined, from->defined, + clobber, report, &collide)) { + into->repeat = from->repeat; + into->defined |= KEY_FIELD_REPEAT; + } + if (UseNewKeyField(KEY_FIELD_DEFAULT_TYPE, into->defined, from->defined, + clobber, report, &collide)) { + into->default_type = from->default_type; + into->defined |= KEY_FIELD_DEFAULT_TYPE; + } + if (UseNewKeyField(KEY_FIELD_GROUPINFO, into->defined, from->defined, + clobber, report, &collide)) { + into->out_of_range_group_action = from->out_of_range_group_action; + into->out_of_range_group_number = from->out_of_range_group_number; + into->defined |= KEY_FIELD_GROUPINFO; + } + + if (collide) + log_warn(info->ctx, + "Symbol map for key %s redefined; " + "Using %s definition for conflicting fields\n", + KeyNameText(info->ctx, into->name), + (clobber ? "first" : "last")); + + ClearKeyInfo(from); + InitKeyInfo(info->ctx, from); + return true; +} + +/* TODO: Make it so this function doesn't need the entire keymap. */ +static bool +AddKeySymbols(SymbolsInfo *info, KeyInfo *keyi, bool same_file) +{ + xkb_atom_t real_name; + KeyInfo *iter; + + /* + * Don't keep aliases in the keys array; this guarantees that + * searching for keys to merge with by straight comparison (see the + * following loop) is enough, and we won't get multiple KeyInfo's + * for the same key because of aliases. + */ + real_name = XkbResolveKeyAlias(info->keymap, keyi->name); + if (real_name != XKB_ATOM_NONE) + keyi->name = real_name; + + darray_foreach(iter, info->keys) + if (iter->name == keyi->name) + return MergeKeys(info, iter, keyi, same_file); + + darray_append(info->keys, *keyi); + InitKeyInfo(info->ctx, keyi); + return true; +} + +static bool +AddModMapEntry(SymbolsInfo *info, ModMapEntry *new) +{ + ModMapEntry *old; + bool clobber = (new->merge != MERGE_AUGMENT); + + darray_foreach(old, info->modmaps) { + xkb_mod_index_t use, ignore; + + if ((new->haveSymbol != old->haveSymbol) || + (new->haveSymbol && new->u.keySym != old->u.keySym) || + (!new->haveSymbol && new->u.keyName != old->u.keyName)) + continue; + + if (new->modifier == old->modifier) + return true; + + use = (clobber ? new->modifier : old->modifier); + ignore = (clobber ? old->modifier : new->modifier); + + if (new->haveSymbol) + log_warn(info->ctx, + "Symbol \"%s\" added to modifier map for multiple modifiers; " + "Using %s, ignoring %s\n", + KeysymText(info->ctx, new->u.keySym), + ModIndexText(info->ctx, &info->mods, use), + ModIndexText(info->ctx, &info->mods, ignore)); + else + log_warn(info->ctx, + "Key \"%s\" added to modifier map for multiple modifiers; " + "Using %s, ignoring %s\n", + KeyNameText(info->ctx, new->u.keyName), + ModIndexText(info->ctx, &info->mods, use), + ModIndexText(info->ctx, &info->mods, ignore)); + + old->modifier = use; + return true; + } + + darray_append(info->modmaps, *new); + return true; +} + +/***====================================================================***/ + +static void +MergeIncludedSymbols(SymbolsInfo *into, SymbolsInfo *from, + enum merge_mode merge) +{ + xkb_atom_t *group_name; + xkb_layout_index_t group_names_in_both; + + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + into->mods = from->mods; + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + group_names_in_both = MIN(darray_size(into->group_names), + darray_size(from->group_names)); + for (xkb_layout_index_t i = 0; i < group_names_in_both; i++) { + if (!darray_item(from->group_names, i)) + continue; + + if (merge == MERGE_AUGMENT && darray_item(into->group_names, i)) + continue; + + darray_item(into->group_names, i) = darray_item(from->group_names, i); + } + /* If @from has more, get them as well. */ + darray_foreach_from(group_name, from->group_names, group_names_in_both) + darray_append(into->group_names, *group_name); + + if (darray_empty(into->keys)) { + into->keys = from->keys; + darray_init(from->keys); + } + else { + KeyInfo *keyi; + darray_foreach(keyi, from->keys) { + keyi->merge = (merge == MERGE_DEFAULT ? keyi->merge : merge); + if (!AddKeySymbols(into, keyi, false)) + into->errorCount++; + } + } + + if (darray_empty(into->modmaps)) { + into->modmaps = from->modmaps; + darray_init(from->modmaps); + } + else { + ModMapEntry *mm; + darray_foreach(mm, from->modmaps) { + mm->merge = (merge == MERGE_DEFAULT ? mm->merge : merge); + if (!AddModMapEntry(into, mm)) + into->errorCount++; + } + } +} + +static void +HandleSymbolsFile(SymbolsInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeSymbols(SymbolsInfo *info, IncludeStmt *include) +{ + SymbolsInfo included; + + InitSymbolsInfo(&included, info->keymap, info->actions, &info->mods); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + SymbolsInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_SYMBOLS); + if (!file) { + info->errorCount += 10; + ClearSymbolsInfo(&included); + return false; + } + + InitSymbolsInfo(&next_incl, info->keymap, info->actions, + &included.mods); + if (stmt->modifier) { + next_incl.explicit_group = atoi(stmt->modifier) - 1; + if (next_incl.explicit_group >= XKB_MAX_GROUPS) { + log_err(info->ctx, + "Cannot set explicit group to %d - must be between 1..%d; " + "Ignoring group number\n", + next_incl.explicit_group + 1, XKB_MAX_GROUPS); + next_incl.explicit_group = info->explicit_group; + } + } + else { + next_incl.explicit_group = info->explicit_group; + } + + HandleSymbolsFile(&next_incl, file, MERGE_OVERRIDE); + + MergeIncludedSymbols(&included, &next_incl, stmt->merge); + + ClearSymbolsInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedSymbols(info, &included, include->merge); + ClearSymbolsInfo(&included); + + return (info->errorCount == 0); +} + +#define SYMBOLS 1 +#define ACTIONS 2 + +static bool +GetGroupIndex(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, + unsigned what, xkb_layout_index_t *ndx_rtrn) +{ + const char *name = (what == SYMBOLS ? "symbols" : "actions"); + + if (arrayNdx == NULL) { + xkb_layout_index_t i; + GroupInfo *groupi; + enum group_field field = (what == SYMBOLS ? + GROUP_FIELD_SYMS : GROUP_FIELD_ACTS); + + darray_enumerate(i, groupi, keyi->groups) { + if (!(groupi->defined & field)) { + *ndx_rtrn = i; + return true; + } + } + + if (i >= XKB_MAX_GROUPS) { + log_err(info->ctx, + "Too many groups of %s for key %s (max %u); " + "Ignoring %s defined for extra groups\n", + name, KeyInfoText(info, keyi), XKB_MAX_GROUPS, name); + return false; + } + + darray_resize0(keyi->groups, darray_size(keyi->groups) + 1); + *ndx_rtrn = darray_size(keyi->groups) - 1; + return true; + } + + if (!ExprResolveGroup(info->ctx, arrayNdx, ndx_rtrn)) { + log_err(info->ctx, + "Illegal group index for %s of key %s\n" + "Definition with non-integer array index ignored\n", + name, KeyInfoText(info, keyi)); + return false; + } + + (*ndx_rtrn)--; + if (*ndx_rtrn >= darray_size(keyi->groups)) + darray_resize0(keyi->groups, *ndx_rtrn + 1); + + return true; +} + +static bool +AddSymbolsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_layout_index_t ndx; + GroupInfo *groupi; + xkb_level_index_t nLevels; + + if (!GetGroupIndex(info, keyi, arrayNdx, SYMBOLS, &ndx)) + return false; + + groupi = &darray_item(keyi->groups, ndx); + + if (value == NULL) { + groupi->defined |= GROUP_FIELD_SYMS; + return true; + } + + if (value->expr.op != EXPR_KEYSYM_LIST) { + log_err(info->ctx, + "Expected a list of symbols, found %s; " + "Ignoring symbols for group %u of %s\n", + expr_op_type_to_string(value->expr.op), ndx + 1, + KeyInfoText(info, keyi)); + return false; + } + + if (groupi->defined & GROUP_FIELD_SYMS) { + log_err(info->ctx, + "Symbols for key %s, group %u already defined; " + "Ignoring duplicate definition\n", + KeyInfoText(info, keyi), ndx + 1); + return false; + } + + nLevels = darray_size(value->keysym_list.symsMapIndex); + if (darray_size(groupi->levels) < nLevels) + darray_resize0(groupi->levels, nLevels); + + groupi->defined |= GROUP_FIELD_SYMS; + + for (xkb_level_index_t i = 0; i < nLevels; i++) { + unsigned int sym_index; + struct xkb_level *leveli = &darray_item(groupi->levels, i); + + sym_index = darray_item(value->keysym_list.symsMapIndex, i); + leveli->num_syms = darray_item(value->keysym_list.symsNumEntries, i); + if (leveli->num_syms > 1) + leveli->u.syms = calloc(leveli->num_syms, sizeof(*leveli->u.syms)); + + for (unsigned j = 0; j < leveli->num_syms; j++) { + xkb_keysym_t keysym = darray_item(value->keysym_list.syms, + sym_index + j); + + if (leveli->num_syms == 1) { + if (keysym == XKB_KEY_NoSymbol) + leveli->num_syms = 0; + else + leveli->u.sym = keysym; + } + else if (leveli->num_syms > 1) { + leveli->u.syms[j] = keysym; + } + } + } + + return true; +} + +static bool +AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_layout_index_t ndx; + GroupInfo *groupi; + unsigned int nActs; + ExprDef *act; + + if (!GetGroupIndex(info, keyi, arrayNdx, ACTIONS, &ndx)) + return false; + + groupi = &darray_item(keyi->groups, ndx); + + if (value == NULL) { + groupi->defined |= GROUP_FIELD_ACTS; + return true; + } + + if (value->expr.op != EXPR_ACTION_LIST) { + log_wsgo(info->ctx, + "Bad expression type (%d) for action list value; " + "Ignoring actions for group %u of %s\n", + value->expr.op, ndx, KeyInfoText(info, keyi)); + return false; + } + + if (groupi->defined & GROUP_FIELD_ACTS) { + log_wsgo(info->ctx, + "Actions for key %s, group %u already defined\n", + KeyInfoText(info, keyi), ndx); + return false; + } + + nActs = 0; + for (act = value->actions.actions; act; act = (ExprDef *) act->common.next) + nActs++; + + if (darray_size(groupi->levels) < nActs) + darray_resize0(groupi->levels, nActs); + + groupi->defined |= GROUP_FIELD_ACTS; + + act = value->actions.actions; + for (unsigned i = 0; i < nActs; i++) { + union xkb_action *toAct = &darray_item(groupi->levels, i).action; + + if (!HandleActionDef(info->ctx, info->actions, &info->mods, act, toAct)) + log_err(info->ctx, + "Illegal action definition for %s; " + "Action for group %u/level %u ignored\n", + KeyInfoText(info, keyi), ndx + 1, i + 1); + + act = (ExprDef *) act->common.next; + } + + return true; +} + +static const LookupEntry repeatEntries[] = { + { "true", KEY_REPEAT_YES }, + { "yes", KEY_REPEAT_YES }, + { "on", KEY_REPEAT_YES }, + { "false", KEY_REPEAT_NO }, + { "no", KEY_REPEAT_NO }, + { "off", KEY_REPEAT_NO }, + { "default", KEY_REPEAT_UNDEFINED }, + { NULL, 0 } +}; + +static bool +SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field, + ExprDef *arrayNdx, ExprDef *value) +{ + if (istreq(field, "type")) { + xkb_layout_index_t ndx; + xkb_atom_t val; + + if (!ExprResolveString(info->ctx, value, &val)) { + log_err(info->ctx, + "The type field of a key symbol map must be a string; " + "Ignoring illegal type definition\n"); + return false; + } + + if (!arrayNdx) { + keyi->default_type = val; + keyi->defined |= KEY_FIELD_DEFAULT_TYPE; + } + else if (!ExprResolveGroup(info->ctx, arrayNdx, &ndx)) { + log_err(info->ctx, + "Illegal group index for type of key %s; " + "Definition with non-integer array index ignored\n", + KeyInfoText(info, keyi)); + return false; + } + else { + ndx--; + if (ndx >= darray_size(keyi->groups)) + darray_resize0(keyi->groups, ndx + 1); + darray_item(keyi->groups, ndx).type = val; + darray_item(keyi->groups, ndx).defined |= GROUP_FIELD_TYPE; + } + } + else if (istreq(field, "symbols")) { + return AddSymbolsToKey(info, keyi, arrayNdx, value); + } + else if (istreq(field, "actions")) { + return AddActionsToKey(info, keyi, arrayNdx, value); + } + else if (istreq(field, "vmods") || + istreq(field, "virtualmods") || + istreq(field, "virtualmodifiers")) { + xkb_mod_mask_t mask; + + if (!ExprResolveModMask(info->ctx, value, MOD_VIRT, &info->mods, + &mask)) { + log_err(info->ctx, + "Expected a virtual modifier mask, found %s; " + "Ignoring virtual modifiers definition for key %s\n", + expr_op_type_to_string(value->expr.op), + KeyInfoText(info, keyi)); + return false; + } + + keyi->vmodmap = mask; + keyi->defined |= KEY_FIELD_VMODMAP; + } + else if (istreq(field, "locking") || + istreq(field, "lock") || + istreq(field, "locks")) { + log_vrb(info->ctx, 1, + "Key behaviors not supported; " + "Ignoring locking specification for key %s\n", + KeyInfoText(info, keyi)); + } + else if (istreq(field, "radiogroup") || + istreq(field, "permanentradiogroup") || + istreq(field, "allownone")) { + log_vrb(info->ctx, 1, + "Radio groups not supported; " + "Ignoring radio group specification for key %s\n", + KeyInfoText(info, keyi)); + } + else if (istreq_prefix("overlay", field) || + istreq_prefix("permanentoverlay", field)) { + log_vrb(info->ctx, 1, + "Overlays not supported; " + "Ignoring overlay specification for key %s\n", + KeyInfoText(info, keyi)); + } + else if (istreq(field, "repeating") || + istreq(field, "repeats") || + istreq(field, "repeat")) { + unsigned int val; + + if (!ExprResolveEnum(info->ctx, value, &val, repeatEntries)) { + log_err(info->ctx, + "Illegal repeat setting for %s; " + "Non-boolean repeat setting ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->repeat = val; + keyi->defined |= KEY_FIELD_REPEAT; + } + else if (istreq(field, "groupswrap") || + istreq(field, "wrapgroups")) { + bool set; + + if (!ExprResolveBoolean(info->ctx, value, &set)) { + log_err(info->ctx, + "Illegal groupsWrap setting for %s; " + "Non-boolean value ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->out_of_range_group_action = (set ? RANGE_WRAP : RANGE_SATURATE); + keyi->defined |= KEY_FIELD_GROUPINFO; + } + else if (istreq(field, "groupsclamp") || + istreq(field, "clampgroups")) { + bool set; + + if (!ExprResolveBoolean(info->ctx, value, &set)) { + log_err(info->ctx, + "Illegal groupsClamp setting for %s; " + "Non-boolean value ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->out_of_range_group_action = (set ? RANGE_SATURATE : RANGE_WRAP); + keyi->defined |= KEY_FIELD_GROUPINFO; + } + else if (istreq(field, "groupsredirect") || + istreq(field, "redirectgroups")) { + xkb_layout_index_t grp; + + if (!ExprResolveGroup(info->ctx, value, &grp)) { + log_err(info->ctx, + "Illegal group index for redirect of key %s; " + "Definition with non-integer group ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->out_of_range_group_action = RANGE_REDIRECT; + keyi->out_of_range_group_number = grp - 1; + keyi->defined |= KEY_FIELD_GROUPINFO; + } + else { + log_err(info->ctx, + "Unknown field %s in a symbol interpretation; " + "Definition ignored\n", + field); + return false; + } + + return true; +} + +static bool +SetGroupName(SymbolsInfo *info, ExprDef *arrayNdx, ExprDef *value) +{ + xkb_layout_index_t group, group_to_use; + xkb_atom_t name; + + if (!arrayNdx) { + log_vrb(info->ctx, 1, + "You must specify an index when specifying a group name; " + "Group name definition without array subscript ignored\n"); + return false; + } + + if (!ExprResolveGroup(info->ctx, arrayNdx, &group)) { + log_err(info->ctx, + "Illegal index in group name definition; " + "Definition with non-integer array index ignored\n"); + return false; + } + + if (!ExprResolveString(info->ctx, value, &name)) { + log_err(info->ctx, + "Group name must be a string; " + "Illegal name for group %d ignored\n", group); + return false; + } + + if (info->explicit_group == XKB_LAYOUT_INVALID) { + group_to_use = group - 1; + } + else if (group - 1 == 0) { + group_to_use = info->explicit_group; + } + else { + log_warn(info->ctx, + "An explicit group was specified for the '%s' map, " + "but it provides a name for a group other than Group1 (%d); " + "Ignoring group name '%s'\n", + info->name, group, + xkb_atom_text(info->ctx, name)); + return false; + } + + if (group_to_use >= darray_size(info->group_names)) + darray_resize0(info->group_names, group_to_use + 1); + darray_item(info->group_names, group_to_use) = name; + + return true; +} + +static bool +HandleGlobalVar(SymbolsInfo *info, VarDef *stmt) +{ + const char *elem, *field; + ExprDef *arrayNdx; + bool ret; + + if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &arrayNdx)) + return false; + + if (elem && istreq(elem, "key")) { + ret = SetSymbolsField(info, &info->default_key, field, arrayNdx, + stmt->value); + } + else if (!elem && (istreq(field, "name") || + istreq(field, "groupname"))) { + ret = SetGroupName(info, arrayNdx, stmt->value); + } + else if (!elem && (istreq(field, "groupswrap") || + istreq(field, "wrapgroups"))) { + log_err(info->ctx, + "Global \"groupswrap\" not supported; Ignored\n"); + ret = true; + } + else if (!elem && (istreq(field, "groupsclamp") || + istreq(field, "clampgroups"))) { + log_err(info->ctx, + "Global \"groupsclamp\" not supported; Ignored\n"); + ret = true; + } + else if (!elem && (istreq(field, "groupsredirect") || + istreq(field, "redirectgroups"))) { + log_err(info->ctx, + "Global \"groupsredirect\" not supported; Ignored\n"); + ret = true; + } + else if (!elem && istreq(field, "allownone")) { + log_err(info->ctx, + "Radio groups not supported; " + "Ignoring \"allownone\" specification\n"); + ret = true; + } + else { + ret = SetActionField(info->ctx, info->actions, &info->mods, + elem, field, arrayNdx, stmt->value); + } + + return ret; +} + +static bool +HandleSymbolsBody(SymbolsInfo *info, VarDef *def, KeyInfo *keyi) +{ + bool ok = true; + const char *elem, *field; + ExprDef *arrayNdx; + + for (; def; def = (VarDef *) def->common.next) { + if (def->name && def->name->expr.op == EXPR_FIELD_REF) { + log_err(info->ctx, + "Cannot set a global default value from within a key statement; " + "Move statements to the global file scope\n"); + continue; + } + + if (!def->name) { + if (!def->value || def->value->expr.op == EXPR_KEYSYM_LIST) + field = "symbols"; + else + field = "actions"; + arrayNdx = NULL; + } + else { + ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, + &arrayNdx); + } + + if (ok) + ok = SetSymbolsField(info, keyi, field, arrayNdx, def->value); + } + + return ok; +} + +static bool +SetExplicitGroup(SymbolsInfo *info, KeyInfo *keyi) +{ + xkb_layout_index_t i; + GroupInfo *groupi; + bool warn = false; + + if (info->explicit_group == XKB_LAYOUT_INVALID) + return true; + + darray_enumerate_from(i, groupi, keyi->groups, 1) { + if (groupi->defined) { + warn = true; + ClearGroupInfo(groupi); + InitGroupInfo(groupi); + } + } + + if (warn) + log_warn(info->ctx, + "For the map %s an explicit group specified, " + "but key %s has more than one group defined; " + "All groups except first one will be ignored\n", + info->name, KeyInfoText(info, keyi)); + + darray_resize0(keyi->groups, info->explicit_group + 1); + if (info->explicit_group > 0) { + darray_item(keyi->groups, info->explicit_group) = + darray_item(keyi->groups, 0); + InitGroupInfo(&darray_item(keyi->groups, 0)); + } + + return true; +} + +static bool +HandleSymbolsDef(SymbolsInfo *info, SymbolsDef *stmt) +{ + KeyInfo keyi; + + keyi = info->default_key; + darray_init(keyi.groups); + darray_copy(keyi.groups, info->default_key.groups); + for (xkb_layout_index_t i = 0; i < darray_size(keyi.groups); i++) + CopyGroupInfo(&darray_item(keyi.groups, i), + &darray_item(info->default_key.groups, i)); + keyi.merge = stmt->merge; + keyi.name = stmt->keyName; + + if (!HandleSymbolsBody(info, stmt->symbols, &keyi)) { + info->errorCount++; + return false; + } + + if (!SetExplicitGroup(info, &keyi)) { + info->errorCount++; + return false; + } + + if (!AddKeySymbols(info, &keyi, true)) { + info->errorCount++; + return false; + } + + return true; +} + +static bool +HandleModMapDef(SymbolsInfo *info, ModMapDef *def) +{ + ModMapEntry tmp; + xkb_mod_index_t ndx; + bool ok; + struct xkb_context *ctx = info->ctx; + + ndx = XkbModNameToIndex(&info->mods, def->modifier, MOD_REAL); + if (ndx == XKB_MOD_INVALID) { + log_err(info->ctx, + "Illegal modifier map definition; " + "Ignoring map for non-modifier \"%s\"\n", + xkb_atom_text(ctx, def->modifier)); + return false; + } + + ok = true; + tmp.modifier = ndx; + tmp.merge = def->merge; + + for (ExprDef *key = def->keys; key; key = (ExprDef *) key->common.next) { + xkb_keysym_t sym; + + if (key->expr.op == EXPR_VALUE && + key->expr.value_type == EXPR_TYPE_KEYNAME) { + tmp.haveSymbol = false; + tmp.u.keyName = key->key_name.key_name; + } + else if (ExprResolveKeySym(ctx, key, &sym)) { + tmp.haveSymbol = true; + tmp.u.keySym = sym; + } + else { + log_err(info->ctx, + "Modmap entries may contain only key names or keysyms; " + "Illegal definition for %s modifier ignored\n", + ModIndexText(info->ctx, &info->mods, tmp.modifier)); + continue; + } + + ok = AddModMapEntry(info, &tmp) && ok; + } + return ok; +} + +static void +HandleSymbolsFile(SymbolsInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeSymbols(info, (IncludeStmt *) stmt); + break; + case STMT_SYMBOLS: + ok = HandleSymbolsDef(info, (SymbolsDef *) stmt); + break; + case STMT_VAR: + ok = HandleGlobalVar(info, (VarDef *) stmt); + break; + case STMT_VMOD: + ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge); + break; + case STMT_MODMAP: + ok = HandleModMapDef(info, (ModMapDef *) stmt); + break; + default: + log_err(info->ctx, + "Symbols files may not include other types; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, "Abandoning symbols file \"%s\"\n", + file->name); + break; + } + } +} + +/** + * Given a keysym @sym, return a key which generates it, or NULL. + * This is used for example in a modifier map definition, such as: + * modifier_map Lock { Caps_Lock }; + * where we want to add the Lock modifier to the modmap of the key + * which matches the keysym Caps_Lock. + * Since there can be many keys which generates the keysym, the key + * is chosen first by lowest group in which the keysym appears, than + * by lowest level and than by lowest key code. + */ +static struct xkb_key * +FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym) +{ + struct xkb_key *key; + xkb_layout_index_t group; + bool got_one_group, got_one_level; + + group = 0; + do { + xkb_level_index_t level = 0; + got_one_group = false; + do { + got_one_level = false; + xkb_keys_foreach(key, keymap) { + if (group < key->num_groups && + level < XkbKeyNumLevels(key, group)) { + got_one_group = got_one_level = true; + if (key->groups[group].levels[level].num_syms == 1 && + key->groups[group].levels[level].u.sym == sym) + return key; + } + } + level++; + } while (got_one_level); + group++; + } while (got_one_group); + + return NULL; +} + +/* + * Find an appropriate type for a group and return its name. + * + * Simple recipe: + * - ONE_LEVEL for width 0/1 + * - ALPHABETIC for 2 shift levels, with lower/upercase keysyms + * - KEYPAD for keypad keys. + * - TWO_LEVEL for other 2 shift level keys. + * and the same for four level keys. + * + * FIXME: Decide how to handle multiple-syms-per-level, and do it. + */ +static xkb_atom_t +FindAutomaticType(struct xkb_context *ctx, GroupInfo *groupi) +{ + xkb_keysym_t sym0, sym1; + const xkb_level_index_t width = darray_size(groupi->levels); + +#define GET_SYM(level) \ + (darray_item(groupi->levels, level).num_syms == 0 ? \ + XKB_KEY_NoSymbol : \ + darray_item(groupi->levels, level).num_syms == 1 ? \ + darray_item(groupi->levels, level).u.sym : \ + /* num_syms > 1 */ \ + darray_item(groupi->levels, level).u.syms[0]) + + if (width == 1 || width <= 0) + return xkb_atom_intern_literal(ctx, "ONE_LEVEL"); + + sym0 = GET_SYM(0); + sym1 = GET_SYM(1); + + if (width == 2) { + if (xkb_keysym_is_lower(sym0) && xkb_keysym_is_upper(sym1)) + return xkb_atom_intern_literal(ctx, "ALPHABETIC"); + + if (xkb_keysym_is_keypad(sym0) || xkb_keysym_is_keypad(sym1)) + return xkb_atom_intern_literal(ctx, "KEYPAD"); + + return xkb_atom_intern_literal(ctx, "TWO_LEVEL"); + } + + if (width <= 4) { + if (xkb_keysym_is_lower(sym0) && xkb_keysym_is_upper(sym1)) { + xkb_keysym_t sym2, sym3; + sym2 = GET_SYM(2); + sym3 = (width == 4 ? GET_SYM(3) : XKB_KEY_NoSymbol); + + if (xkb_keysym_is_lower(sym2) && xkb_keysym_is_upper(sym3)) + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL_ALPHABETIC"); + + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL_SEMIALPHABETIC"); + } + + if (xkb_keysym_is_keypad(sym0) || xkb_keysym_is_keypad(sym1)) + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL_KEYPAD"); + + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL"); + } + + return XKB_ATOM_NONE; + +#undef GET_SYM +} + +static const struct xkb_key_type * +FindTypeForGroup(struct xkb_keymap *keymap, KeyInfo *keyi, + xkb_layout_index_t group, bool *explicit_type) +{ + unsigned int i; + GroupInfo *groupi = &darray_item(keyi->groups, group); + xkb_atom_t type_name = groupi->type; + + *explicit_type = true; + + if (type_name == XKB_ATOM_NONE) { + if (keyi->default_type != XKB_ATOM_NONE) { + type_name = keyi->default_type; + } + else { + type_name = FindAutomaticType(keymap->ctx, groupi); + if (type_name != XKB_ATOM_NONE) + *explicit_type = false; + } + } + + if (type_name == XKB_ATOM_NONE) { + log_warn(keymap->ctx, + "Couldn't find an automatic type for key '%s' group %d with %lu levels; " + "Using the default type\n", + KeyNameText(keymap->ctx, keyi->name), group + 1, + (unsigned long) darray_size(groupi->levels)); + goto use_default; + } + + for (i = 0; i < keymap->num_types; i++) + if (keymap->types[i].name == type_name) + break; + + if (i >= keymap->num_types) { + log_warn(keymap->ctx, + "The type \"%s\" for key '%s' group %d was not previously defined; " + "Using the default type\n", + xkb_atom_text(keymap->ctx, type_name), + KeyNameText(keymap->ctx, keyi->name), group + 1); + goto use_default; + } + + return &keymap->types[i]; + +use_default: + /* + * Index 0 is guaranteed to contain something, usually + * ONE_LEVEL or at least some default one-level type. + */ + return &keymap->types[0]; +} + +static bool +CopySymbolsDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info, + KeyInfo *keyi) +{ + struct xkb_key *key; + GroupInfo *groupi; + const GroupInfo *group0; + xkb_layout_index_t i; + + /* + * The name is guaranteed to be real and not an alias (see + * AddKeySymbols), so 'false' is safe here. + */ + key = XkbKeyByName(keymap, keyi->name, false); + if (!key) { + log_vrb(info->ctx, 5, + "Key %s not found in keycodes; Symbols ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + /* Find the range of groups we need. */ + key->num_groups = 0; + darray_enumerate(i, groupi, keyi->groups) + if (groupi->defined) + key->num_groups = i + 1; + + if (key->num_groups <= 0) + return false; /* WSGO */ + + darray_resize(keyi->groups, key->num_groups); + + /* + * If there are empty groups between non-empty ones, fill them with data + * from the first group. + * We can make a wrong assumption here. But leaving gaps is worse. + */ + group0 = &darray_item(keyi->groups, 0); + darray_foreach_from(groupi, keyi->groups, 1) { + if (groupi->defined) + continue; + + CopyGroupInfo(groupi, group0); + } + + key->groups = calloc(key->num_groups, sizeof(*key->groups)); + + /* Find and assign the groups' types in the keymap. */ + darray_enumerate(i, groupi, keyi->groups) { + const struct xkb_key_type *type; + bool explicit_type; + + type = FindTypeForGroup(keymap, keyi, i, &explicit_type); + + /* Always have as many levels as the type specifies. */ + if (type->num_levels < darray_size(groupi->levels)) { + struct xkb_level *leveli; + + log_vrb(info->ctx, 1, + "Type \"%s\" has %d levels, but %s has %d levels; " + "Ignoring extra symbols\n", + xkb_atom_text(keymap->ctx, type->name), type->num_levels, + KeyInfoText(info, keyi), + (int) darray_size(groupi->levels)); + + darray_foreach_from(leveli, groupi->levels, type->num_levels) + ClearLevelInfo(leveli); + } + darray_resize0(groupi->levels, type->num_levels); + + key->groups[i].explicit_type = explicit_type; + key->groups[i].type = type; + } + + /* Copy levels. */ + darray_enumerate(i, groupi, keyi->groups) + darray_steal(groupi->levels, &key->groups[i].levels, NULL); + + key->out_of_range_group_number = keyi->out_of_range_group_number; + key->out_of_range_group_action = keyi->out_of_range_group_action; + + if (keyi->defined & KEY_FIELD_VMODMAP) { + key->vmodmap = keyi->vmodmap; + key->explicit |= EXPLICIT_VMODMAP; + } + + if (keyi->repeat != KEY_REPEAT_UNDEFINED) { + key->repeats = (keyi->repeat == KEY_REPEAT_YES); + key->explicit |= EXPLICIT_REPEAT; + } + + darray_foreach(groupi, keyi->groups) { + if (groupi->defined & GROUP_FIELD_ACTS) { + key->explicit |= EXPLICIT_INTERP; + break; + } + } + + return true; +} + +static bool +CopyModMapDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info, + ModMapEntry *entry) +{ + struct xkb_key *key; + + if (!entry->haveSymbol) { + key = XkbKeyByName(keymap, entry->u.keyName, true); + if (!key) { + log_vrb(info->ctx, 5, + "Key %s not found in keycodes; " + "Modifier map entry for %s not updated\n", + KeyNameText(info->ctx, entry->u.keyName), + ModIndexText(info->ctx, &info->mods, entry->modifier)); + return false; + } + } + else { + key = FindKeyForSymbol(keymap, entry->u.keySym); + if (!key) { + log_vrb(info->ctx, 5, + "Key \"%s\" not found in symbol map; " + "Modifier map entry for %s not updated\n", + KeysymText(info->ctx, entry->u.keySym), + ModIndexText(info->ctx, &info->mods, entry->modifier)); + return false; + } + } + + key->modmap |= (1u << entry->modifier); + return true; +} + +static bool +CopySymbolsToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info) +{ + KeyInfo *keyi; + ModMapEntry *mm; + + keymap->symbols_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->symbols_section_name); + + keymap->mods = info->mods; + + darray_steal(info->group_names, + &keymap->group_names, &keymap->num_group_names); + + darray_foreach(keyi, info->keys) + if (!CopySymbolsDefToKeymap(keymap, info, keyi)) + info->errorCount++; + + if (xkb_context_get_log_verbosity(keymap->ctx) > 3) { + struct xkb_key *key; + + xkb_keys_foreach(key, keymap) { + if (key->name == XKB_ATOM_NONE) + continue; + + if (key->num_groups < 1) + log_info(info->ctx, + "No symbols defined for %s\n", + KeyNameText(info->ctx, key->name)); + } + } + + darray_foreach(mm, info->modmaps) + if (!CopyModMapDefToKeymap(keymap, info, mm)) + info->errorCount++; + + /* XXX: If we don't ignore errorCount, things break. */ + return true; +} + +bool +CompileSymbols(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + SymbolsInfo info; + ActionsInfo *actions; + + actions = NewActionsInfo(); + if (!actions) + return false; + + InitSymbolsInfo(&info, keymap, actions, &keymap->mods); + info.default_key.merge = merge; + + HandleSymbolsFile(&info, file, merge); + + if (info.errorCount != 0) + goto err_info; + + if (!CopySymbolsToKeymap(keymap, &info)) + goto err_info; + + ClearSymbolsInfo(&info); + FreeActionsInfo(actions); + return true; + +err_info: + FreeActionsInfo(actions); + ClearSymbolsInfo(&info); + return false; +} diff --git a/src/xkbcomp/types.c b/src/xkbcomp/types.c new file mode 100644 index 0000000..3feaf41 --- /dev/null +++ b/src/xkbcomp/types.c @@ -0,0 +1,744 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "vmod.h" +#include "expr.h" +#include "include.h" + +enum type_field { + TYPE_FIELD_MASK = (1 << 0), + TYPE_FIELD_MAP = (1 << 1), + TYPE_FIELD_PRESERVE = (1 << 2), + TYPE_FIELD_LEVEL_NAME = (1 << 3), +}; + +typedef struct { + enum type_field defined; + enum merge_mode merge; + + xkb_atom_t name; + xkb_mod_mask_t mods; + xkb_level_index_t num_levels; + darray(struct xkb_key_type_entry) entries; + darray(xkb_atom_t) level_names; +} KeyTypeInfo; + +typedef struct { + char *name; + int errorCount; + + darray(KeyTypeInfo) types; + struct xkb_mod_set mods; + + struct xkb_context *ctx; +} KeyTypesInfo; + +/***====================================================================***/ + +static inline const char * +MapEntryTxt(KeyTypesInfo *info, struct xkb_key_type_entry *entry) +{ + return ModMaskText(info->ctx, &info->mods, entry->mods.mods); +} + +static inline const char * +TypeTxt(KeyTypesInfo *info, KeyTypeInfo *type) +{ + return xkb_atom_text(info->ctx, type->name); +} + +static inline const char * +TypeMaskTxt(KeyTypesInfo *info, KeyTypeInfo *type) +{ + return ModMaskText(info->ctx, &info->mods, type->mods); +} + +static inline bool +ReportTypeShouldBeArray(KeyTypesInfo *info, KeyTypeInfo *type, + const char *field) +{ + return ReportShouldBeArray(info->ctx, "key type", field, + TypeTxt(info, type)); +} + +static inline bool +ReportTypeBadType(KeyTypesInfo *info, KeyTypeInfo *type, + const char *field, const char *wanted) +{ + return ReportBadType(info->ctx, "key type", field, + TypeTxt(info, type), wanted); +} + +/***====================================================================***/ + +static void +InitKeyTypesInfo(KeyTypesInfo *info, struct xkb_context *ctx, + const struct xkb_mod_set *mods) +{ + memset(info, 0, sizeof(*info)); + info->ctx = ctx; + info->mods = *mods; +} + +static void +ClearKeyTypeInfo(KeyTypeInfo *type) +{ + darray_free(type->entries); + darray_free(type->level_names); +} + +static void +ClearKeyTypesInfo(KeyTypesInfo *info) +{ + free(info->name); + darray_free(info->types); +} + +static KeyTypeInfo * +FindMatchingKeyType(KeyTypesInfo *info, xkb_atom_t name) +{ + KeyTypeInfo *old; + + darray_foreach(old, info->types) + if (old->name == name) + return old; + + return NULL; +} + +static bool +AddKeyType(KeyTypesInfo *info, KeyTypeInfo *new, bool same_file) +{ + KeyTypeInfo *old; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + + old = FindMatchingKeyType(info, new->name); + if (old) { + if (new->merge == MERGE_REPLACE || new->merge == MERGE_OVERRIDE) { + if ((same_file && verbosity > 0) || verbosity > 9) { + log_warn(info->ctx, + "Multiple definitions of the %s key type; " + "Earlier definition ignored\n", + xkb_atom_text(info->ctx, new->name)); + } + + ClearKeyTypeInfo(old); + *old = *new; + darray_init(new->entries); + darray_init(new->level_names); + return true; + } + + if (same_file) + log_vrb(info->ctx, 4, + "Multiple definitions of the %s key type; " + "Later definition ignored\n", + xkb_atom_text(info->ctx, new->name)); + + ClearKeyTypeInfo(new); + return true; + } + + darray_append(info->types, *new); + return true; +} + +/***====================================================================***/ + +static void +MergeIncludedKeyTypes(KeyTypesInfo *into, KeyTypesInfo *from, + enum merge_mode merge) +{ + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + into->mods = from->mods; + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + if (darray_empty(into->types)) { + into->types = from->types; + darray_init(from->types); + } + else { + KeyTypeInfo *type; + darray_foreach(type, from->types) { + type->merge = (merge == MERGE_DEFAULT ? type->merge : merge); + if (!AddKeyType(into, type, false)) + into->errorCount++; + } + } +} + +static void +HandleKeyTypesFile(KeyTypesInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeKeyTypes(KeyTypesInfo *info, IncludeStmt *include) +{ + KeyTypesInfo included; + + InitKeyTypesInfo(&included, info->ctx, &info->mods); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + KeyTypesInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_TYPES); + if (!file) { + info->errorCount += 10; + ClearKeyTypesInfo(&included); + return false; + } + + InitKeyTypesInfo(&next_incl, info->ctx, &included.mods); + + HandleKeyTypesFile(&next_incl, file, stmt->merge); + + MergeIncludedKeyTypes(&included, &next_incl, stmt->merge); + + ClearKeyTypesInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedKeyTypes(info, &included, include->merge); + ClearKeyTypesInfo(&included); + + return (info->errorCount == 0); +} + +/***====================================================================***/ + +static bool +SetModifiers(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_mod_mask_t mods; + + if (arrayNdx) + log_warn(info->ctx, + "The modifiers field of a key type is not an array; " + "Illegal array subscript ignored\n"); + + if (!ExprResolveModMask(info->ctx, value, MOD_BOTH, &info->mods, &mods)) { + log_err(info->ctx, + "Key type mask field must be a modifier mask; " + "Key type definition ignored\n"); + return false; + } + + if (type->defined & TYPE_FIELD_MASK) { + log_warn(info->ctx, + "Multiple modifier mask definitions for key type %s; " + "Using %s, ignoring %s\n", + xkb_atom_text(info->ctx, type->name), + TypeMaskTxt(info, type), + ModMaskText(info->ctx, &info->mods, mods)); + return false; + } + + type->mods = mods; + return true; +} + +/***====================================================================***/ + +static struct xkb_key_type_entry * +FindMatchingMapEntry(KeyTypeInfo *type, xkb_mod_mask_t mods) +{ + struct xkb_key_type_entry *entry; + + darray_foreach(entry, type->entries) + if (entry->mods.mods == mods) + return entry; + + return NULL; +} + +static bool +AddMapEntry(KeyTypesInfo *info, KeyTypeInfo *type, + struct xkb_key_type_entry *new, bool clobber, bool report) +{ + struct xkb_key_type_entry *old; + + old = FindMatchingMapEntry(type, new->mods.mods); + if (old) { + if (report && old->level != new->level) { + log_warn(info->ctx, + "Multiple map entries for %s in %s; " + "Using %d, ignoring %d\n", + MapEntryTxt(info, new), TypeTxt(info, type), + (clobber ? new->level : old->level) + 1, + (clobber ? old->level : new->level) + 1); + } + else { + log_vrb(info->ctx, 10, + "Multiple occurrences of map[%s]= %d in %s; Ignored\n", + MapEntryTxt(info, new), new->level + 1, + TypeTxt(info, type)); + return true; + } + + if (clobber) { + if (new->level >= type->num_levels) + type->num_levels = new->level + 1; + old->level = new->level; + } + + return true; + } + + if (new->level >= type->num_levels) + type->num_levels = new->level + 1; + + darray_append(type->entries, *new); + return true; +} + +static bool +SetMapEntry(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + struct xkb_key_type_entry entry; + + if (arrayNdx == NULL) + return ReportTypeShouldBeArray(info, type, "map entry"); + + if (!ExprResolveModMask(info->ctx, arrayNdx, MOD_BOTH, &info->mods, + &entry.mods.mods)) + return ReportTypeBadType(info, type, "map entry", "modifier mask"); + + if (entry.mods.mods & (~type->mods)) { + log_vrb(info->ctx, 1, + "Map entry for unused modifiers in %s; " + "Using %s instead of %s\n", + TypeTxt(info, type), + ModMaskText(info->ctx, &info->mods, + entry.mods.mods & type->mods), + MapEntryTxt(info, &entry)); + entry.mods.mods &= type->mods; + } + + if (!ExprResolveLevel(info->ctx, value, &entry.level)) { + log_err(info->ctx, + "Level specifications in a key type must be integer; " + "Ignoring malformed level specification\n"); + return false; + } + + entry.preserve.mods = 0; + + return AddMapEntry(info, type, &entry, true, true); +} + +/***====================================================================***/ + +static bool +AddPreserve(KeyTypesInfo *info, KeyTypeInfo *type, + xkb_mod_mask_t mods, xkb_mod_mask_t preserve_mods) +{ + struct xkb_key_type_entry *entry; + struct xkb_key_type_entry new; + + darray_foreach(entry, type->entries) { + if (entry->mods.mods != mods) + continue; + + /* Map exists without previous preserve (or "None"); override. */ + if (entry->preserve.mods == 0) { + entry->preserve.mods = preserve_mods; + return true; + } + + /* Map exists with same preserve; do nothing. */ + if (entry->preserve.mods == preserve_mods) { + log_vrb(info->ctx, 10, + "Identical definitions for preserve[%s] in %s; " + "Ignored\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type)); + return true; + } + + /* Map exists with different preserve; latter wins. */ + log_vrb(info->ctx, 1, + "Multiple definitions for preserve[%s] in %s; " + "Using %s, ignoring %s\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type), + ModMaskText(info->ctx, &info->mods, preserve_mods), + ModMaskText(info->ctx, &info->mods, entry->preserve.mods)); + + entry->preserve.mods = preserve_mods; + return true; + } + + /* + * Map does not exist, i.e. preserve[] came before map[]. + * Create a map with the specified mask mapping to Level1. The level + * may be overridden later with an explicit map[] statement. + */ + new.level = 0; + new.mods.mods = mods; + new.preserve.mods = preserve_mods; + darray_append(type->entries, new); + return true; +} + +static bool +SetPreserve(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_mod_mask_t mods, preserve_mods; + + if (arrayNdx == NULL) + return ReportTypeShouldBeArray(info, type, "preserve entry"); + + if (!ExprResolveModMask(info->ctx, arrayNdx, MOD_BOTH, &info->mods, &mods)) + return ReportTypeBadType(info, type, "preserve entry", + "modifier mask"); + + if (mods & ~type->mods) { + const char *before, *after; + + before = ModMaskText(info->ctx, &info->mods, mods); + mods &= type->mods; + after = ModMaskText(info->ctx, &info->mods, mods); + + log_vrb(info->ctx, 1, + "Preserve for modifiers not used by the %s type; " + "Index %s converted to %s\n", + TypeTxt(info, type), before, after); + } + + if (!ExprResolveModMask(info->ctx, value, MOD_BOTH, &info->mods, + &preserve_mods)) { + log_err(info->ctx, + "Preserve value in a key type is not a modifier mask; " + "Ignoring preserve[%s] in type %s\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type)); + return false; + } + + if (preserve_mods & ~mods) { + const char *before, *after; + + before = ModMaskText(info->ctx, &info->mods, preserve_mods); + preserve_mods &= mods; + after = ModMaskText(info->ctx, &info->mods, preserve_mods); + + log_vrb(info->ctx, 1, + "Illegal value for preserve[%s] in type %s; " + "Converted %s to %s\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type), before, after); + } + + return AddPreserve(info, type, mods, preserve_mods); +} + +/***====================================================================***/ + +static bool +AddLevelName(KeyTypesInfo *info, KeyTypeInfo *type, + xkb_level_index_t level, xkb_atom_t name, bool clobber) +{ + /* New name. */ + if (level >= darray_size(type->level_names)) { + darray_resize0(type->level_names, level + 1); + goto finish; + } + + /* Same level, same name. */ + if (darray_item(type->level_names, level) == name) { + log_vrb(info->ctx, 10, + "Duplicate names for level %d of key type %s; Ignored\n", + level + 1, TypeTxt(info, type)); + return true; + } + + /* Same level, different name. */ + if (darray_item(type->level_names, level) != XKB_ATOM_NONE) { + const char *old, *new; + old = xkb_atom_text(info->ctx, + darray_item(type->level_names, level)); + new = xkb_atom_text(info->ctx, name); + log_vrb(info->ctx, 1, + "Multiple names for level %d of key type %s; " + "Using %s, ignoring %s\n", + level + 1, TypeTxt(info, type), + (clobber ? new : old), (clobber ? old : new)); + + if (!clobber) + return true; + } + + /* XXX: What about different level, same name? */ + +finish: + darray_item(type->level_names, level) = name; + return true; +} + +static bool +SetLevelName(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_level_index_t level; + xkb_atom_t level_name; + + if (arrayNdx == NULL) + return ReportTypeShouldBeArray(info, type, "level name"); + + if (!ExprResolveLevel(info->ctx, arrayNdx, &level)) + return ReportTypeBadType(info, type, "level name", "integer"); + + if (!ExprResolveString(info->ctx, value, &level_name)) { + log_err(info->ctx, + "Non-string name for level %d in key type %s; " + "Ignoring illegal level name definition\n", + level + 1, xkb_atom_text(info->ctx, type->name)); + return false; + } + + return AddLevelName(info, type, level, level_name, true); +} + +/***====================================================================***/ + +static bool +SetKeyTypeField(KeyTypesInfo *info, KeyTypeInfo *type, + const char *field, ExprDef *arrayNdx, ExprDef *value) +{ + bool ok = false; + enum type_field type_field = 0; + + if (istreq(field, "modifiers")) { + type_field = TYPE_FIELD_MASK; + ok = SetModifiers(info, type, arrayNdx, value); + } + else if (istreq(field, "map")) { + type_field = TYPE_FIELD_MAP; + ok = SetMapEntry(info, type, arrayNdx, value); + } + else if (istreq(field, "preserve")) { + type_field = TYPE_FIELD_PRESERVE; + ok = SetPreserve(info, type, arrayNdx, value); + } + else if (istreq(field, "levelname") || istreq(field, "level_name")) { + type_field = TYPE_FIELD_LEVEL_NAME; + ok = SetLevelName(info, type, arrayNdx, value); + } else { + log_err(info->ctx, + "Unknown field %s in key type %s; Definition ignored\n", + field, TypeTxt(info, type)); + } + + type->defined |= type_field; + return ok; +} + +static bool +HandleKeyTypeBody(KeyTypesInfo *info, VarDef *def, KeyTypeInfo *type) +{ + bool ok = true; + const char *elem, *field; + ExprDef *arrayNdx; + + for (; def; def = (VarDef *) def->common.next) { + ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, + &arrayNdx); + if (!ok) + continue; + + if (elem && istreq(elem, "type")) { + log_err(info->ctx, + "Support for changing the default type has been removed; " + "Statement ignored\n"); + continue; + } + + ok = SetKeyTypeField(info, type, field, arrayNdx, def->value); + } + + return ok; +} + +static bool +HandleKeyTypeDef(KeyTypesInfo *info, KeyTypeDef *def, enum merge_mode merge) +{ + KeyTypeInfo type = { + .defined = 0, + .merge = (def->merge == MERGE_DEFAULT ? merge : def->merge), + .name = def->name, + .mods = 0, + .num_levels = 1, + .entries = darray_new(), + .level_names = darray_new(), + }; + + if (!HandleKeyTypeBody(info, def->body, &type)) { + info->errorCount++; + return false; + } + + if (!AddKeyType(info, &type, true)) { + info->errorCount++; + return false; + } + + return true; +} + +static void +HandleKeyTypesFile(KeyTypesInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeKeyTypes(info, (IncludeStmt *) stmt); + break; + case STMT_TYPE: + ok = HandleKeyTypeDef(info, (KeyTypeDef *) stmt, merge); + break; + case STMT_VAR: + log_err(info->ctx, + "Support for changing the default type has been removed; " + "Statement ignored\n"); + ok = true; + break; + case STMT_VMOD: + ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge); + break; + default: + log_err(info->ctx, + "Key type files may not include other declarations; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, + "Abandoning keytypes file \"%s\"\n", file->name); + break; + } + } +} + +/***====================================================================***/ + +static bool +CopyKeyTypesToKeymap(struct xkb_keymap *keymap, KeyTypesInfo *info) +{ + unsigned num_types; + struct xkb_key_type *types; + + num_types = darray_empty(info->types) ? 1 : darray_size(info->types); + types = calloc(num_types, sizeof(*types)); + if (!types) + return false; + + /* + * If no types were specified, a default unnamed one-level type is + * used for all keys. + */ + if (darray_empty(info->types)) { + struct xkb_key_type *type = &types[0]; + + type->mods.mods = 0; + type->num_levels = 1; + type->entries = NULL; + type->num_entries = 0; + type->name = xkb_atom_intern_literal(keymap->ctx, "default"); + type->level_names = NULL; + type->num_level_names = 0; + } + else { + for (unsigned i = 0; i < num_types; i++) { + KeyTypeInfo *def = &darray_item(info->types, i); + struct xkb_key_type *type = &types[i]; + + type->name = def->name; + type->mods.mods = def->mods; + type->num_levels = def->num_levels; + darray_steal(def->level_names, &type->level_names, &type->num_level_names); + darray_steal(def->entries, &type->entries, &type->num_entries); + } + } + + keymap->types_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->types_section_name); + keymap->num_types = num_types; + keymap->types = types; + keymap->mods = info->mods; + return true; +} + +/***====================================================================***/ + +bool +CompileKeyTypes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + KeyTypesInfo info; + + InitKeyTypesInfo(&info, keymap->ctx, &keymap->mods); + + HandleKeyTypesFile(&info, file, merge); + if (info.errorCount != 0) + goto err_info; + + if (!CopyKeyTypesToKeymap(keymap, &info)) + goto err_info; + + ClearKeyTypesInfo(&info); + return true; + +err_info: + ClearKeyTypesInfo(&info); + return false; +} diff --git a/src/xkbcomp/vmod.c b/src/xkbcomp/vmod.c new file mode 100644 index 0000000..0e8ac12 --- /dev/null +++ b/src/xkbcomp/vmod.c @@ -0,0 +1,107 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "vmod.h" + +bool +HandleVModDef(struct xkb_context *ctx, struct xkb_mod_set *mods, + VModDef *stmt, enum merge_mode merge) +{ + xkb_mod_index_t i; + struct xkb_mod *mod; + xkb_mod_mask_t mapping; + + merge = (merge == MERGE_DEFAULT ? stmt->merge : merge); + + if (stmt->value) { + /* + * This is a statement such as 'virtualModifiers NumLock = Mod1'; + * it sets the vmod-to-real-mod[s] mapping directly instead of going + * through modifier_map or some such. + */ + if (!ExprResolveModMask(ctx, stmt->value, MOD_REAL, mods, &mapping)) { + log_err(ctx, + "Declaration of %s ignored\n", + xkb_atom_text(ctx, stmt->name)); + return false; + } + } + else { + mapping = 0; + } + + xkb_mods_enumerate(i, mod, mods) { + if (mod->name == stmt->name) { + if (mod->type != MOD_VIRT) { + log_err(ctx, + "Can't add a virtual modifier named \"%s\"; " + "there is already a non-virtual modifier with this name! Ignored\n", + xkb_atom_text(ctx, mod->name)); + return false; + } + + if (mod->mapping == mapping) + return true; + + if (mod->mapping != 0) { + xkb_mod_mask_t use, ignore; + + use = (merge == MERGE_OVERRIDE ? mapping : mod->mapping); + ignore = (merge == MERGE_OVERRIDE ? mod->mapping : mapping); + + log_warn(ctx, + "Virtual modifier %s defined multiple times; " + "Using %s, ignoring %s\n", + xkb_atom_text(ctx, stmt->name), + ModMaskText(ctx, mods, use), + ModMaskText(ctx, mods, ignore)); + + mapping = use; + } + + mod->mapping = mapping; + return true; + } + } + + if (mods->num_mods >= XKB_MAX_MODS) { + log_err(ctx, + "Too many modifiers defined (maximum %d)\n", + XKB_MAX_MODS); + return false; + } + + mods->mods[mods->num_mods].name = stmt->name; + mods->mods[mods->num_mods].type = MOD_VIRT; + mods->mods[mods->num_mods].mapping = mapping; + mods->num_mods++; + return true; +} diff --git a/src/xkbcomp/vmod.h b/src/xkbcomp/vmod.h new file mode 100644 index 0000000..546cf7e --- /dev/null +++ b/src/xkbcomp/vmod.h @@ -0,0 +1,34 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_VMOD_H +#define XKBCOMP_VMOD_H + +bool +HandleVModDef(struct xkb_context *ctx, struct xkb_mod_set *mods, + VModDef *stmt, enum merge_mode merge); + +#endif diff --git a/src/xkbcomp/xkbcomp-priv.h b/src/xkbcomp/xkbcomp-priv.h new file mode 100644 index 0000000..6cb774d --- /dev/null +++ b/src/xkbcomp/xkbcomp-priv.h @@ -0,0 +1,124 @@ +/************************************************************ + * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of Silicon Graphics not be + * used in advertising or publicity pertaining to distribution + * of the software without specific prior written permission. + * Silicon Graphics makes no representation about the suitability + * of this software for any purpose. It is provided "as is" + * without any express or implied warranty. + * + * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************/ + +#ifndef XKBCOMP_PRIV_H +#define XKBCOMP_PRIV_H + +#include "keymap.h" +#include "ast.h" + +struct xkb_component_names { + char *keycodes; + char *types; + char *compat; + char *symbols; +}; + +char * +text_v1_keymap_get_as_string(struct xkb_keymap *keymap); + +XkbFile * +XkbParseFile(struct xkb_context *ctx, FILE *file, + const char *file_name, const char *map); + +XkbFile * +XkbParseString(struct xkb_context *ctx, + const char *string, size_t len, + const char *file_name, const char *map); + +void +FreeXkbFile(XkbFile *file); + +XkbFile * +XkbFileFromComponents(struct xkb_context *ctx, + const struct xkb_component_names *kkctgs); + +bool +CompileKeycodes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileKeyTypes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileSymbols(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +/***====================================================================***/ + +static inline bool +ReportNotArray(struct xkb_context *ctx, const char *type, const char *field, + const char *name) +{ + log_err(ctx, + "The %s %s field is not an array; " + "Ignoring illegal assignment in %s\n", + type, field, name); + return false; +} + +static inline bool +ReportShouldBeArray(struct xkb_context *ctx, const char *type, + const char *field, const char *name) +{ + log_err(ctx, + "Missing subscript for %s %s; " + "Ignoring illegal assignment in %s\n", + type, field, name); + return false; +} + +static inline bool +ReportBadType(struct xkb_context *ctx, const char *type, const char *field, + const char *name, const char *wanted) +{ + log_err(ctx, "The %s %s field must be a %s; " + "Ignoring illegal assignment in %s\n", + type, field, wanted, name); + return false; +} + +static inline bool +ReportBadField(struct xkb_context *ctx, const char *type, const char *field, + const char *name) +{ + log_err(ctx, + "Unknown %s field %s in %s; " + "Ignoring assignment to unknown field in %s\n", + type, field, name, name); + return false; +} + +#endif diff --git a/src/xkbcomp/xkbcomp.c b/src/xkbcomp/xkbcomp.c new file mode 100644 index 0000000..48547c9 --- /dev/null +++ b/src/xkbcomp/xkbcomp.c @@ -0,0 +1,141 @@ +/* + * Copyright © 2009 Dan Nicholson + * Copyright © 2012 Intel Corporation + * Copyright © 2012 Ran Benita <ran234@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Dan Nicholson <dbn.lists@gmail.com> + * Ran Benita <ran234@gmail.com> + * Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "rules.h" + +static bool +compile_keymap_file(struct xkb_keymap *keymap, XkbFile *file) +{ + if (file->file_type != FILE_TYPE_KEYMAP) { + log_err(keymap->ctx, + "Cannot compile a %s file alone into a keymap\n", + xkb_file_type_to_string(file->file_type)); + return false; + } + + if (!CompileKeymap(file, keymap, MERGE_OVERRIDE)) { + log_err(keymap->ctx, + "Failed to compile keymap\n"); + return false; + } + + return true; +} + +static bool +text_v1_keymap_new_from_names(struct xkb_keymap *keymap, + const struct xkb_rule_names *rmlvo) +{ + bool ok; + struct xkb_component_names kccgst; + XkbFile *file; + + log_dbg(keymap->ctx, + "Compiling from RMLVO: rules '%s', model '%s', layout '%s', " + "variant '%s', options '%s'\n", + rmlvo->rules, rmlvo->model, rmlvo->layout, rmlvo->variant, + rmlvo->options); + + ok = xkb_components_from_rules(keymap->ctx, rmlvo, &kccgst); + if (!ok) { + log_err(keymap->ctx, + "Couldn't look up rules '%s', model '%s', layout '%s', " + "variant '%s', options '%s'\n", + rmlvo->rules, rmlvo->model, rmlvo->layout, rmlvo->variant, + rmlvo->options); + return false; + } + + log_dbg(keymap->ctx, + "Compiling from KcCGST: keycodes '%s', types '%s', " + "compat '%s', symbols '%s'\n", + kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); + + file = XkbFileFromComponents(keymap->ctx, &kccgst); + + free(kccgst.keycodes); + free(kccgst.types); + free(kccgst.compat); + free(kccgst.symbols); + + if (!file) { + log_err(keymap->ctx, + "Failed to generate parsed XKB file from components\n"); + return false; + } + + ok = compile_keymap_file(keymap, file); + FreeXkbFile(file); + return ok; +} + +static bool +text_v1_keymap_new_from_string(struct xkb_keymap *keymap, + const char *string, size_t len) +{ + bool ok; + XkbFile *xkb_file; + + xkb_file = XkbParseString(keymap->ctx, string, len, "(input string)", NULL); + if (!xkb_file) { + log_err(keymap->ctx, "Failed to parse input xkb string\n"); + return false; + } + + ok = compile_keymap_file(keymap, xkb_file); + FreeXkbFile(xkb_file); + return ok; +} + +static bool +text_v1_keymap_new_from_file(struct xkb_keymap *keymap, FILE *file) +{ + bool ok; + XkbFile *xkb_file; + + xkb_file = XkbParseFile(keymap->ctx, file, "(unknown file)", NULL); + if (!xkb_file) { + log_err(keymap->ctx, "Failed to parse input xkb file\n"); + return false; + } + + ok = compile_keymap_file(keymap, xkb_file); + FreeXkbFile(xkb_file); + return ok; +} + +const struct xkb_keymap_format_ops text_v1_keymap_format_ops = { + .keymap_new_from_names = text_v1_keymap_new_from_names, + .keymap_new_from_string = text_v1_keymap_new_from_string, + .keymap_new_from_file = text_v1_keymap_new_from_file, + .keymap_get_as_string = text_v1_keymap_get_as_string, +}; |
