[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