diff --git a/meson.build b/meson.build index f37d416..ce17b1d 100644 --- a/meson.build +++ b/meson.build @@ -543,6 +543,15 @@ You can disable the Wayland xkbcli programs with -Denable-wayland=false.''') install_dir: dir_libexec) install_man('tools/xkbcli-list.1') endif + + executable('xkb-check-messages', + 'tools/check-messages.c', + 'tools/messages.c', + 'tools/messages.h', + 'src/messages-codes.h', + dependencies: [tools_dep], + include_directories: [include_directories('src', 'include', 'tools')], + install: false) endif @@ -698,6 +707,17 @@ test( executable('test-modifiers', 'test/modifiers.c', dependencies: test_dep), env: test_env, ) +test( + 'messages', + executable( + 'test-messages', + 'test/messages.c', + 'tools/messages.c', + 'tools/messages.h', + include_directories: include_directories('src', 'include', 'tools'), + dependencies: test_dep), + env: test_env, +) if get_option('enable-x11') test( 'x11', diff --git a/scripts/update-message-registry.py b/scripts/update-message-registry.py index 785f209..402835b 100755 --- a/scripts/update-message-registry.py +++ b/scripts/update-message-registry.py @@ -272,4 +272,7 @@ generate( Path("src/messages-codes.h"), skip_removed=True, ) +generate( + message_registry, jinja_env, args.root, Path("tools/messages.c"), skip_removed=True +) generate(message_registry, jinja_env, args.root, Path("doc/message-registry.md")) diff --git a/test/messages.c b/test/messages.c new file mode 100644 index 0000000..b272a09 --- /dev/null +++ b/test/messages.c @@ -0,0 +1,63 @@ +/* + * Copyright © 2023 Pierre Le Marre + * + * 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 +#include + +#include "test.h" +#include "messages-codes.h" +#include "messages.h" + +static void +test_message_get(void) +{ + const struct xkb_message_entry* entry; + /* Invalid codes */ + /* NOTE: 0 must not be a valid message code */ + entry = xkb_message_get(0); + assert(entry == NULL); + entry = xkb_message_get(_XKB_LOG_MESSAGE_MIN_CODE - 1); + assert(entry == NULL); + entry = xkb_message_get(_XKB_LOG_MESSAGE_MAX_CODE + 1); + assert(entry == NULL); + + /* Valid codes */ + entry = xkb_message_get(_XKB_LOG_MESSAGE_MIN_CODE); + assert(entry != NULL); + entry = xkb_message_get(XKB_WARNING_CANNOT_INFER_KEY_TYPE); + assert(entry != NULL); + entry = xkb_message_get(XKB_ERROR_INVALID_SYNTAX); + assert(entry != NULL); + entry = xkb_message_get(XKB_WARNING_CONFLICTING_KEY_FIELDS); + assert(entry != NULL); + entry = xkb_message_get(_XKB_LOG_MESSAGE_MAX_CODE); + assert(entry != NULL); +} + +int +main(void) +{ + test_message_get(); +} diff --git a/tools/check-messages.c b/tools/check-messages.c new file mode 100644 index 0000000..c7e7cf1 --- /dev/null +++ b/tools/check-messages.c @@ -0,0 +1,94 @@ +/* + * Copyright © 2023 Pierre Le Marre + * + * 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 +#include +#include + +#include "utils.h" +#include "messages-codes.h" +#include "messages.h" + +static xkb_message_code_t +parse_message_code(char *raw_code) { + xkb_message_code_t code; + code = atoi(raw_code); + if (!code && strstr(raw_code, "XKB-")) { + return atoi(&(raw_code[4])); + } else { + return code; + } +} + +static void +usage(char **argv) +{ + printf("Usage: %s MESSAGE_CODES\n" + "\n" + "Check whether the given message codes are supported.\n", + argv[0]); + + const struct xkb_message_entry *xkb_messages; + size_t count = xkb_message_get_all(&xkb_messages); + + printf("\nCurrent supported messages:\n"); + for (size_t idx = 0; idx < count; idx++) { + printf("- XKB-%03u: %s\n", xkb_messages[idx].code, xkb_messages[idx].label); + } +} + +#define XKB_CHECK_MSG_ERROR_PREFIX "xkb-check-messages: ERROR: " +#define MALFORMED_MESSAGE (1 << 2) +#define UNSUPPORTED_MESSAGE (1 << 3) + +int main(int argc, char **argv) { + if (argc <= 1) { + usage(argv); + return EXIT_INVALID_USAGE; + } + + int rc = 0; + xkb_message_code_t code; + const struct xkb_message_entry* entry; + for (int k=1; k < argc; k++) { + code = parse_message_code(argv[k]); + if (!code) { + fprintf(stderr, + XKB_CHECK_MSG_ERROR_PREFIX "Malformed message code: %s\n", + argv[k]); + rc |= MALFORMED_MESSAGE; + continue; + } + entry = xkb_message_get(code); + if (entry == NULL) { + fprintf(stderr, + XKB_CHECK_MSG_ERROR_PREFIX "Unsupported message code: %s\n", + argv[k]); + rc |= UNSUPPORTED_MESSAGE; + } + } + + return rc; +} diff --git a/tools/messages.c b/tools/messages.c new file mode 100644 index 0000000..365792b --- /dev/null +++ b/tools/messages.c @@ -0,0 +1,83 @@ +/* + * NOTE: This file has been generated automatically by “update-message-registry.py”. + * Do not edit manually! + * + */ + +/* + * Copyright © 2023 Pierre Le Marre + * + * 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 +#include +#include + +#include "messages-codes.h" +#include "messages.h" +#include "utils.h" + +static const struct xkb_message_entry xkb_messages[] = { + {XKB_ERROR_MALFORMED_NUMBER_LITERAL, "Malformed number literal"}, + {XKB_ERROR_UNSUPPORTED_MODIFIER_MASK, "Unsupported modifier mask"}, + {XKB_WARNING_UNRECOGNIZED_KEYSYM, "Unrecognized keysym"}, + {XKB_WARNING_CANNOT_INFER_KEY_TYPE, "Cannot infer key type"}, + {XKB_ERROR_UNSUPPORTED_GROUP_INDEX, "Unsupported group index"}, + {XKB_WARNING_UNDEFINED_KEY_TYPE, "Undefined key type"}, + {XKB_WARNING_NON_BASE_GROUP_NAME, "Non base group name"}, + {XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL, "Unsupported shift level"}, + {XKB_WARNING_CONFLICTING_KEY_SYMBOL, "Conflicting key symbol"}, + {XKB_WARNING_EXTRA_SYMBOLS_IGNORED, "Extra symbols ignored"}, + {XKB_ERROR_WRONG_FIELD_TYPE, "Wrong field type"}, + {XKB_WARNING_UNKNOWN_CHAR_ESCAPE_SEQUENCE, "Unknown char escape sequence"}, + {XKB_WARNING_MULTIPLE_GROUPS_AT_ONCE, "Multiple groups at once"}, + {XKB_ERROR_INVALID_SYNTAX, "Invalid syntax"}, + {XKB_WARNING_UNDEFINED_KEYCODE, "Undefined keycode"}, + {XKB_WARNING_CONFLICTING_MODMAP, "Conflicting modmap"}, + {XKB_WARNING_CONFLICTING_KEY_ACTION, "Conflicting key action"}, + {XKB_WARNING_CONFLICTING_KEY_TYPE, "Conflicting key type"}, + {XKB_WARNING_CONFLICTING_KEY_FIELDS, "Conflicting key fields"}, + {XKB_WARNING_UNRESOLVED_KEYMAP_SYMBOL, "Unresolved keymap symbol"} +}; + +int +xkb_message_get_all(const struct xkb_message_entry **messages) +{ + *messages = xkb_messages; + return ARRAY_SIZE(xkb_messages); +} + +const struct xkb_message_entry* +xkb_message_get(xkb_message_code_t code) +{ + if (code < _XKB_LOG_MESSAGE_MIN_CODE || code > _XKB_LOG_MESSAGE_MAX_CODE) + return NULL; + + for (size_t idx = 0; idx < ARRAY_SIZE(xkb_messages); idx++) { + if (xkb_messages[idx].code == code) + return &xkb_messages[idx]; + } + + /* no matching message code found */ + return NULL; +} diff --git a/tools/messages.c.jinja b/tools/messages.c.jinja new file mode 100644 index 0000000..de0a9d4 --- /dev/null +++ b/tools/messages.c.jinja @@ -0,0 +1,67 @@ +/* + * NOTE: This file has been generated automatically by “{{script}}”. + * Do not edit manually! + * + */ + +/* + * Copyright © 2023 Pierre Le Marre + * + * 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 +#include +#include + +#include "messages-codes.h" +#include "messages.h" +#include "utils.h" + +static const struct xkb_message_entry xkb_messages[] = { + {% for entry in entries %} + { {#--#}{{ entry.message_code_constant}}, "{{entry.message_name}}"}{{ "" if loop.last else "," }} + {% endfor %} +}; + +int +xkb_message_get_all(const struct xkb_message_entry **messages) +{ + *messages = xkb_messages; + return ARRAY_SIZE(xkb_messages); +} + +const struct xkb_message_entry* +xkb_message_get(xkb_message_code_t code) +{ + {# Binary search seems overkill for now #} + if (code < _XKB_LOG_MESSAGE_MIN_CODE || code > _XKB_LOG_MESSAGE_MAX_CODE) + return NULL; + + for (size_t idx = 0; idx < ARRAY_SIZE(xkb_messages); idx++) { + if (xkb_messages[idx].code == code) + return &xkb_messages[idx]; + } + + /* no matching message code found */ + return NULL; +} diff --git a/tools/messages.h b/tools/messages.h new file mode 100644 index 0000000..95db9df --- /dev/null +++ b/tools/messages.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2023 Pierre Le Marre + * + * 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 CHECK_MESSAGES_H +#define CHECK_MESSAGES_H + +#include "messages-codes.h" + +struct xkb_message_entry { + const xkb_message_code_t code; + const char *label; +}; + +int +xkb_message_get_all(const struct xkb_message_entry **xkb_messages); + +const struct xkb_message_entry* +xkb_message_get(xkb_message_code_t code); + +#endif