ci: some tweaks
[platform/upstream/libxkbcommon.git] / src / keysym.c
index 41098ed..bf49c63 100644 (file)
@@ -69,9 +69,9 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size)
         return -1;
     }
 
-    size_t lo = 0, hi = ARRAY_SIZE(keysym_to_name) - 1;
+    int32_t lo = 0, hi = ARRAY_SIZE(keysym_to_name) - 1;
     while (hi >= lo) {
-        size_t mid = (lo + hi) / 2;
+        int32_t mid = (lo + hi) / 2;
         if (ks > keysym_to_name[mid].keysym) {
             lo = mid + 1;
         } else if (ks < keysym_to_name[mid].keysym) {
@@ -91,12 +91,37 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size)
     return snprintf(buffer, size, "0x%08x", ks);
 }
 
+/*
+ * Parse the numeric part of a 0xXXXX and UXXXX keysym.
+ * Not using strtoul -- it's slower and accepts a bunch of stuff
+ * we don't want to allow, like signs, spaces, even locale stuff.
+ */
+static bool
+parse_keysym_hex(const char *s, uint32_t *out)
+{
+    uint32_t result = 0;
+    int i;
+    for (i = 0; i < 8 && s[i] != '\0'; i++) {
+        result <<= 4;
+        if ('0' <= s[i] && s[i] <= '9')
+            result += s[i] - '0';
+        else if ('a' <= s[i] && s[i] <= 'f')
+            result += 10 + s[i] - 'a';
+        else if ('A' <= s[i] && s[i] <= 'F')
+            result += 10 + s[i] - 'A';
+        else
+            return false;
+    }
+    *out = result;
+    return s[i] == '\0' && i > 0;
+}
+
 XKB_EXPORT xkb_keysym_t
 xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
 {
     const struct name_keysym *entry = NULL;
     char *tmp;
-    xkb_keysym_t val;
+    uint32_t val;
     bool icase = (flags & XKB_KEYSYM_CASE_INSENSITIVE);
 
     if (flags & ~XKB_KEYSYM_CASE_INSENSITIVE)
@@ -107,22 +132,17 @@ xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
      * So do it in a fast path.
      */
     if (!icase) {
-        size_t lo = 0, hi = ARRAY_SIZE(name_to_keysym) - 1;
-        while (hi >= lo) {
-            size_t mid = (lo + hi) / 2;
-            int cmp = strcmp(name, get_name(&name_to_keysym[mid]));
-            if (cmp > 0)
-                lo = mid + 1;
-            else if (cmp < 0)
-                hi = mid - 1;
-            else
-                return name_to_keysym[mid].keysym;
+        size_t pos = keysym_name_perfect_hash(name);
+        if (pos < ARRAY_SIZE(name_to_keysym)) {
+            const char *s = get_name(&name_to_keysym[pos]);
+            if (strcmp(name, s) == 0)
+                return name_to_keysym[pos].keysym;
         }
     }
     /*
     * Find the correct keysym for case-insensitive match.
     *
-    * The name_to_keysym_icase table is sorted by istrcmp(). So the binary
+    * The name_to_keysym table is sorted by istrcmp(). So the binary
     * search may return _any_ of all possible case-insensitive duplicates. This
     * code searches the entry, all previous and all next entries that match by
     * case-insensitive comparison and returns the "best" case-insensitive
@@ -135,16 +155,16 @@ xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
     * lower-case match is enough in this case.
     */
     else {
-        size_t lo = 0, hi = ARRAY_SIZE(name_to_keysym_icase) - 1;
+        int32_t lo = 0, hi = ARRAY_SIZE(name_to_keysym) - 1;
         while (hi >= lo) {
-            size_t mid = (lo + hi) / 2;
-            int cmp = istrcmp(name, get_name(&name_to_keysym_icase[mid]));
+            int32_t mid = (lo + hi) / 2;
+            int cmp = istrcmp(name, get_name(&name_to_keysym[mid]));
             if (cmp > 0) {
                 lo = mid + 1;
             } else if (cmp < 0) {
                 hi = mid - 1;
             } else {
-                entry = &name_to_keysym_icase[mid];
+                entry = &name_to_keysym[mid];
                 break;
             }
         }
@@ -154,14 +174,14 @@ xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
             if (icase && xkb_keysym_is_lower(entry->keysym))
                 return entry->keysym;
 
-            for (iter = entry - 1; iter >= name_to_keysym_icase; --iter) {
+            for (iter = entry - 1; iter >= name_to_keysym; --iter) {
                 if (istrcmp(get_name(iter), get_name(entry)) != 0)
                     break;
                 if (xkb_keysym_is_lower(iter->keysym))
                     return iter->keysym;
             }
 
-            last = name_to_keysym_icase + ARRAY_SIZE(name_to_keysym_icase);
+            last = name_to_keysym + ARRAY_SIZE(name_to_keysym);
             for (iter = entry + 1; iter < last; ++iter) {
                 if (istrcmp(get_name(iter), get_name(entry)) != 0)
                     break;
@@ -174,24 +194,21 @@ xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
     }
 
     if (*name == 'U' || (icase && *name == 'u')) {
-        val = strtoul(&name[1], &tmp, 16);
-        if (tmp && *tmp != '\0')
+        if (!parse_keysym_hex(&name[1], &val))
             return XKB_KEY_NoSymbol;
 
         if (val < 0x20 || (val > 0x7e && val < 0xa0))
             return XKB_KEY_NoSymbol;
         if (val < 0x100)
-            return val;
+            return (xkb_keysym_t) val;
         if (val > 0x10ffff)
             return XKB_KEY_NoSymbol;
-        return val | 0x01000000;
+        return (xkb_keysym_t) val | 0x01000000;
     }
     else if (name[0] == '0' && (name[1] == 'x' || (icase && name[1] == 'X'))) {
-        val = strtoul(&name[2], &tmp, 16);
-        if (tmp && *tmp != '\0')
+        if (!parse_keysym_hex(&name[2], &val))
             return XKB_KEY_NoSymbol;
-
-        return val;
+        return (xkb_keysym_t) val;
     }
 
     /* Stupid inconsistency between the headers and XKeysymDB: the former has