Similar to strcasecmp() this function does an case-insensitive match. The match will _always_ be case-insensitive and return the lower-case character if there are conflicts. So if you want an exact match, you should use the normal lookup first and fallback to this.
This should _never_ be used for protocol-parsing or parsing of computer-generated content. This function may return other values than expected so it should only be used as an hint to the user what they might have _meant_ if no case-sensitive match can be found. Signed-off-by: David Herrmann <dh.herrm...@googlemail.com> --- Makefile.am | 6 ++++++ makekeys/makekeys.c | 14 ++++++++++---- src/keysym.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/keysym.c | 29 +++++++++++++++++++++++++++++ xkbcommon/xkbcommon.h | 20 ++++++++++++++++++++ 5 files changed, 115 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 763e1bc..b5722e5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -96,17 +96,23 @@ noinst_PROGRAMS = makekeys/makekeys makekeys_makekeys_SOURCES = makekeys/makekeys.c CLEANFILES += \ makekeys/name2key.gperf \ + makekeys/case2key.gperf \ makekeys/key2name.gperf src/ks_tables.h: \ $(top_builddir)/makekeys/name2key.gperf \ + $(top_builddir)/makekeys/case2key.gperf \ $(top_builddir)/makekeys/key2name.gperf $(AM_V_GEN)$(GPERF) --language="ANSI-C" $(top_builddir)/makekeys/name2key.gperf > $@ + $(AM_V_GEN)$(GPERF) --language="ANSI-C" $(top_builddir)/makekeys/case2key.gperf >> $@ $(AM_V_GEN)$(GPERF) --language="ANSI-C" $(top_builddir)/makekeys/key2name.gperf >> $@ $(top_builddir)/makekeys/name2key.gperf: $(top_builddir)/makekeys/makekeys$(EXEEXT) $(AM_V_GEN)$< --name2key $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h > $@ +$(top_builddir)/makekeys/case2key.gperf: $(top_builddir)/makekeys/makekeys$(EXEEXT) + $(AM_V_GEN)$< --case2key $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h > $@ + $(top_builddir)/makekeys/key2name.gperf: $(top_builddir)/makekeys/makekeys$(EXEEXT) $(AM_V_GEN)$< --key2name $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h > $@ diff --git a/makekeys/makekeys.c b/makekeys/makekeys.c index 22ece2f..2311cd1 100644 --- a/makekeys/makekeys.c +++ b/makekeys/makekeys.c @@ -320,9 +320,10 @@ static bool create_file(FILE *out, const char *prefix, unsigned int flags, /* * Main Entry - * This checks the argument-list for one of --name2key and --key2name. All other - * arguments are taken as file-list that should be parsed. The generated gperf - * input is written to STDOUT. + * This checks the argument-list for one of --name2key, --case2key and + * --key2name. + * All other arguments are taken as file-list that should be parsed. The + * generated gperf input is written to STDOUT. */ int main(int argc, char **argv) { @@ -336,6 +337,11 @@ int main(int argc, char **argv) prefix = "name2key_"; flags = FLAG_USE_NULL_STRINGS; flags |= FLAG_COMPARE_LENGTHS; + } else if (!strcmp(argv[i], "--case2key")) { + prefix = "case2key_"; + flags = FLAG_USE_NULL_STRINGS; + flags |= FLAG_COMPARE_LENGTHS; + flags |= FLAG_IGNORE_CASE; } else if (!strcmp(argv[i], "--key2name")) { prefix = "key2name_"; flags = FLAG_USE_NULL_STRINGS; @@ -348,7 +354,7 @@ int main(int argc, char **argv) } if (!prefix) { - fprintf(stderr, "none of --name2key and --key2name specified\n"); + fprintf(stderr, "none of --name2key, --case2key and --key2name specified\n"); return EXIT_FAILURE; } diff --git a/src/keysym.c b/src/keysym.c index 3ee0499..2218578 100644 --- a/src/keysym.c +++ b/src/keysym.c @@ -56,6 +56,8 @@ const struct name2key_entry *name2key_lookup(register const char *str, register unsigned int len); +const struct case2key_entry *case2key_lookup(register const char *str, + register unsigned int len); const struct key2name_entry *key2name_lookup(register const char *str, register unsigned int len); @@ -133,6 +135,54 @@ xkb_keysym_from_name(const char *s) return XKB_KEY_NoSymbol; } +XKB_EXPORT xkb_keysym_t +xkb_keysym_from_casename(const char *s) +{ + const struct case2key_entry *e; + char *tmp; + xkb_keysym_t val, ret; + + e = case2key_lookup(s, strlen(s)); + if (e) + return e->value; + + if (*s == 'U' || *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' || 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 (strncasecmp(s, "XF86_", 5) == 0) { + tmp = strdup(s); + if (!tmp) + return XKB_KEY_NoSymbol; + memmove(&tmp[4], &tmp[5], strlen(s) - 5 + 1); + ret = xkb_keysym_from_casename(tmp); + free(tmp); + return ret; + } + + return XKB_KEY_NoSymbol; +} + enum keysym_case { NONE, LOWERCASE, diff --git a/test/keysym.c b/test/keysym.c index 1bf704b..2eb0f78 100644 --- a/test/keysym.c +++ b/test/keysym.c @@ -37,6 +37,19 @@ test_string(const char *string, xkb_keysym_t expected) } static int +test_casestring(const char *string, xkb_keysym_t expected) +{ + xkb_keysym_t keysym; + + keysym = xkb_keysym_from_casename(string); + + fprintf(stderr, "Expected casestring %s -> %x\n", string, expected); + fprintf(stderr, "Received casestring %s -> %x\n\n", string, keysym); + + return keysym == expected; +} + +static int test_keysym(xkb_keysym_t keysym, const char *expected) { char s[16]; @@ -80,6 +93,22 @@ main(void) assert(test_keysym(0x1008FE20, "XF86Ungrab")); assert(test_keysym(0x01001234, "U1234")); + assert(test_casestring("Undo", 0xFF65)); + assert(test_casestring("UNDO", 0xFF65)); + assert(test_casestring("A", 0x61)); + assert(test_casestring("a", 0x61)); + assert(test_casestring("ThisKeyShouldNotExist", XKB_KEY_NoSymbol)); + assert(test_casestring("XF86_Switch_vT_5", 0x1008FE05)); + assert(test_casestring("xF86_SwitcH_VT_5", 0x1008FE05)); + assert(test_casestring("xF86SwiTch_VT_5", 0x1008FE05)); + assert(test_casestring("xF86Switch_vt_5", 0x1008FE05)); + assert(test_casestring("VoidSymbol", 0xFFFFFF)); + assert(test_casestring("vOIDsymBol", 0xFFFFFF)); + assert(test_casestring("U4567", 0x1004567)); + assert(test_casestring("u4567", 0x1004567)); + assert(test_casestring("0x10203040", 0x10203040)); + assert(test_casestring("0X10203040", 0x10203040)); + assert(test_utf8(XKB_KEY_y, "y")); assert(test_utf8(XKB_KEY_u, "u")); assert(test_utf8(XKB_KEY_m, "m")); diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h index a6b0fa8..f2f6868 100644 --- a/xkbcommon/xkbcommon.h +++ b/xkbcommon/xkbcommon.h @@ -230,6 +230,26 @@ xkb_keysym_t xkb_keysym_from_name(const char *name); /** + * @brief Get a keysym from its name (case insensitive) + * + * @param name The name of a keysym. + * + * @remark This does the same as xkb_keysym_from_name() but performs a + * case-insensitive lookup. When conflicts are detected, the lower-case keysm is + * reported. So searching for "A" returns XKB_KEY_a, not XKB_KEY_A. + * + * This should only be used as fallback if xkb_keysym_from_name() failed. You + * should also print a warning that this fallback was used and never rely on it + * to work correctly. It is only available for error-recovery on wrong + * user-input or to print suggestions to the users what they probably meant. + * + * @returns The keysym, if name is valid. Otherwise, XKB_KEY_NoSymbol is + * returned. + */ +xkb_keysym_t +xkb_keysym_from_casename(const char *name); + +/** * @brief Get the Unicode/UTF-8 representation of a keysym. * * @param[in] keysym The keysym. -- 1.7.12.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel