[PATCH libxkbcommon v2 2/2] Add xkb_keysym_from_casename() with case-insensitive look
David Herrmann
dh.herrmann at googlemail.com
Tue Oct 2 10:51:54 PDT 2012
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.herrmann at 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
More information about the wayland-devel
mailing list