compose/parser: parse (! mods) properly We don't actually do anything with them. But if someone uses them we can at least not choke. Signed-off-by: Ran Benita <ran234@gmail.com>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
diff --git a/src/compose/parser.c b/src/compose/parser.c
index 10433fe..d1ac013 100644
--- a/src/compose/parser.c
+++ b/src/compose/parser.c
@@ -104,17 +104,14 @@ cached_keysym_from_name(struct keysym_from_name_cache *cache,
* Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c.
* See also the XCompose(5) manpage.
*
- * We don't support the MODIFIER rules, which are commented out.
- *
* FILE ::= { [PRODUCTION] [COMMENT] "\n" | INCLUDE }
* INCLUDE ::= "include" '"' INCLUDE_STRING '"'
* PRODUCTION ::= LHS ":" RHS [ COMMENT ]
* COMMENT ::= "#" {<any character except null or newline>}
* LHS ::= EVENT { EVENT }
- * EVENT ::= "<" keysym ">"
- * # EVENT ::= [MODIFIER_LIST] "<" keysym ">"
- * # MODIFIER_LIST ::= ("!" {MODIFIER} ) | "None"
- * # MODIFIER ::= ["~"] modifier_name
+ * EVENT ::= [MODIFIER_LIST] "<" keysym ">"
+ * MODIFIER_LIST ::= ("!" {MODIFIER} ) | "None"
+ * MODIFIER ::= ["~"] modifier_name
* RHS ::= ( STRING | keysym | STRING keysym )
* STRING ::= '"' { CHAR } '"'
* CHAR ::= GRAPHIC_CHAR | ESCAPED_CHAR
@@ -141,8 +138,10 @@ enum rules_token {
TOK_INCLUDE_STRING,
TOK_LHS_KEYSYM,
TOK_COLON,
+ TOK_BANG,
+ TOK_TILDE,
TOK_STRING,
- TOK_RHS_KEYSYM,
+ TOK_IDENT,
TOK_ERROR
};
@@ -198,6 +197,10 @@ skip_more_whitespace_and_comments:
/* Colon. */
if (chr(s, ':'))
return TOK_COLON;
+ if (chr(s, '!'))
+ return TOK_BANG;
+ if (chr(s, '~'))
+ return TOK_TILDE;
/* String literal. */
if (chr(s, '\"')) {
@@ -244,7 +247,7 @@ skip_more_whitespace_and_comments:
return TOK_STRING;
}
- /* RHS keysym or include. */
+ /* Identifier or include. */
if (is_alpha(peek(s)) || peek(s) == '_') {
s->buf_pos = 0;
while (is_alnum(peek(s)) || peek(s) == '_')
@@ -259,7 +262,7 @@ skip_more_whitespace_and_comments:
val->string.str = s->buf;
val->string.len = s->buf_pos;
- return TOK_RHS_KEYSYM;
+ return TOK_IDENT;
}
/* Discard rest of line. */
@@ -350,6 +353,9 @@ struct production {
char string[256];
bool has_keysym;
bool has_string;
+
+ xkb_mod_mask_t mods;
+ xkb_mod_mask_t modmask;
};
static uint32_t
@@ -447,6 +453,28 @@ add_production(struct xkb_compose_table *table, struct scanner *s,
}
}
+static xkb_mod_index_t
+resolve_modifier(const char *name)
+{
+ static const struct {
+ const char *name;
+ xkb_mod_index_t mod;
+ } mods[] = {
+ { "Shift", 0 },
+ { "Ctrl", 2 },
+ { "Alt", 3 },
+ { "Meta", 3 },
+ { "Lock", 1 },
+ { "Caps", 1 },
+ };
+
+ for (unsigned i = 0; i < ARRAY_SIZE(mods); i++)
+ if (streq(name, mods[i].name))
+ return mods[i].mod;
+
+ return XKB_MOD_INVALID;
+}
+
static bool
parse(struct xkb_compose_table *table, struct scanner *s,
unsigned include_depth);
@@ -502,6 +530,7 @@ parse(struct xkb_compose_table *table, struct scanner *s,
union lvalue val;
struct keysym_from_name_cache *cache = s->priv;
xkb_keysym_t keysym;
+ bool tilde;
struct production production;
enum { MAX_ERRORS = 10 };
int num_errors = 0;
@@ -510,6 +539,8 @@ initial:
production.len = 0;
production.has_keysym = false;
production.has_string = false;
+ production.mods = 0;
+ production.modmask = 0;
/* fallthrough */
@@ -547,6 +578,32 @@ lhs:
tok = lex(s, &val);
lhs_tok:
switch (tok) {
+ case TOK_COLON:
+ if (production.len <= 0) {
+ scanner_warn(s, "expected at least one keysym on left-hand side; skipping line");
+ goto skip;
+ }
+ goto rhs;
+ case TOK_IDENT:
+ if (!streq(val.string.str, "None")) {
+ scanner_err(s, "unrecognized identifier \"%s\"", val.string.str);
+ goto error;
+ }
+ production.mods = 0;
+ /* XXX Should only include the mods in resolve_mods(). */
+ production.modmask = 0xff;
+ goto lhs_keysym;
+ case TOK_BANG:
+ tilde = false;
+ goto lhs_mod_list;
+ default:
+ goto lhs_keysym_tok;
+ }
+
+lhs_keysym:
+ tok = lex(s, &val);
+lhs_keysym_tok:
+ switch (tok) {
case TOK_LHS_KEYSYM:
keysym = cached_keysym_from_name(cache, val.string.str, val.string.len);
if (keysym == XKB_KEY_NoSymbol) {
@@ -560,13 +617,32 @@ lhs_tok:
goto skip;
}
production.lhs[production.len++] = keysym;
+ production.mods = 0;
+ production.modmask = 0;
goto lhs;
- case TOK_COLON:
- if (production.len <= 0) {
- scanner_warn(s, "expected at least one keysym on left-hand side; skipping line");
- goto skip;
+ default:
+ goto unexpected;
+ }
+
+lhs_mod_list:
+ switch (tok = lex(s, &val)) {
+ case TOK_TILDE:
+ tilde = true;
+ goto lhs_mod_list;
+ case TOK_IDENT: {
+ xkb_mod_index_t mod = resolve_modifier(val.string.str);
+ if (mod == XKB_MOD_INVALID) {
+ scanner_err(s, "unrecognized modifier \"%s\"",
+ val.string.str);
+ goto error;
}
- goto rhs;
+ production.modmask |= 1 << mod;
+ if (tilde)
+ production.mods &= ~(1 << mod);
+ else
+ production.mods |= 1 << mod;
+ goto lhs;
+ }
default:
goto unexpected;
}
@@ -589,7 +665,7 @@ rhs:
strcpy(production.string, val.string.str);
production.has_string = true;
goto rhs;
- case TOK_RHS_KEYSYM:
+ case TOK_IDENT:
keysym = cached_keysym_from_name(cache, val.string.str, val.string.len);
if (keysym == XKB_KEY_NoSymbol) {
scanner_err(s, "unrecognized keysym \"%s\" on right-hand side",