diff --git a/doc/cool-uris.yaml b/doc/cool-uris.yaml
index b739ee5..9fae6d1 100644
--- a/doc/cool-uris.yaml
+++ b/doc/cool-uris.yaml
@@ -7,6 +7,7 @@ dir_63ce773eee1f9b680e6e312b48cc99ca.html: []
dir_891596f32582d3133e8915e72908625f.html: []
dir_d44c64559bbebec7f509842c48db8b23.html: []
dir_e68e8157741866f444e17edd764ebbae.html: []
+error-index.html: []
files.html: []
functions.html: []
functions_func.html: []
diff --git a/doc/doxygen-extra.css b/doc/doxygen-extra.css
index 104fa0b..569cd2e 100644
--- a/doc/doxygen-extra.css
+++ b/doc/doxygen-extra.css
@@ -31,3 +31,31 @@ a[href^="https://"]::after
background-size: contain;
display: inline-block;
}
+
+/*******************************************************************************
+ * Error index
+ ******************************************************************************/
+
+div.example-container {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: space-between;
+ gap: 1em;
+}
+
+div.example {
+ flex-grow: 1;
+ overflow-x: auto;
+ margin-top: 1em;
+}
+
+div.example-inner {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+div.example-title {
+ padding: 0 0 1em 0;
+ font-style: italic;
+}
diff --git a/doc/message-registry.md b/doc/message-registry.md
new file mode 100644
index 0000000..69027be
--- /dev/null
+++ b/doc/message-registry.md
@@ -0,0 +1,293 @@
+# Error index {#error-index}
+
+
+
+This page lists the warnings and errors generated by xkbcommon.
+There are currently 20 entries.
+
+@todo The documentation of the log messages is a work in progress.
+
+## Index
+
+| Code | Identifier | Description | Type |
+| --------- | ---------------------------- | ----------- | ---- |
+| [XKB-034] | `malformed-number-literal` | Warn on malformed number literals | Error |
+| [XKB-060] | `unsupported-modifier-mask` | Warn on unsupported modifier mask | Error |
+| [XKB-107] | `unrecognized-keysym` | Warn on unrecognized keysyms | Warning |
+| [XKB-183] | `cannot-infer-key-type` | Warn if no key type can be inferred | Warning |
+| [XKB-237] | `unsupported-group-index` | Warn when a group index is not supported | Error |
+| [XKB-286] | `undefined-key-type` | Warn if using an undefined key type | Warning |
+| [XKB-305] | `non-base-group-name` | Warn if a group name was defined for group other than the first one | Warning |
+| [XKB-312] | `unsupported-shift-level` | Warn when a shift level is not supported | Error |
+| [XKB-461] | `conflicting-key-symbol` | Warn if there are conflicting keysyms while merging keys | Warning |
+| [XKB-516] | `extra-symbols-ignored` | TODO: add description | Warning |
+| [XKB-578] | `wrong-field-type` | Warn when a field has not the expected type | Error |
+| [XKB-645] | `unknown-char-escape-sequence` | Warn on unknown escape sequence in string literal | Warning |
+| [XKB-700] | `multiple-groups-at-once` | Warn if a key defines multiple groups at once | Warning |
+| [XKB-769] | `invalid-syntax` | The syntax is invalid and the file cannot be parsed | Error |
+| [XKB-770] | `undefined-keycode` | TODO: add description | Warning |
+| [XKB-800] | `conflicting-modmap` | Warn if there are conflicting modmap definitions | Warning |
+| [XKB-883] | `conflicting-key-action` | Warn if there are conflicting actions while merging keys | Warning |
+| [XKB-893] | `conflicting-key-type` | Warn if there are conflicting key types while merging groups | Warning |
+| [XKB-935] | `conflicting-key-fields` | Warn if there are conflicting fields while merging keys | Warning |
+| [XKB-965] | `unresolved-keymap-symbol` | Warn if using a symbol not defined in the keymap | Warning |
+
+## Details
+
+### XKB-034 – Malformed number literal {#XKB-034}
+
+
+ - Since
- 1.0.0
+ - Type
- Error
+ - Summary
- Warn on malformed number literals
+
+
+xkbcommon can parse the following number literal formats:
+
+- *decimal integer:* 1, 123, etc.
+- *decimal floating-point number:* 1.23, etc.
+- *hexadecimal integer:* prefixed with “0x”: 0x123, 0xff, 0xAB, etc.
+
+
+### XKB-060 – Unsupported modifier mask {#XKB-060}
+
+
+ - Since
- 1.0.0
+ - Type
- Error
+ - Summary
- Warn on unsupported modifier mask
+
+
+### XKB-107 – Unrecognized keysym {#XKB-107}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn on unrecognized keysyms
+
+
+xkbcommon replaces keysyms it does not recognize by the keysym `NoSymbol`.
+
+You may find the list of supported keysyms in
+`include/xkbcommon/xkbcommon-keysyms.h`.
+
+
+#### Examples
+
+
+ Unrecognized keysym “`coma`”
+
+**Error message:**
+
+```
+xkbcommon: WARNING: [XKB-107] de:31:20: unrecognized keysym "coma"
+```
+
+xkbcommon does not recognize the keysym “`coma`”. It is most probably
+a typo for “comma
”.
+See: `XKB_KEY_comma` in `include/xkbcommon/xkbcommon-keysyms.h`.
+
+**Fix:**
+
+
+
+
Before
+```c
+key
{[ coma, semicolon, periodcentered, multiply ]};
+```
+
+
+
+
+
After
+```c
+key
{[ comma, semicolon, periodcentered, multiply ]};
+```
+
+
+
+
+
+### XKB-183 – Cannot infer key type {#XKB-183}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if no key type can be inferred
+
+
+### XKB-237 – Unsupported group index {#XKB-237}
+
+
+ - Since
- 1.0.0
+ - Type
- Error
+ - Summary
- Warn when a group index is not supported
+
+
+xkbcommon supports group index in the range (1..4).
+
+
+### XKB-286 – Undefined key type {#XKB-286}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if using an undefined key type
+
+
+### XKB-305 – Non base group name {#XKB-305}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if a group name was defined for group other than the first one
+
+
+### XKB-312 – Unsupported shift level {#XKB-312}
+
+
+ - Since
- 1.0.0
+ - Type
- Error
+ - Summary
- Warn when a shift level is not supported
+
+
+Shift levels are _one_-indexed. xkbcommon supports two formats of shift levels:
+as numbers and as identifiers `LevelN`, where `N` is in the range (1..8).
+
+
+### XKB-461 – Conflicting key symbol {#XKB-461}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if there are conflicting keysyms while merging keys
+
+
+### XKB-516 – Extra symbols ignored {#XKB-516}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- TODO: add description
+
+
+### XKB-578 – Wrong field type {#XKB-578}
+
+
+ - Since
- 1.0.0
+ - Type
- Error
+ - Summary
- Warn when a field has not the expected type
+
+
+### XKB-645 – Unknown char escape sequence {#XKB-645}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn on unknown escape sequence in string literal
+
+
+xkbcommon support the following escape sequences in string literals:
+
+| Escape sequence | Corresponding character |
+| --------------- | ----------------------------------- |
+| `\b` | `U+0008` Backspace |
+| `\t` | `U+0009` Character tabulation |
+| `\n` | `U+000A` Line feed |
+| `\v` | `U+000B` Vertical tabulation |
+| `\f` | `U+000C` Form feed |
+| `\r` | `U+000D` Carriage return |
+| `\e` | `U+001B` Escape |
+| `\\` | `U+005C` Backslash |
+| `\NNN` | _Octal_ escape, from `\0` to `\777` |
+
+
+### XKB-700 – Multiple groups at once {#XKB-700}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if a key defines multiple groups at once
+
+
+### XKB-769 – Invalid syntax {#XKB-769}
+
+
+ - Since
- 1.0.0
+ - Type
- Error
+ - Summary
- The syntax is invalid and the file cannot be parsed
+
+
+### XKB-770 – Undefined keycode {#XKB-770}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- TODO: add description
+
+
+### XKB-800 – Conflicting modmap {#XKB-800}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if there are conflicting modmap definitions
+
+
+@todo detailed explanation and examples
+
+
+### XKB-883 – Conflicting key action {#XKB-883}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if there are conflicting actions while merging keys
+
+
+### XKB-893 – Conflicting key type {#XKB-893}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if there are conflicting key types while merging groups
+
+
+### XKB-935 – Conflicting key fields {#XKB-935}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if there are conflicting fields while merging keys
+
+
+### XKB-965 – Unresolved keymap symbol {#XKB-965}
+
+
+ - Since
- 1.0.0
+ - Type
- Warning
+ - Summary
- Warn if using a symbol not defined in the keymap
+
+
+[XKB-034]: @ref XKB-034
+[XKB-060]: @ref XKB-060
+[XKB-107]: @ref XKB-107
+[XKB-183]: @ref XKB-183
+[XKB-237]: @ref XKB-237
+[XKB-286]: @ref XKB-286
+[XKB-305]: @ref XKB-305
+[XKB-312]: @ref XKB-312
+[XKB-461]: @ref XKB-461
+[XKB-516]: @ref XKB-516
+[XKB-578]: @ref XKB-578
+[XKB-645]: @ref XKB-645
+[XKB-700]: @ref XKB-700
+[XKB-769]: @ref XKB-769
+[XKB-770]: @ref XKB-770
+[XKB-800]: @ref XKB-800
+[XKB-883]: @ref XKB-883
+[XKB-893]: @ref XKB-893
+[XKB-935]: @ref XKB-935
+[XKB-965]: @ref XKB-965
diff --git a/doc/message-registry.md.jinja b/doc/message-registry.md.jinja
new file mode 100644
index 0000000..e1d40d7
--- /dev/null
+++ b/doc/message-registry.md.jinja
@@ -0,0 +1,74 @@
+# Error index { {#--#} #error-index}
+
+{# NOTE: Prevent Doxygen issue by writing the comment after the first header. #}
+
+
+This page lists the warnings and errors generated by xkbcommon.
+There are currently {{ entries|length }} entries.
+
+@todo The documentation of the log messages is a work in progress.
+
+## Index
+
+| Code | Identifier | Description | Type |
+| --------- | ---------------------------- | ----------- | ---- |
+{% for entry in entries %}
+| [{{entry.message_code}}] | `{{entry.id}}` | {{entry.description|prepend_todo}} | {{entry.type|capitalize}} |
+{% endfor %}
+
+## Details
+
+{% for entry in entries %}
+### {{entry.message_code}} – {{entry.message_name}} { {#--#}#{{entry.message_code}}}
+
+
+{% if entry.removed %}
+ - Added in
- {{entry.added}}
+ - Removed in
- {{entry.removed}}
+{% else %}
+ - Since
- {{entry.added}}
+{% endif %}
+ - Type
- {{entry.type|capitalize}}
+ - Summary
- {{entry.description|prepend_todo}}
+
+
+{% if entry.details %}
+{{entry.details}}
+
+{% endif %}
+{% if entry.examples %}
+#### Examples
+
+{% for example in entry.examples %}
+
+ {{example.name}}
+
+{{example.description}}
+{% if example.before %}
+**Fix:**
+
+
+
+
Before
+{{example.before-}}
+
+
+
+
+
After
+{{example.after-}}
+
+
+
+{% endif %}
+
+
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% for entry in entries %}
+[{{entry.message_code}}]: @ref {{entry.message_code}}
+{% endfor %}
diff --git a/doc/message-registry.yaml b/doc/message-registry.yaml
new file mode 100644
index 0000000..fea4658
--- /dev/null
+++ b/doc/message-registry.yaml
@@ -0,0 +1,176 @@
+# Guidelines:
+# • A message code must always have the same meaning forever.
+# • Codes may be retired or introduced in new releases. In order to avoid
+# clashes, retired codes must not be deleted.
+# • Codes should not themselves reflect classification, e.g. a range for parse
+# errors and a range for each keymap component.
+# • Codes should not be assigned sequentially because it is misleading.
+# • Codes must be in the range 1..999. This range may be extended once every
+# code has be assigned.
+#
+# See the following guidelines for futher details on good practices:
+# https://github.com/haskellfoundation/error-message-index/blob/main/tool-developers.md#code-assignment-recommendations
+
+# NOTE: Field “added: ALWAYS” means that the precise version is unknown and
+# anterior to the introduction of the message registry. It will be replaced by
+# the default version 1.0.0 in the generated documentation. While this is deemed
+# good enough to avoid spelunking commit history, a more precise version would
+# be welcome.
+
+# TODO: fix missing detailed description, examples, resolution
+
+- id: "malformed-number-literal"
+ code: 34
+ added: ALWAYS
+ type: error
+ description: "Warn on malformed number literals"
+ details: |
+ xkbcommon can parse the following number literal formats:
+
+ - *decimal integer:* 1, 123, etc.
+ - *decimal floating-point number:* 1.23, etc.
+ - *hexadecimal integer:* prefixed with “0x”: 0x123, 0xff, 0xAB, etc.
+- id: "unsupported-modifier-mask"
+ code: 60
+ added: ALWAYS
+ type: error
+ description: "Warn on unsupported modifier mask"
+- id: "unrecognized-keysym"
+ code: 107
+ added: ALWAYS
+ type: warning
+ description: "Warn on unrecognized keysyms"
+ details: |
+ xkbcommon replaces keysyms it does not recognize by the keysym `NoSymbol`.
+
+ You may find the list of supported keysyms in
+ `include/xkbcommon/xkbcommon-keysyms.h`.
+ examples:
+ - name: Unrecognized keysym “`coma`”
+ description: |
+ **Error message:**
+
+ ```
+ xkbcommon: WARNING: [XKB-107] de:31:20: unrecognized keysym "coma"
+ ```
+
+ xkbcommon does not recognize the keysym “`coma`”. It is most probably
+ a typo for “comma
”.
+ See: `XKB_KEY_comma` in `include/xkbcommon/xkbcommon-keysyms.h`.
+ before: |
+ ```c
+ key {[ coma, semicolon, periodcentered, multiply ]};
+ ```
+ after: |
+ ```c
+ key {[ comma, semicolon, periodcentered, multiply ]};
+ ```
+- id: "cannot-infer-key-type"
+ code: 183
+ added: ALWAYS
+ type: warning
+ description: "Warn if no key type can be inferred"
+- id: "unsupported-group-index"
+ code: 237
+ added: ALWAYS
+ type: error
+ description: "Warn when a group index is not supported"
+ details: |
+ xkbcommon supports group index in the range (1..{{XKB_MAX_GROUPS}}).
+- id: "undefined-key-type"
+ code: 286
+ added: ALWAYS
+ type: warning
+ description: "Warn if using an undefined key type"
+- id: "non-base-group-name"
+ code: 305
+ added: ALWAYS
+ type: warning
+ description: "Warn if a group name was defined for group other than the first one"
+- id: "unsupported-shift-level"
+ code: 312
+ added: ALWAYS
+ type: error
+ description: "Warn when a shift level is not supported"
+ details: |
+ Shift levels are _one_-indexed. xkbcommon supports two formats of shift levels:
+ as numbers and as identifiers `LevelN`, where `N` is in the range (1..8).
+- id: "conflicting-key-symbol"
+ code: 461
+ added: ALWAYS
+ type: warning
+ description: "Warn if there are conflicting keysyms while merging keys"
+- id: "extra-symbols-ignored"
+ code: 516
+ added: ALWAYS
+ type: warning
+ description: "TODO: add description"
+- id: "wrong-field-type"
+ code: 578
+ added: ALWAYS
+ type: error
+ description: "Warn when a field has not the expected type"
+- id: "unknown-char-escape-sequence"
+ code: 645
+ added: ALWAYS
+ type: warning
+ description: "Warn on unknown escape sequence in string literal"
+ details: |
+ xkbcommon support the following escape sequences in string literals:
+
+ | Escape sequence | Corresponding character |
+ | --------------- | ----------------------------------- |
+ | `\b` | `U+0008` Backspace |
+ | `\t` | `U+0009` Character tabulation |
+ | `\n` | `U+000A` Line feed |
+ | `\v` | `U+000B` Vertical tabulation |
+ | `\f` | `U+000C` Form feed |
+ | `\r` | `U+000D` Carriage return |
+ | `\e` | `U+001B` Escape |
+ | `\\` | `U+005C` Backslash |
+ | `\NNN` | _Octal_ escape, from `\0` to `\777` |
+- id: "multiple-groups-at-once"
+ code: 700
+ added: ALWAYS
+ type: warning
+ description: "Warn if a key defines multiple groups at once"
+- id: "invalid-syntax"
+ code: 769
+ added: ALWAYS
+ type: error
+ description: "The syntax is invalid and the file cannot be parsed"
+- id: "undefined-keycode"
+ code: 770
+ added: ALWAYS
+ type: warning
+ description: "TODO: add description"
+- id: "conflicting-modmap"
+ code: 800
+ added: ALWAYS
+ type: warning
+ description: "Warn if there are conflicting modmap definitions"
+ details: |
+ @todo detailed explanation and examples
+- id: "conflicting-key-action"
+ code: 883
+ added: ALWAYS
+ type: warning
+ description: "Warn if there are conflicting actions while merging keys"
+- id: "conflicting-key-type"
+ code: 893
+ added: ALWAYS
+ type: warning
+ description: "Warn if there are conflicting key types while merging groups"
+- id: "conflicting-key-fields"
+ code: 935
+ added: ALWAYS
+ type: warning
+ description: "Warn if there are conflicting fields while merging keys"
+- id: "unresolved-keymap-symbol"
+ code: 965
+ added: ALWAYS
+ type: warning
+ description: "Warn if using a symbol not defined in the keymap"
+
+# TODO: deprecated keysym
+# TODO: unicode keysym when named and recommended keysym exists
diff --git a/meson.build b/meson.build
index 0d6c8ce..f37d416 100644
--- a/meson.build
+++ b/meson.build
@@ -221,6 +221,7 @@ libxkbcommon_sources = [
'src/keymap.c',
'src/keymap.h',
'src/keymap-priv.c',
+ 'src/messages-codes.h',
'src/scanner-utils.h',
'src/state.c',
'src/text.c',
@@ -816,6 +817,7 @@ You can disable the documentation with -Denable-docs=false.''')
'doc/user-configuration.md',
'doc/rules-format.md',
'doc/keymap-format-text-v1.md',
+ 'doc/message-registry.md',
'include/xkbcommon/xkbcommon.h',
'include/xkbcommon/xkbcommon-compose.h',
'include/xkbcommon/xkbcommon-keysyms.h',
diff --git a/scripts/update-message-registry.py b/scripts/update-message-registry.py
new file mode 100755
index 0000000..785f209
--- /dev/null
+++ b/scripts/update-message-registry.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python3
+
+from __future__ import annotations
+
+import argparse
+from dataclasses import astuple, dataclass
+from pathlib import Path
+import re
+from typing import Callable, Generic, Sequence, TypeVar
+
+import jinja2
+import yaml
+
+
+@dataclass(order=True)
+class Version:
+ """A semantic version number: MAJOR.MINOR.PATCH."""
+
+ UNKNOWN_VERSION = "ALWAYS"
+ DEFAULT_VERSION = "1.0.0"
+
+ major: int
+ minor: int
+ patch: int = 0
+
+ def __str__(self):
+ return ".".join(map(str, astuple(self)))
+
+ @classmethod
+ def parse(cls, raw_version: str) -> Version:
+ if raw_version == cls.UNKNOWN_VERSION:
+ raw_version = cls.DEFAULT_VERSION
+ version = raw_version.split(".")
+ assert 2 <= len(version) <= 3 and all(
+ n.isdecimal() for n in version
+ ), raw_version
+ return Version(*map(int, version))
+
+
+@dataclass
+class Example:
+ """An example in a message entry."""
+
+ name: str
+ description: str
+ before: str | None
+ after: str | None
+
+ @classmethod
+ def parse(cls, entry) -> Example:
+ name = entry.get("name")
+ assert name, entry
+
+ description = entry.get("description")
+ assert description
+
+ before = entry.get("before")
+ after = entry.get("after")
+ # Either none or both of them
+ assert not (bool(before) ^ bool(after))
+
+ return Example(name=name, description=description, before=before, after=after)
+
+
+@dataclass
+class Entry:
+ """An xkbcommon message entry in the message registry"""
+
+ VALID_TYPES = ("warning", "error")
+
+ code: int
+ """A unique strictly positive integer identifier"""
+ id: str
+ """A unique short human-readable string identifier"""
+ type: str
+ """Log level of the message"""
+ description: str
+ """A short description of the meaning of the message"""
+ details: str
+ """A long description of the meaning of the message"""
+ added: Version
+ """Version of xkbcommon the message has been added"""
+ removed: Version | None
+ """Version of xkbcommon the message has been removed"""
+ examples: tuple[Example, ...]
+ """
+ Optional examples of situations in which the message occurs.
+ If the message is an error or a warning, also provide hints on how to fix it.
+ """
+
+ @classmethod
+ def parse(cls, entry) -> Entry:
+ code = entry.get("code")
+ assert code is not None and isinstance(code, int) and code > 0, entry
+
+ id = entry.get("id")
+ assert id is not None, entry
+
+ type_ = entry.get("type")
+ assert type_ in cls.VALID_TYPES, entry
+
+ description = entry.get("description")
+ assert description is not None, entry
+
+ details = entry.get("details", "")
+
+ raw_added = entry.get("added", "")
+ assert raw_added, entry
+
+ added = Version.parse(raw_added)
+ assert added, entry
+
+ if removed := entry.get("removed"):
+ removed = Version.parse(removed)
+ assert added < removed, entry
+
+ if examples := entry.get("examples", ()):
+ examples = tuple(map(Example.parse, examples))
+
+ return Entry(
+ code=code,
+ id=id,
+ type=type_,
+ description=description,
+ added=added,
+ removed=removed,
+ details=details,
+ examples=examples,
+ )
+
+ @property
+ def message_code(self) -> str:
+ """Format the message code for display"""
+ return f"XKB-{self.code:0>3}"
+
+ @property
+ def message_code_constant(self: Entry) -> str:
+ """Returns the C enumeration member denoting the message code"""
+ id = self.id.replace("-", "_").upper()
+ return f"XKB_{self.type.upper()}_{id}"
+
+ @property
+ def message_name(self: Entry):
+ """Format the message string identifier for display"""
+ return self.id.replace("-", " ").capitalize()
+
+
+def prepend_todo(text: str) -> str:
+ if text.startswith("TODO"):
+ return f"""{text[:5]}{text[5:]}"""
+ else:
+ return text
+
+
+def load_message_registry(
+ env: jinja2.Environment, constants: dict[str, int], path: Path
+) -> Sequence[Entry]:
+ # Load the message registry YAML file as a Jinja2 template
+ registry_template = env.get_template(str(path))
+
+ # Load message registry
+ message_registry = sorted(
+ map(Entry.parse, yaml.safe_load(registry_template.render(constants))),
+ key=lambda e: e.code,
+ )
+
+ # Check message codes and identifiers are unique
+ codes: set[int] = set()
+ identifiers: set[str] = set()
+ for n, entry in enumerate(message_registry):
+ if entry.code in codes:
+ raise ValueError("Duplicated code in entry #{n}: {entry.code}")
+ if entry.id in identifiers:
+ raise ValueError("Duplicated identifier in entry #{n}: {entry.id}")
+ codes.add(entry.code)
+ identifiers.add(entry.id)
+
+ return message_registry
+
+
+def generate(
+ registry: Sequence[Entry],
+ env: jinja2.Environment,
+ root: Path,
+ file: Path,
+ skip_removed: bool = False,
+):
+ """Generate a file from its Jinja2 template and the message registry"""
+ template_path = file.with_suffix(f"{file.suffix}.jinja")
+ template = env.get_template(str(template_path))
+ path = root / file
+ script = Path(__file__).name
+ with path.open("wt", encoding="utf-8") as fd:
+ entries = (
+ tuple(filter(lambda e: e.removed is None, registry))
+ if skip_removed
+ else registry
+ )
+ fd.writelines(template.generate(entries=entries, script=script))
+
+
+T = TypeVar("T")
+
+
+@dataclass
+class Constant(Generic[T]):
+ name: str
+ pattern: re.Pattern
+ conversion: Callable[[str], T]
+
+
+def read_constants(path: Path, patterns: Sequence[Constant[T]]) -> dict[str, T]:
+ constants: dict[str, T] = {}
+ patternsʹ = list(patterns)
+ with path.open("rt", encoding="utf-8") as fd:
+ for line in fd:
+ for k, constant in enumerate(patternsʹ):
+ if m := constant.pattern.match(line):
+ constants[constant.name] = constant.conversion(m.group(1))
+ del patternsʹ[k]
+ continue # Expect only one match per line
+ if not patternsʹ:
+ # No more pattern to match
+ break
+ for constant in patternsʹ:
+ print(f"ERROR: could not find constant: {constant.name}.")
+ if patternsʹ:
+ raise ValueError("Some constants were not found.")
+ return constants
+
+
+# Root of the project
+ROOT = Path(__file__).parent.parent
+
+# Parse commands
+parser = argparse.ArgumentParser(description="Generate files from the message registry")
+parser.add_argument(
+ "--root",
+ type=Path,
+ default=ROOT,
+ help="Path to the root of the project (default: %(default)s)",
+)
+
+args = parser.parse_args()
+
+# Read some constants from libxkbcommon that we need
+constants = read_constants(
+ Path(__file__).parent.parent / "src" / "keymap.h",
+ (Constant("XKB_MAX_GROUPS", re.compile("^#define\s+XKB_MAX_GROUPS\s+(\d+)"), int),),
+)
+
+# Configure Jinja
+template_loader = jinja2.FileSystemLoader(args.root, encoding="utf-8")
+jinja_env = jinja2.Environment(
+ loader=template_loader,
+ keep_trailing_newline=True,
+ trim_blocks=True,
+ lstrip_blocks=True,
+)
+jinja_env.filters["prepend_todo"] = prepend_todo
+
+# Load message registry
+message_registry = load_message_registry(
+ jinja_env, constants, Path("doc/message-registry.yaml")
+)
+
+# Generate the files
+generate(
+ message_registry,
+ jinja_env,
+ args.root,
+ Path("src/messages-codes.h"),
+ skip_removed=True,
+)
+generate(message_registry, jinja_env, args.root, Path("doc/message-registry.md"))
diff --git a/src/context.h b/src/context.h
index 488bfad..d5fa117 100644
--- a/src/context.h
+++ b/src/context.h
@@ -27,6 +27,7 @@
#define CONTEXT_H
#include "atom.h"
+#include "messages-codes.h"
struct xkb_context {
int refcnt;
@@ -114,16 +115,31 @@ xkb_context_sanitize_rule_names(struct xkb_context *ctx,
* format is supplied without arguments. Not supplying it would still
* result in an error, though.
*/
+#define xkb_log_with_code(ctx, level, verbosity, msg_id, fmt, ...) \
+ xkb_log(ctx, level, verbosity, "[XKB-%03d] " fmt, \
+ msg_id, ##__VA_ARGS__)
+#define log_dbg_with_code(ctx, id, ...) \
+ xkb_log_with_code((ctx), XKB_LOG_LEVEL_DEBUG, 0, (id), __VA_ARGS__)
#define log_dbg(ctx, ...) \
xkb_log((ctx), XKB_LOG_LEVEL_DEBUG, 0, __VA_ARGS__)
+#define log_info_with_code(ctx, id, ...) \
+ xkb_log_with_code((ctx), XKB_LOG_LEVEL_INFO, 0, (id), __VA_ARGS__)
#define log_info(ctx, ...) \
xkb_log((ctx), XKB_LOG_LEVEL_INFO, 0, __VA_ARGS__)
+#define log_warn_with_code(ctx, id, ...) \
+ xkb_log_with_code((ctx), XKB_LOG_LEVEL_WARNING, 0, (id), __VA_ARGS__)
#define log_warn(ctx, ...) \
- xkb_log((ctx), XKB_LOG_LEVEL_WARNING, 0, __VA_ARGS__)
+ xkb_log((ctx), XKB_LOG_LEVEL_WARNING, 0, __VA_ARGS__)
+#define log_err_with_code(ctx, id, ...) \
+ xkb_log_with_code((ctx), XKB_LOG_LEVEL_ERROR, 0, (id), __VA_ARGS__)
#define log_err(ctx, ...) \
- xkb_log((ctx), XKB_LOG_LEVEL_ERROR, 0, __VA_ARGS__)
+ xkb_log((ctx), XKB_LOG_LEVEL_ERROR, 0, __VA_ARGS__)
+#define log_wsgo_with_code(ctx, id, ...) \
+ xkb_log_with_code((ctx), XKB_LOG_LEVEL_CRITICAL, 0, (id), __VA_ARGS__)
#define log_wsgo(ctx, ...) \
xkb_log((ctx), XKB_LOG_LEVEL_CRITICAL, 0, __VA_ARGS__)
+#define log_vrb_with_code(ctx, vrb, id, ...) \
+ xkb_log_with_code((ctx), XKB_LOG_LEVEL_WARNING, (vrb), (id), __VA_ARGS__)
#define log_vrb(ctx, vrb, ...) \
xkb_log((ctx), XKB_LOG_LEVEL_WARNING, (vrb), __VA_ARGS__)
diff --git a/src/messages-codes.h b/src/messages-codes.h
new file mode 100644
index 0000000..64a6177
--- /dev/null
+++ b/src/messages-codes.h
@@ -0,0 +1,62 @@
+// NOTE: This file has been generated automatically by “update-message-registry.py”.
+// Do not edit manually!
+
+#ifndef MESSAGES_H
+#define MESSAGES_H
+
+#include
+
+/**
+ * @name Codes of the log messages
+ *
+ * @added 1.6.0
+ *
+ */
+enum xkb_message_code {
+ _XKB_LOG_MESSAGE_MIN_CODE = 34,
+ /** Warn on malformed number literals */
+ XKB_ERROR_MALFORMED_NUMBER_LITERAL = 34,
+ /** Warn on unsupported modifier mask */
+ XKB_ERROR_UNSUPPORTED_MODIFIER_MASK = 60,
+ /** Warn on unrecognized keysyms */
+ XKB_WARNING_UNRECOGNIZED_KEYSYM = 107,
+ /** Warn if no key type can be inferred */
+ XKB_WARNING_CANNOT_INFER_KEY_TYPE = 183,
+ /** Warn when a group index is not supported */
+ XKB_ERROR_UNSUPPORTED_GROUP_INDEX = 237,
+ /** Warn if using an undefined key type */
+ XKB_WARNING_UNDEFINED_KEY_TYPE = 286,
+ /** Warn if a group name was defined for group other than the first one */
+ XKB_WARNING_NON_BASE_GROUP_NAME = 305,
+ /** Warn when a shift level is not supported */
+ XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL = 312,
+ /** Warn if there are conflicting keysyms while merging keys */
+ XKB_WARNING_CONFLICTING_KEY_SYMBOL = 461,
+ /** TODO: add description */
+ XKB_WARNING_EXTRA_SYMBOLS_IGNORED = 516,
+ /** Warn when a field has not the expected type */
+ XKB_ERROR_WRONG_FIELD_TYPE = 578,
+ /** Warn on unknown escape sequence in string literal */
+ XKB_WARNING_UNKNOWN_CHAR_ESCAPE_SEQUENCE = 645,
+ /** Warn if a key defines multiple groups at once */
+ XKB_WARNING_MULTIPLE_GROUPS_AT_ONCE = 700,
+ /** The syntax is invalid and the file cannot be parsed */
+ XKB_ERROR_INVALID_SYNTAX = 769,
+ /** TODO: add description */
+ XKB_WARNING_UNDEFINED_KEYCODE = 770,
+ /** Warn if there are conflicting modmap definitions */
+ XKB_WARNING_CONFLICTING_MODMAP = 800,
+ /** Warn if there are conflicting actions while merging keys */
+ XKB_WARNING_CONFLICTING_KEY_ACTION = 883,
+ /** Warn if there are conflicting key types while merging groups */
+ XKB_WARNING_CONFLICTING_KEY_TYPE = 893,
+ /** Warn if there are conflicting fields while merging keys */
+ XKB_WARNING_CONFLICTING_KEY_FIELDS = 935,
+ /** Warn if using a symbol not defined in the keymap */
+ XKB_WARNING_UNRESOLVED_KEYMAP_SYMBOL = 965,
+ _XKB_LOG_MESSAGE_MAX_CODE = 965
+};
+
+typedef uint32_t xkb_message_code_t;
+
+#endif
diff --git a/src/messages-codes.h.jinja b/src/messages-codes.h.jinja
new file mode 100644
index 0000000..2195af4
--- /dev/null
+++ b/src/messages-codes.h.jinja
@@ -0,0 +1,26 @@
+// NOTE: This file has been generated automatically by “{{script}}”.
+// Do not edit manually!
+
+#ifndef MESSAGES_H
+#define MESSAGES_H
+
+#include
+
+/**
+ * @name Codes of the log messages
+ *
+ * @added 1.6.0
+ *
+ */
+enum xkb_message_code {
+ _XKB_LOG_MESSAGE_MIN_CODE = {{ entries[0].code }},
+ {% for entry in entries %}
+ /** {{ entry.description }} */
+ {{ entry.message_code_constant }} = {{ entry.code }},
+ {% endfor %}
+ _XKB_LOG_MESSAGE_MAX_CODE = {{ entries[-1].code }}
+};
+
+typedef uint32_t xkb_message_code_t;
+
+#endif
diff --git a/src/scanner-utils.h b/src/scanner-utils.h
index cede0ff..f4c799e 100644
--- a/src/scanner-utils.h
+++ b/src/scanner-utils.h
@@ -57,15 +57,28 @@ struct scanner {
void *priv;
};
+#define scanner_log_with_code(scanner, level, log_msg_id, fmt, ...) \
+ xkb_log_with_code((scanner)->ctx, (level), 0, log_msg_id, \
+ "%s:%zu:%zu: " fmt "\n", \
+ (scanner)->file_name, \
+ (scanner)->token_line, \
+ (scanner)->token_column, ##__VA_ARGS__)
+
#define scanner_log(scanner, level, fmt, ...) \
xkb_log((scanner)->ctx, (level), 0, \
"%s:%zu:%zu: " fmt "\n", \
- (scanner)->file_name, \
- (scanner)->token_line, (scanner)->token_column, ##__VA_ARGS__)
+ (scanner)->file_name, \
+ (scanner)->token_line, (scanner)->token_column, ##__VA_ARGS__)
+
+#define scanner_err_with_code(scanner, id, fmt, ...) \
+ scanner_log_with_code(scanner, XKB_LOG_LEVEL_ERROR, id, fmt, ##__VA_ARGS__)
#define scanner_err(scanner, fmt, ...) \
scanner_log(scanner, XKB_LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
+#define scanner_warn_with_code(scanner, id, fmt, ...) \
+ scanner_log_with_code(scanner, XKB_LOG_LEVEL_WARNING, id, fmt, ##__VA_ARGS__)
+
#define scanner_warn(scanner, fmt, ...) \
scanner_log(scanner, XKB_LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__)
diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c
index e2d4c40..1410aab 100644
--- a/src/xkbcomp/action.c
+++ b/src/xkbcomp/action.c
@@ -190,10 +190,11 @@ fieldText(enum action_field field)
/***====================================================================***/
static inline bool
-ReportMismatch(struct xkb_context *ctx, enum xkb_action_type action,
- enum action_field field, const char *type)
+ReportMismatch(struct xkb_context *ctx, xkb_message_code_t code,
+ enum xkb_action_type action, enum action_field field,
+ const char *type)
{
- log_err(ctx,
+ log_err_with_code(ctx, code,
"Value of %s field must be of type %s; "
"Action %s definition ignored\n",
fieldText(field), type, ActionTypeText(action));
@@ -243,7 +244,8 @@ CheckBooleanFlag(struct xkb_context *ctx, enum xkb_action_type action,
return ReportActionNotArray(ctx, action, field);
if (!ExprResolveBoolean(ctx, value, &set))
- return ReportMismatch(ctx, action, field, "boolean");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ action, field, "boolean");
if (set)
*flags_inout |= flag;
@@ -274,7 +276,7 @@ CheckModifierField(struct xkb_context *ctx, const struct xkb_mod_set *mods,
}
if (!ExprResolveModMask(ctx, value, MOD_BOTH, mods, mods_rtrn))
- return ReportMismatch(ctx, action,
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action,
ACTION_FIELD_MODIFIERS, "modifier mask");
*flags_inout &= ~ACTION_MODS_LOOKUP_MODMAP;
@@ -300,7 +302,8 @@ CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action,
return ReportActionNotArray(ctx, action, ACTION_FIELD_AFFECT);
if (!ExprResolveEnum(ctx, value, &flags, lockWhich))
- return ReportMismatch(ctx, action, ACTION_FIELD_AFFECT,
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ action, ACTION_FIELD_AFFECT,
"lock, unlock, both, neither");
*flags_inout &= ~(ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK);
@@ -359,8 +362,8 @@ CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action,
}
if (!ExprResolveGroup(ctx, spec, &idx))
- return ReportMismatch(ctx, action, ACTION_FIELD_GROUP,
- "integer (range 1..8)");
+ return ReportMismatch(ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX, action,
+ ACTION_FIELD_GROUP, "integer (range 1..8)");
/* +n, -n are relative, n is absolute. */
if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) {
@@ -416,7 +419,8 @@ HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods,
return ReportActionNotArray(ctx, action->type, field);
if (!ExprResolveInteger(ctx, value, &val))
- return ReportMismatch(ctx, action->type, field, "integer");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "integer");
if (val < INT16_MIN || val > INT16_MAX) {
log_err(ctx,
@@ -462,8 +466,8 @@ HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods,
return ReportActionNotArray(ctx, action->type, field);
if (!ExprResolveButton(ctx, value, &btn))
- return ReportMismatch(ctx, action->type, field,
- "integer (range 1..5)");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "integer (range 1..5)");
if (btn < 0 || btn > 5) {
log_err(ctx,
@@ -487,7 +491,8 @@ HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods,
return ReportActionNotArray(ctx, action->type, field);
if (!ExprResolveInteger(ctx, value, &val))
- return ReportMismatch(ctx, action->type, field, "integer");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "integer");
if (val < 0 || val > 255) {
log_err(ctx,
@@ -524,8 +529,8 @@ HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods,
return ReportActionNotArray(ctx, action->type, field);
if (!ExprResolveEnum(ctx, value, &val, ptrDflts))
- return ReportMismatch(ctx, action->type, field,
- "pointer component");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "pointer component");
return true;
}
else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) {
@@ -546,8 +551,8 @@ HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods,
}
if (!ExprResolveButton(ctx, button, &btn))
- return ReportMismatch(ctx, action->type, field,
- "integer (range 1..5)");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "integer (range 1..5)");
if (btn < 0 || btn > 5) {
log_err(ctx,
@@ -594,8 +599,8 @@ HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods,
}
if (!ExprResolveInteger(ctx, scrn, &val))
- return ReportMismatch(ctx, action->type, field,
- "integer (0..255)");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "integer (0..255)");
if (val < 0 || val > 255) {
log_err(ctx,
@@ -630,8 +635,8 @@ HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods,
return ReportActionNotArray(ctx, action->type, field);
if (!ExprResolveMask(ctx, value, &mask, ctrlMaskNames))
- return ReportMismatch(ctx, action->type, field,
- "controls mask");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+ field, "controls mask");
act->ctrls = mask;
return true;
@@ -658,7 +663,8 @@ HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods,
return ReportActionNotArray(ctx, action->type, field);
if (!ExprResolveInteger(ctx, value, &type))
- return ReportMismatch(ctx, ACTION_TYPE_PRIVATE, field, "integer");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ ACTION_TYPE_PRIVATE, field, "integer");
if (type < 0 || type > 255) {
log_err(ctx,
@@ -696,7 +702,8 @@ HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods,
size_t len;
if (!ExprResolveString(ctx, value, &val))
- return ReportMismatch(ctx, action->type, field, "string");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ action->type, field, "string");
str = xkb_atom_text(ctx, val);
len = strlen(str);
@@ -731,7 +738,8 @@ HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods,
}
if (!ExprResolveInteger(ctx, value, &datum))
- return ReportMismatch(ctx, act->type, field, "integer");
+ return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, act->type,
+ field, "integer");
if (datum < 0 || datum > 255) {
log_err(ctx,
diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c
index b8922c9..121f5f2 100644
--- a/src/xkbcomp/compat.c
+++ b/src/xkbcomp/compat.c
@@ -124,7 +124,8 @@ static inline bool
ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field,
const char *wanted)
{
- return ReportBadType(info->ctx, "symbol interpretation", field,
+ return ReportBadType(info->ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ "symbol interpretation", field,
siText(si, info), wanted);
}
@@ -132,7 +133,8 @@ static inline bool
ReportLedBadType(CompatInfo *info, LedInfo *ledi, const char *field,
const char *wanted)
{
- return ReportBadType(info->ctx, "indicator map", field,
+ return ReportBadType(info->ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ "indicator map", field,
xkb_atom_text(info->ctx, ledi->led.name),
wanted);
}
diff --git a/src/xkbcomp/expr.c b/src/xkbcomp/expr.c
index bbdf038..f935ce6 100644
--- a/src/xkbcomp/expr.c
+++ b/src/xkbcomp/expr.c
@@ -407,8 +407,9 @@ ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr,
return false;
if (result <= 0 || result > XKB_MAX_GROUPS) {
- log_err(ctx, "Group index %u is out of range (1..%d)\n",
- result, XKB_MAX_GROUPS);
+ log_err_with_code(ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX,
+ "Group index %u is out of range (1..%d)\n",
+ result, XKB_MAX_GROUPS);
return false;
}
@@ -429,7 +430,8 @@ ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr,
return false;
if (result < 1) {
- log_err(ctx, "Shift level %d is out of range\n", result);
+ log_err_with_code(ctx, XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL,
+ "Shift level %d is out of range\n", result);
return false;
}
@@ -658,8 +660,9 @@ ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr,
return false;
if (val < XKB_KEYSYM_MIN) {
- log_warn(ctx, "unrecognized keysym \"-0x%x\" (%d)\n",
- (unsigned int) -val, val);
+ log_warn_with_code(ctx, XKB_WARNING_UNRECOGNIZED_KEYSYM,
+ "unrecognized keysym \"-0x%x\" (%d)\n",
+ (unsigned int) -val, val);
return false;
}
@@ -674,7 +677,9 @@ ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr,
return true;
}
- log_warn(ctx, "unrecognized keysym \"0x%x\" (%d)\n", val, val);
+ log_warn_with_code(ctx, XKB_WARNING_UNRECOGNIZED_KEYSYM,
+ "unrecognized keysym \"0x%x\" (%d)\n",
+ (unsigned int) val, val);
return false;
}
diff --git a/src/xkbcomp/keycodes.c b/src/xkbcomp/keycodes.c
index b8abf36..35c84b7 100644
--- a/src/xkbcomp/keycodes.c
+++ b/src/xkbcomp/keycodes.c
@@ -472,7 +472,8 @@ HandleLedNameDef(KeyNamesInfo *info, LedNameDef *def,
char buf[20];
snprintf(buf, sizeof(buf), "%u", def->ndx);
info->errorCount++;
- return ReportBadType(info->ctx, "indicator", "name", buf, "string");
+ return ReportBadType(info->ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+ "indicator", "name", buf, "string");
}
ledi.merge = merge;
diff --git a/src/xkbcomp/parser.y b/src/xkbcomp/parser.y
index 7cf2039..1beb30b 100644
--- a/src/xkbcomp/parser.y
+++ b/src/xkbcomp/parser.y
@@ -46,16 +46,16 @@ struct parser_param {
bool more_maps;
};
-#define parser_err(param, fmt, ...) \
- scanner_err((param)->scanner, fmt, ##__VA_ARGS__)
+#define parser_err(param, error_id, fmt, ...) \
+ scanner_err_with_code((param)->scanner, error_id, fmt, ##__VA_ARGS__)
-#define parser_warn(param, fmt, ...) \
- scanner_warn((param)->scanner, fmt, ##__VA_ARGS__)
+#define parser_warn(param, warning_id, fmt, ...) \
+ scanner_warn_with_code((param)->scanner, warning_id, fmt, ##__VA_ARGS__)
static void
_xkbcommon_error(struct parser_param *param, const char *msg)
{
- parser_err(param, "%s", msg);
+ parser_err(param, XKB_ERROR_INVALID_SYNTAX, "%s", msg);
}
static bool
@@ -728,7 +728,12 @@ KeySyms : OBRACE KeySymList CBRACE
KeySym : IDENT
{
if (!resolve_keysym($1, &$$)) {
- parser_warn(param, "unrecognized keysym \"%s\"", $1);
+ parser_warn(
+ param,
+ XKB_WARNING_UNRECOGNIZED_KEYSYM,
+ "unrecognized keysym \"%s\"",
+ $1
+ );
$$ = XKB_KEY_NoSymbol;
}
free($1);
@@ -737,7 +742,12 @@ KeySym : IDENT
| Integer
{
if ($1 < XKB_KEYSYM_MIN) {
- parser_warn(param, "unrecognized keysym \"%"PRId64"\"", $1);
+ parser_warn(
+ param,
+ XKB_WARNING_UNRECOGNIZED_KEYSYM,
+ "unrecognized keysym \"%"PRId64"\"",
+ $1
+ );
$$ = XKB_KEY_NoSymbol;
}
/* Special case for digits 0..9 */
@@ -748,7 +758,11 @@ KeySym : IDENT
if ($1 <= XKB_KEYSYM_MAX) {
$$ = (xkb_keysym_t) $1;
} else {
- parser_warn(param, "unrecognized keysym \"0x%"PRIx64"\"", $1);
+ parser_warn(
+ param, XKB_WARNING_UNRECOGNIZED_KEYSYM,
+ "unrecognized keysym \"0x%"PRIx64"\" "
+ "(%"PRId64")", $1, $1
+ );
$$ = XKB_KEY_NoSymbol;
}
}
diff --git a/src/xkbcomp/scanner.c b/src/xkbcomp/scanner.c
index e502216..e78f102 100644
--- a/src/xkbcomp/scanner.c
+++ b/src/xkbcomp/scanner.c
@@ -100,7 +100,10 @@ skip_more_whitespace_and_comments:
else if (scanner_chr(s, 'e')) scanner_buf_append(s, '\033');
else if (scanner_oct(s, &o)) scanner_buf_append(s, (char) o);
else {
- scanner_warn(s, "unknown escape sequence in string literal");
+ // TODO: display actual sequence! See: scanner_peek(s).
+ // require escaping any potential control character
+ scanner_warn_with_code(s, XKB_WARNING_UNKNOWN_CHAR_ESCAPE_SEQUENCE,
+ "unknown escape sequence in string literal");
/* Ignore. */
}
} else {
@@ -171,7 +174,8 @@ skip_more_whitespace_and_comments:
/* Number literal (hexadecimal / decimal / float). */
if (number(s, &yylval->num, &tok)) {
if (tok == ERROR_TOK) {
- scanner_err(s, "malformed number literal");
+ scanner_err_with_code(s, XKB_ERROR_MALFORMED_NUMBER_LITERAL,
+ "malformed number literal");
return ERROR_TOK;
}
return tok;
diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c
index f990529..e438139 100644
--- a/src/xkbcomp/symbols.c
+++ b/src/xkbcomp/symbols.c
@@ -61,6 +61,10 @@
#include "include.h"
#include "keysym.h"
+
+// TODO: convert log_err to log_err_with_code
+// TODO: convert log_vrb to log_vrb_with_code
+
enum key_repeat {
KEY_REPEAT_UNDEFINED = 0,
KEY_REPEAT_YES = 1,
@@ -240,13 +244,15 @@ MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber,
xkb_atom_t use = (clobber ? from->type : into->type);
xkb_atom_t ignore = (clobber ? into->type : from->type);
- if (report)
- log_warn(info->ctx,
+ if (report) {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_CONFLICTING_KEY_TYPE,
"Multiple definitions for group %d type of key %s; "
"Using %s, ignoring %s\n",
group + 1, KeyNameText(info->ctx, key_name),
xkb_atom_text(info->ctx, use),
xkb_atom_text(info->ctx, ignore));
+ }
into->type = use;
}
@@ -284,13 +290,15 @@ MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber,
use = (clobber ? &fromLevel->action : &intoLevel->action);
ignore = (clobber ? &intoLevel->action : &fromLevel->action);
- if (report)
- log_warn(info->ctx,
+ if (report) {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_CONFLICTING_KEY_ACTION,
"Multiple actions for level %d/group %u on key %s; "
"Using %s, ignoring %s\n",
i + 1, group + 1, KeyNameText(info->ctx, key_name),
ActionTypeText(use->type),
ActionTypeText(ignore->type));
+ }
intoLevel->action = *use;
}
@@ -307,13 +315,15 @@ MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber,
fromLevel->num_syms = 0;
}
else if (!XkbLevelsSameSyms(fromLevel, intoLevel)) {
- if (report)
- log_warn(info->ctx,
+ if (report) {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_CONFLICTING_KEY_SYMBOL,
"Multiple symbols for level %d/group %u on key %s; "
"Using %s, ignoring %s\n",
i + 1, group + 1, KeyNameText(info->ctx, key_name),
(clobber ? "from" : "to"),
(clobber ? "to" : "from"));
+ }
if (clobber) {
ClearLevelInfo(intoLevel);
@@ -406,12 +416,14 @@ MergeKeys(SymbolsInfo *info, KeyInfo *into, KeyInfo *from, bool same_file)
into->defined |= KEY_FIELD_GROUPINFO;
}
- if (collide)
- log_warn(info->ctx,
+ if (collide) {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_CONFLICTING_KEY_FIELDS,
"Symbol map for key %s redefined; "
"Using %s definition for conflicting fields\n",
KeyNameText(info->ctx, into->name),
(clobber ? "first" : "last"));
+ }
ClearKeyInfo(from);
InitKeyInfo(info->ctx, from);
@@ -464,21 +476,23 @@ AddModMapEntry(SymbolsInfo *info, ModMapEntry *new)
use = (clobber ? new->modifier : old->modifier);
ignore = (clobber ? old->modifier : new->modifier);
- if (new->haveSymbol)
- log_warn(info->ctx,
+ if (new->haveSymbol) {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_CONFLICTING_MODMAP,
"Symbol \"%s\" added to modifier map for multiple modifiers; "
"Using %s, ignoring %s\n",
KeysymText(info->ctx, new->u.keySym),
ModIndexText(info->ctx, &info->mods, use),
ModIndexText(info->ctx, &info->mods, ignore));
- else
- log_warn(info->ctx,
+ } else {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_CONFLICTING_MODMAP,
"Key \"%s\" added to modifier map for multiple modifiers; "
"Using %s, ignoring %s\n",
KeyNameText(info->ctx, new->u.keyName),
ModIndexText(info->ctx, &info->mods, use),
ModIndexText(info->ctx, &info->mods, ignore));
-
+ }
old->modifier = use;
return true;
}
@@ -639,7 +653,7 @@ GetGroupIndex(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx,
}
if (!ExprResolveGroup(info->ctx, arrayNdx, ndx_rtrn)) {
- log_err(info->ctx,
+ log_err_with_code(info->ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX,
"Illegal group index for %s of key %s\n"
"Definition with non-integer array index ignored\n",
name, KeyInfoText(info, keyi));
@@ -812,7 +826,7 @@ SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field,
keyi->defined |= KEY_FIELD_DEFAULT_TYPE;
}
else if (!ExprResolveGroup(info->ctx, arrayNdx, &ndx)) {
- log_err(info->ctx,
+ log_err_with_code(info->ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX,
"Illegal group index for type of key %s; "
"Definition with non-integer array index ignored\n",
KeyInfoText(info, keyi));
@@ -924,7 +938,7 @@ SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field,
xkb_layout_index_t grp;
if (!ExprResolveGroup(info->ctx, value, &grp)) {
- log_err(info->ctx,
+ log_err_with_code(info->ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX,
"Illegal group index for redirect of key %s; "
"Definition with non-integer group ignored\n",
KeyInfoText(info, keyi));
@@ -960,7 +974,7 @@ SetGroupName(SymbolsInfo *info, ExprDef *arrayNdx, ExprDef *value)
}
if (!ExprResolveGroup(info->ctx, arrayNdx, &group)) {
- log_err(info->ctx,
+ log_err_with_code(info->ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX,
"Illegal index in group name definition; "
"Definition with non-integer array index ignored\n");
return false;
@@ -980,7 +994,8 @@ SetGroupName(SymbolsInfo *info, ExprDef *arrayNdx, ExprDef *value)
group_to_use = info->explicit_group;
}
else {
- log_warn(info->ctx,
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_NON_BASE_GROUP_NAME,
"An explicit group was specified for the '%s' map, "
"but it provides a name for a group other than Group1 (%d); "
"Ignoring group name '%s'\n",
@@ -1098,12 +1113,14 @@ SetExplicitGroup(SymbolsInfo *info, KeyInfo *keyi)
}
}
- if (warn)
- log_warn(info->ctx,
+ if (warn) {
+ log_warn_with_code(info->ctx,
+ XKB_WARNING_MULTIPLE_GROUPS_AT_ONCE,
"For the map %s an explicit group specified, "
"but key %s has more than one group defined; "
"All groups except first one will be ignored\n",
info->name, KeyInfoText(info, keyi));
+ }
darray_resize0(keyi->groups, info->explicit_group + 1);
if (info->explicit_group > 0) {
@@ -1371,7 +1388,8 @@ FindTypeForGroup(struct xkb_keymap *keymap, KeyInfo *keyi,
}
if (type_name == XKB_ATOM_NONE) {
- log_warn(keymap->ctx,
+ log_warn_with_code(keymap->ctx,
+ XKB_WARNING_CANNOT_INFER_KEY_TYPE,
"Couldn't find an automatic type for key '%s' group %d with %lu levels; "
"Using the default type\n",
KeyNameText(keymap->ctx, keyi->name), group + 1,
@@ -1384,7 +1402,8 @@ FindTypeForGroup(struct xkb_keymap *keymap, KeyInfo *keyi,
break;
if (i >= keymap->num_types) {
- log_warn(keymap->ctx,
+ log_warn_with_code(keymap->ctx,
+ XKB_WARNING_UNDEFINED_KEY_TYPE,
"The type \"%s\" for key '%s' group %d was not previously defined; "
"Using the default type\n",
xkb_atom_text(keymap->ctx, type_name),
@@ -1417,7 +1436,8 @@ CopySymbolsDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info,
*/
key = XkbKeyByName(keymap, keyi->name, false);
if (!key) {
- log_vrb(info->ctx, 5,
+ log_vrb_with_code(info->ctx, 5,
+ XKB_WARNING_UNDEFINED_KEYCODE,
"Key %s not found in keycodes; Symbols ignored\n",
KeyInfoText(info, keyi));
return false;
@@ -1460,7 +1480,8 @@ CopySymbolsDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info,
if (type->num_levels < darray_size(groupi->levels)) {
struct xkb_level *leveli;
- log_vrb(info->ctx, 1,
+ log_vrb_with_code(info->ctx, 1,
+ XKB_WARNING_EXTRA_SYMBOLS_IGNORED,
"Type \"%s\" has %d levels, but %s has %d levels; "
"Ignoring extra symbols\n",
xkb_atom_text(keymap->ctx, type->name), type->num_levels,
@@ -1512,7 +1533,8 @@ CopyModMapDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info,
if (!entry->haveSymbol) {
key = XkbKeyByName(keymap, entry->u.keyName, true);
if (!key) {
- log_vrb(info->ctx, 5,
+ log_vrb_with_code(info->ctx, 5,
+ XKB_WARNING_UNDEFINED_KEYCODE,
"Key %s not found in keycodes; "
"Modifier map entry for %s not updated\n",
KeyNameText(info->ctx, entry->u.keyName),
@@ -1523,7 +1545,8 @@ CopyModMapDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info,
else {
key = FindKeyForSymbol(keymap, entry->u.keySym);
if (!key) {
- log_vrb(info->ctx, 5,
+ log_vrb_with_code(info->ctx, 5,
+ XKB_WARNING_UNRESOLVED_KEYMAP_SYMBOL,
"Key \"%s\" not found in symbol map; "
"Modifier map entry for %s not updated\n",
KeysymText(info->ctx, entry->u.keySym),
diff --git a/src/xkbcomp/types.c b/src/xkbcomp/types.c
index 3feaf41..e8e82df 100644
--- a/src/xkbcomp/types.c
+++ b/src/xkbcomp/types.c
@@ -89,10 +89,10 @@ ReportTypeShouldBeArray(KeyTypesInfo *info, KeyTypeInfo *type,
}
static inline bool
-ReportTypeBadType(KeyTypesInfo *info, KeyTypeInfo *type,
- const char *field, const char *wanted)
+ReportTypeBadType(KeyTypesInfo *info, xkb_message_code_t code,
+ KeyTypeInfo *type, const char *field, const char *wanted)
{
- return ReportBadType(info->ctx, "key type", field,
+ return ReportBadType(info->ctx, code, "key type", field,
TypeTxt(info, type), wanted);
}
@@ -340,7 +340,8 @@ SetMapEntry(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx,
if (!ExprResolveModMask(info->ctx, arrayNdx, MOD_BOTH, &info->mods,
&entry.mods.mods))
- return ReportTypeBadType(info, type, "map entry", "modifier mask");
+ return ReportTypeBadType(info, XKB_ERROR_UNSUPPORTED_MODIFIER_MASK,
+ type, "map entry", "modifier mask");
if (entry.mods.mods & (~type->mods)) {
log_vrb(info->ctx, 1,
@@ -354,9 +355,9 @@ SetMapEntry(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx,
}
if (!ExprResolveLevel(info->ctx, value, &entry.level)) {
- log_err(info->ctx,
- "Level specifications in a key type must be integer; "
- "Ignoring malformed level specification\n");
+ log_err_with_code(info->ctx, XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL,
+ "Level specifications in a key type must be integer; "
+ "Ignoring malformed level specification\n");
return false;
}
@@ -429,8 +430,8 @@ SetPreserve(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx,
return ReportTypeShouldBeArray(info, type, "preserve entry");
if (!ExprResolveModMask(info->ctx, arrayNdx, MOD_BOTH, &info->mods, &mods))
- return ReportTypeBadType(info, type, "preserve entry",
- "modifier mask");
+ return ReportTypeBadType(info, XKB_ERROR_UNSUPPORTED_MODIFIER_MASK,
+ type, "preserve entry", "modifier mask");
if (mods & ~type->mods) {
const char *before, *after;
@@ -526,7 +527,8 @@ SetLevelName(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx,
return ReportTypeShouldBeArray(info, type, "level name");
if (!ExprResolveLevel(info->ctx, arrayNdx, &level))
- return ReportTypeBadType(info, type, "level name", "integer");
+ return ReportTypeBadType(info, XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL,
+ type, "level name", "integer");
if (!ExprResolveString(info->ctx, value, &level_name)) {
log_err(info->ctx,
diff --git a/src/xkbcomp/xkbcomp-priv.h b/src/xkbcomp/xkbcomp-priv.h
index 6cb774d..5a7765f 100644
--- a/src/xkbcomp/xkbcomp-priv.h
+++ b/src/xkbcomp/xkbcomp-priv.h
@@ -101,10 +101,10 @@ ReportShouldBeArray(struct xkb_context *ctx, const char *type,
}
static inline bool
-ReportBadType(struct xkb_context *ctx, const char *type, const char *field,
- const char *name, const char *wanted)
+ReportBadType(struct xkb_context *ctx, xkb_message_code_t code, const char *type,
+ const char *field, const char *name, const char *wanted)
{
- log_err(ctx, "The %s %s field must be a %s; "
+ log_err_with_code(ctx, code, "The %s %s field must be a %s; "
"Ignoring illegal assignment in %s\n",
type, field, wanted, name);
return false;