/************************************************************
- Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
-
- Permission to use, copy, modify, and distribute this
- software and its documentation for any purpose and without
- fee is hereby granted, provided that the above copyright
- notice appear in all copies and that both that copyright
- notice and this permission notice appear in supporting
- documentation, and that the name of Silicon Graphics not be
- used in advertising or publicity pertaining to distribution
- of the software without specific prior written permission.
- Silicon Graphics makes no representation about the suitability
- of this software for any purpose. It is provided "as is"
- without any express or implied warranty.
-
- SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
- GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
- THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
+ * Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of Silicon Graphics not be
+ * used in advertising or publicity pertaining to distribution
+ * of the software without specific prior written permission.
+ * Silicon Graphics makes no representation about the suitability
+ * of this software for any purpose. It is provided "as is"
+ * without any express or implied warranty.
+ *
+ * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
********************************************************/
-#include <stdio.h>
-#include <ctype.h>
+/*
+ * Copyright © 2012 Ran Benita <ran234@gmail.com>
+ *
+ * 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 "xkbcomp-priv.h"
#include "rules.h"
-#include "path.h"
+#include "include.h"
+#include "scanner-utils.h"
-#define DFLT_LINE_SIZE 128
+#define MAX_INCLUDE_DEPTH 5
-struct input_line {
- size_t size;
- size_t offset;
- char buf[DFLT_LINE_SIZE];
- char *line;
+/* Scanner / Lexer */
+
+/* Values returned with some tokens, like yylval. */
+union lvalue {
+ struct sval string;
};
-static void
-input_line_init(struct input_line *line)
-{
- line->size = DFLT_LINE_SIZE;
- line->offset = 0;
- line->line = line->buf;
-}
+enum rules_token {
+ TOK_END_OF_FILE = 0,
+ TOK_END_OF_LINE,
+ TOK_IDENTIFIER,
+ TOK_GROUP_NAME,
+ TOK_BANG,
+ TOK_EQUALS,
+ TOK_STAR,
+ TOK_INCLUDE,
+ TOK_ERROR
+};
-static void
-input_line_deinit(struct input_line *line)
+static inline bool
+is_ident(char ch)
{
- if (line->line != line->buf)
- free(line->line);
- line->offset = 0;
- line->size = DFLT_LINE_SIZE;
- line->line = line->buf;
+ return is_graph(ch) && ch != '\\';
}
-static int
-input_line_add_char(struct input_line *line, int ch)
+static enum rules_token
+lex(struct scanner *s, union lvalue *val)
{
- if (line->offset >= line->size) {
- if (line->line == line->buf) {
- line->line = malloc(line->size * 2);
- memcpy(line->line, line->buf, line->size);
- }
- else {
- line->line = realloc(line->line, line->size * 2);
- }
+skip_more_whitespace_and_comments:
+ /* Skip spaces. */
+ while (scanner_chr(s, ' ') || scanner_chr(s, '\t') || scanner_chr(s, '\r'));
- line->size *= 2;
+ /* Skip comments. */
+ if (scanner_lit(s, "//")) {
+ scanner_skip_to_eol(s);
}
- line->line[line->offset++] = ch;
- return ch;
-}
-
-static bool
-input_line_get(FILE *file, struct input_line *line)
-{
- int ch;
- bool end_of_file = false;
- bool space_pending;
- bool slash_pending;
- bool in_comment;
-
- while (!end_of_file && line->offset == 0) {
- space_pending = slash_pending = in_comment = false;
-
- while ((ch = getc(file)) != '\n' && ch != EOF) {
- if (ch == '\\') {
- ch = getc(file);
-
- if (ch == EOF)
- break;
-
- if (ch == '\n') {
- in_comment = false;
- ch = ' ';
- }
- }
-
- if (in_comment)
- continue;
-
- if (ch == '/') {
- if (slash_pending) {
- in_comment = true;
- slash_pending = false;
- }
- else {
- slash_pending = true;
- }
-
- continue;
- }
-
- if (slash_pending) {
- if (space_pending) {
- input_line_add_char(line, ' ');
- space_pending = false;
- }
-
- input_line_add_char(line, '/');
- slash_pending = false;
- }
-
- if (isspace(ch)) {
- while (isspace(ch) && ch != '\n' && ch != EOF)
- ch = getc(file);
-
- if (ch == EOF)
- break;
-
- if (ch != '\n' && line->offset > 0)
- space_pending = true;
-
- ungetc(ch, file);
- }
- else {
- if (space_pending) {
- input_line_add_char(line, ' ');
- space_pending = false;
- }
-
- if (ch == '!') {
- if (line->offset != 0) {
- WARN("The '!' is legal only at start of line\n");
- ACTION("Line containing '!' ignored\n");
- line->offset = 0;
- break;
- }
- }
+ /* New line. */
+ if (scanner_eol(s)) {
+ while (scanner_eol(s)) scanner_next(s);
+ return TOK_END_OF_LINE;
+ }
- input_line_add_char(line, ch);
- }
+ /* Escaped line continuation. */
+ if (scanner_chr(s, '\\')) {
+ /* Optional \r. */
+ scanner_chr(s, '\r');
+ if (!scanner_eol(s)) {
+ scanner_err(s, "illegal new line escape; must appear at end of line");
+ return TOK_ERROR;
}
+ scanner_next(s);
+ goto skip_more_whitespace_and_comments;
+ }
- if (ch == EOF)
- end_of_file = true;
+ /* See if we're done. */
+ if (scanner_eof(s)) return TOK_END_OF_FILE;
+
+ /* New token. */
+ s->token_line = s->line;
+ s->token_column = s->column;
+
+ /* Operators and punctuation. */
+ if (scanner_chr(s, '!')) return TOK_BANG;
+ if (scanner_chr(s, '=')) return TOK_EQUALS;
+ if (scanner_chr(s, '*')) return TOK_STAR;
+
+ /* Group name. */
+ if (scanner_chr(s, '$')) {
+ val->string.start = s->s + s->pos;
+ val->string.len = 0;
+ while (is_ident(scanner_peek(s))) {
+ scanner_next(s);
+ val->string.len++;
+ }
+ if (val->string.len == 0) {
+ scanner_err(s, "unexpected character after \'$\'; expected name");
+ return TOK_ERROR;
+ }
+ return TOK_GROUP_NAME;
}
- if (line->offset == 0 && end_of_file)
- return false;
+ /* Include statement. */
+ if (scanner_lit(s, "include"))
+ return TOK_INCLUDE;
+
+ /* Identifier. */
+ if (is_ident(scanner_peek(s))) {
+ val->string.start = s->s + s->pos;
+ val->string.len = 0;
+ while (is_ident(scanner_peek(s))) {
+ scanner_next(s);
+ val->string.len++;
+ }
+ return TOK_IDENTIFIER;
+ }
- input_line_add_char(line, '\0');
- return true;
+ scanner_err(s, "unrecognized token");
+ return TOK_ERROR;
}
/***====================================================================***/
-enum {
- /* "Parts" - the MLVO which rules file maps to components. */
- MODEL = 0,
- LAYOUT,
- VARIANT,
- OPTION,
-
-#define PART_MASK \
- ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
-
- /* Components */
- KEYCODES,
- SYMBOLS,
- TYPES,
- COMPAT,
- GEOMETRY,
- KEYMAP,
-
-#define COMPONENT_MASK \
- ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
- (1 << GEOMETRY) | (1 << KEYMAP))
-
- MAX_WORDS
+enum rules_mlvo {
+ MLVO_MODEL,
+ MLVO_LAYOUT,
+ MLVO_VARIANT,
+ MLVO_OPTION,
+ _MLVO_NUM_ENTRIES
};
-static const char *cname[] = {
- [MODEL] = "model",
- [LAYOUT] = "layout",
- [VARIANT] = "variant",
- [OPTION] = "option",
-
- [KEYCODES] = "keycodes",
- [SYMBOLS] = "symbols",
- [TYPES] = "types",
- [COMPAT] = "compat",
- [GEOMETRY] = "geometry",
- [KEYMAP] = "keymap",
-};
+#define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
-struct var_defs {
- const char *model;
- const char *layout;
- const char *variant;
- const char *options;
+static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
+ [MLVO_MODEL] = SVAL_LIT("model"),
+ [MLVO_LAYOUT] = SVAL_LIT("layout"),
+ [MLVO_VARIANT] = SVAL_LIT("variant"),
+ [MLVO_OPTION] = SVAL_LIT("option"),
};
-struct mapping {
- /* Sequential id for the mappings. */
- int number;
- size_t num_maps;
-
- struct {
- int word;
- int index;
- } map[MAX_WORDS];
+enum rules_kccgst {
+ KCCGST_KEYCODES,
+ KCCGST_TYPES,
+ KCCGST_COMPAT,
+ KCCGST_SYMBOLS,
+ KCCGST_GEOMETRY,
+ _KCCGST_NUM_ENTRIES
};
-struct multi_defs {
- const char *model;
- const char *layout[XkbNumKbdGroups + 1];
- const char *variant[XkbNumKbdGroups + 1];
- char *options;
+static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
+ [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
+ [KCCGST_TYPES] = SVAL_LIT("types"),
+ [KCCGST_COMPAT] = SVAL_LIT("compat"),
+ [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
+ [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
};
-struct var_desc {
- char *name;
- char *desc;
+/* We use this to keep score whether an mlvo was matched or not; if not,
+ * we warn the user that his preference was ignored. */
+struct matched_sval {
+ struct sval sval;
+ bool matched;
};
+typedef darray(struct matched_sval) darray_matched_sval;
-struct describe_vars {
- size_t sz_desc;
- size_t num_desc;
- struct var_desc *desc;
+/*
+ * A broken-down version of xkb_rule_names (without the rules,
+ * obviously).
+ */
+struct rule_names {
+ struct matched_sval model;
+ darray_matched_sval layouts;
+ darray_matched_sval variants;
+ darray_matched_sval options;
};
struct group {
- int number;
- char *name;
- char *words;
+ struct sval name;
+ darray_sval elements;
};
-enum rule_flag {
- RULE_FLAG_PENDING_MATCH = (1L << 1),
- RULE_FLAG_OPTION = (1L << 2),
- RULE_FLAG_APPEND = (1L << 3),
- RULE_FLAG_NORMAL = (1L << 4),
+struct mapping {
+ int mlvo_at_pos[_MLVO_NUM_ENTRIES];
+ unsigned int num_mlvo;
+ unsigned int defined_mlvo_mask;
+ xkb_layout_index_t layout_idx, variant_idx;
+ int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
+ unsigned int num_kccgst;
+ unsigned int defined_kccgst_mask;
+ bool skip;
};
-struct rule {
- int number;
-
- char *model;
- char *layout;
- int layout_num;
- char *variant;
- int variant_num;
- char *option;
-
- /* yields */
-
- char *keycodes;
- char *symbols;
- char *types;
- char *compat;
- char *keymap;
- unsigned flags;
+enum mlvo_match_type {
+ MLVO_MATCH_NORMAL = 0,
+ MLVO_MATCH_WILDCARD,
+ MLVO_MATCH_GROUP,
};
-struct rules {
- struct describe_vars models;
- struct describe_vars layouts;
- struct describe_vars variants;
- struct describe_vars options;
-
- size_t sz_rules;
- size_t num_rules;
- struct rule *rules;
-
- size_t sz_groups;
- size_t num_groups;
- struct group *groups;
+struct rule {
+ struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
+ enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
+ unsigned int num_mlvo_values;
+ struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
+ unsigned int num_kccgst_values;
+ bool skip;
};
-/***====================================================================***/
-
/*
- * Resolve numeric index, such as "[4]" in layout[4]. Missing index
- * means zero.
+ * This is the main object used to match a given RMLVO against a rules
+ * file and aggragate the results in a KcCGST. It goes through a simple
+ * matching state machine, with tokens as transitions (see
+ * matcher_match()).
*/
-static char *
-get_index(char *str, int *ndx)
-{
- int empty = 0, consumed = 0, num;
-
- sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
- if (consumed > 0) {
- *ndx = num;
- str += consumed;
- } else if (empty > 0) {
- *ndx = -1;
- } else {
- *ndx = 0;
- }
+struct matcher {
+ struct xkb_context *ctx;
+ /* Input.*/
+ struct rule_names rmlvo;
+ union lvalue val;
+ darray(struct group) groups;
+ /* Current mapping. */
+ struct mapping mapping;
+ /* Current rule. */
+ struct rule rule;
+ /* Output. */
+ darray_char kccgst[_KCCGST_NUM_ENTRIES];
+};
- return str;
+static struct sval
+strip_spaces(struct sval v)
+{
+ while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; }
+ while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--;
+ return v;
}
-/*
- * Match a mapping line which opens a rule, e.g:
- * ! model layout[4] variant[4] = symbols geometry
- * Which will be followed by lines such as:
- * * ben basic = +in(ben):4 nec(pc98)
- * So if the MLVO matches the LHS of some line, we'll get the components
- * on the RHS.
- * In this example, we will get for the second and fourth columns:
- * mapping->map[1] = {.word = LAYOUT, .index = 4}
- * mapping->map[3] = {.word = SYMBOLS, .index = 0}
- */
-static void
-match_mapping_line(struct input_line *line, struct mapping *mapping)
+static darray_matched_sval
+split_comma_separated_mlvo(const char *s)
{
- char *tok;
- char *str = &line->line[1];
- unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
- int i, tmp;
- size_t len;
- int ndx;
- char *strtok_buf;
- bool found;
+ darray_matched_sval arr = darray_new();
/*
- * Remember the last sequential mapping id (incremented if the match
- * is successful).
+ * Make sure the array returned by this function always includes at
+ * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
*/
- tmp = mapping->number;
- memset(mapping, 0, sizeof(*mapping));
- mapping->number = tmp;
- while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
- found = false;
- str = NULL;
+ if (!s) {
+ struct matched_sval val = { .sval = { NULL, 0 } };
+ darray_append(arr, val);
+ return arr;
+ }
- if (strcmp(tok, "=") == 0)
- continue;
+ while (true) {
+ struct matched_sval val = { .sval = { s, 0 } };
+ while (*s != '\0' && *s != ',') { s++; val.sval.len++; }
+ val.sval = strip_spaces(val.sval);
+ darray_append(arr, val);
+ if (*s == '\0') break;
+ if (*s == ',') s++;
+ }
- for (i = 0; i < MAX_WORDS; i++) {
- len = strlen(cname[i]);
-
- if (strncmp(cname[i], tok, len) == 0) {
- if (strlen(tok) > len) {
- char *end = get_index(tok + len, &ndx);
-
- if ((i != LAYOUT && i != VARIANT) ||
- *end != '\0' || ndx == -1) {
- WARN("Illegal %s index: %d\n", cname[i], ndx);
- WARN("Can only index layout and variant\n");
- break;
- }
-
- if (ndx < 1 || ndx > XkbNumKbdGroups) {
- WARN("Illegal %s index: %d\n", cname[i], ndx);
- WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
- break;
- }
- } else {
- ndx = 0;
- }
+ return arr;
+}
- found = true;
+static struct matcher *
+matcher_new(struct xkb_context *ctx,
+ const struct xkb_rule_names *rmlvo)
+{
+ struct matcher *m = calloc(1, sizeof(*m));
+ if (!m)
+ return NULL;
- if (present & (1 << i)) {
- if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
- (i == VARIANT && variant_ndx_present & (1 << ndx))) {
- WARN("Component \"%s\" listed twice\n", tok);
- ACTION("Second definition ignored\n");
- break;
- }
- }
+ m->ctx = ctx;
+ m->rmlvo.model.sval.start = rmlvo->model;
+ m->rmlvo.model.sval.len = strlen_safe(rmlvo->model);
+ m->rmlvo.layouts = split_comma_separated_mlvo(rmlvo->layout);
+ m->rmlvo.variants = split_comma_separated_mlvo(rmlvo->variant);
+ m->rmlvo.options = split_comma_separated_mlvo(rmlvo->options);
- present |= (1 << i);
- if (i == LAYOUT)
- layout_ndx_present |= 1 << ndx;
- if (i == VARIANT)
- variant_ndx_present |= 1 << ndx;
+ return m;
+}
- mapping->map[mapping->num_maps].word = i;
- mapping->map[mapping->num_maps].index = ndx;
- mapping->num_maps++;
- break;
- }
- }
+static void
+matcher_free(struct matcher *m)
+{
+ struct group *group;
+ if (!m)
+ return;
+ darray_free(m->rmlvo.layouts);
+ darray_free(m->rmlvo.variants);
+ darray_free(m->rmlvo.options);
+ darray_foreach(group, m->groups)
+ darray_free(group->elements);
+ for (int i = 0; i < _KCCGST_NUM_ENTRIES; i++)
+ darray_free(m->kccgst[i]);
+ darray_free(m->groups);
+ free(m);
+}
- if (!found) {
- WARN("Unknown component \"%s\"\n", tok);
- ACTION("ignored\n");
- }
- }
+static void
+matcher_group_start_new(struct matcher *m, struct sval name)
+{
+ struct group group = { .name = name, .elements = darray_new() };
+ darray_append(m->groups, group);
+}
+
+static void
+matcher_group_add_element(struct matcher *m, struct scanner *s,
+ struct sval element)
+{
+ darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
+ element);
+}
- if ((present & PART_MASK) == 0) {
- WARN("Mapping needs at least one MLVO part\n");
- ACTION("Illegal mapping ignored\n");
- mapping->num_maps = 0;
+static bool
+read_rules_file(struct xkb_context *ctx,
+ struct matcher *matcher,
+ unsigned include_depth,
+ FILE *file,
+ const char *path);
+
+static void
+matcher_include(struct matcher *m, struct scanner *parent_scanner,
+ unsigned include_depth,
+ struct sval inc)
+{
+ struct scanner s; /* parses the !include value */
+ FILE *file;
+
+ scanner_init(&s, m->ctx, inc.start, inc.len,
+ parent_scanner->file_name, NULL);
+ s.token_line = parent_scanner->token_line;
+ s.token_column = parent_scanner->token_column;
+ s.buf_pos = 0;
+
+ if (include_depth >= MAX_INCLUDE_DEPTH) {
+ scanner_err(&s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
+ MAX_INCLUDE_DEPTH);
return;
}
- if ((present & COMPONENT_MASK) == 0) {
- WARN("Mapping needs at least one component\n");
- ACTION("Illegal mapping ignored\n");
- mapping->num_maps = 0;
+ while (!scanner_eof(&s) && !scanner_eol(&s)) {
+ if (scanner_chr(&s, '%')) {
+ if (scanner_chr(&s, '%')) {
+ scanner_buf_append(&s, '%');
+ }
+ else if (scanner_chr(&s, 'H')) {
+ const char *home = xkb_context_getenv(m->ctx, "HOME");
+ if (!home) {
+ scanner_err(&s, "%%H was used in an include statement, but the HOME environment variable is not set");
+ return;
+ }
+ if (!scanner_buf_appends(&s, home)) {
+ scanner_err(&s, "include path after expanding %%H is too long");
+ return;
+ }
+ }
+ else if (scanner_chr(&s, 'S')) {
+ const char *default_root = xkb_context_include_path_get_system_path(m->ctx);
+ if (!scanner_buf_appends(&s, default_root) || !scanner_buf_appends(&s, "/rules")) {
+ scanner_err(&s, "include path after expanding %%S is too long");
+ return;
+ }
+ }
+ else if (scanner_chr(&s, 'E')) {
+ const char *default_root = xkb_context_include_path_get_extra_path(m->ctx);
+ if (!scanner_buf_appends(&s, default_root) || !scanner_buf_appends(&s, "/rules")) {
+ scanner_err(&s, "include path after expanding %%E is too long");
+ return;
+ }
+ }
+ else {
+ scanner_err(&s, "unknown %% format (%c) in include statement", scanner_peek(&s));
+ return;
+ }
+ }
+ else {
+ scanner_buf_append(&s, scanner_next(&s));
+ }
+ }
+ if (!scanner_buf_append(&s, '\0')) {
+ scanner_err(&s, "include path is too long");
return;
}
- if (((present & COMPONENT_MASK) & (1 << KEYMAP)) &&
- ((present & COMPONENT_MASK) != (1 << KEYMAP))) {
- WARN("Keymap cannot appear with other components\n");
- ACTION("Illegal mapping ignored\n");
- mapping->num_maps = 0;
- return;
+ file = fopen(s.buf, "rb");
+ if (file) {
+ bool ret = read_rules_file(m->ctx, m, include_depth + 1, file, s.buf);
+ if (!ret)
+ log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
+ "No components returned from included XKB rules \"%s\"\n",
+ s.buf);
+ fclose(file);
+ } else {
+ log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
+ "Failed to open included XKB rules \"%s\"\n",
+ s.buf);
}
+}
- mapping->number++;
+static void
+matcher_mapping_start_new(struct matcher *m)
+{
+ for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++)
+ m->mapping.mlvo_at_pos[i] = -1;
+ for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++)
+ m->mapping.kccgst_at_pos[i] = -1;
+ m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID;
+ m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
+ m->mapping.defined_mlvo_mask = 0;
+ m->mapping.defined_kccgst_mask = 0;
+ m->mapping.skip = false;
}
-/*
- * Match a line such as:
- * ! $pcmodels = pc101 pc102 pc104 pc105
- */
-static bool
-match_group_line(struct input_line *line, struct group *group)
+static int
+extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out)
{
- int i;
- char *name = strchr(line->line, '$');
- char *words = strchr(name, ' ');
+ /* This function is pretty stupid, but works for now. */
+ *out = XKB_LAYOUT_INVALID;
+ if (max_len < 3)
+ return -1;
+ if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']')
+ return -1;
+ if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS)
+ return -1;
+ /* To zero-based index. */
+ *out = s[1] - '0' - 1;
+ return 3;
+}
- if (!words)
- return false;
+static void
+matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s,
+ struct sval ident)
+{
+ enum rules_mlvo mlvo;
+ struct sval mlvo_sval;
- *words++ = '\0';
+ for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
+ mlvo_sval = rules_mlvo_svals[mlvo];
- for (; *words; words++) {
- if (*words != '=' && *words != ' ')
+ if (svaleq_prefix(mlvo_sval, ident))
break;
}
- if (*words == '\0')
- return false;
+ /* Not found. */
+ if (mlvo >= _MLVO_NUM_ENTRIES) {
+ scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
+ ident.len, ident.start);
+ m->mapping.skip = true;
+ return;
+ }
- group->name = strdup(name);
- group->words = strdup(words);
+ if (m->mapping.defined_mlvo_mask & (1u << mlvo)) {
+ scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
+ mlvo_sval.len, mlvo_sval.start);
+ m->mapping.skip = true;
+ return;
+ }
- words = group->words;
- for (i = 1; *words; words++) {
- if (*words == ' ') {
- *words++ = '\0';
- i++;
+ /* If there are leftovers still, it must be an index. */
+ if (mlvo_sval.len < ident.len) {
+ xkb_layout_index_t idx;
+ int consumed = extract_layout_index(ident.start + mlvo_sval.len,
+ ident.len - mlvo_sval.len, &idx);
+ if ((int) (ident.len - mlvo_sval.len) != consumed) {
+ scanner_err(s, "invalid mapping: \"%.*s\" may only be followed by a valid group index; ignoring rule set",
+ mlvo_sval.len, mlvo_sval.start);
+ m->mapping.skip = true;
+ return;
}
- }
- group->number = i;
- return true;
+ if (mlvo == MLVO_LAYOUT) {
+ m->mapping.layout_idx = idx;
+ }
+ else if (mlvo == MLVO_VARIANT) {
+ m->mapping.variant_idx = idx;
+ }
+ else {
+ scanner_err(s, "invalid mapping: \"%.*s\" cannot be followed by a group index; ignoring rule set",
+ mlvo_sval.len, mlvo_sval.start);
+ m->mapping.skip = true;
+ return;
+ }
+ }
+ m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
+ m->mapping.defined_mlvo_mask |= 1u << mlvo;
+ m->mapping.num_mlvo++;
}
-/* Match lines following a mapping (see match_mapping_line comment). */
-static bool
-match_rule_line(struct input_line *line, struct mapping *mapping,
- struct rule *rule)
+static void
+matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident)
{
- char *str, *tok;
- int nread, i;
- char *strtok_buf;
- bool append = false;
- const char *names[MAX_WORDS] = { NULL };
-
- if (mapping->num_maps == 0) {
- WARN("Must have a mapping before first line of data\n");
- ACTION("Illegal line of data ignored\n");
- return false;
+ enum rules_kccgst kccgst;
+ struct sval kccgst_sval;
+
+ for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
+ kccgst_sval = rules_kccgst_svals[kccgst];
+
+ if (svaleq(rules_kccgst_svals[kccgst], ident))
+ break;
}
- str = line->line;
+ /* Not found. */
+ if (kccgst >= _KCCGST_NUM_ENTRIES) {
+ scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
+ ident.len, ident.start);
+ m->mapping.skip = true;
+ return;
+ }
- for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
- str = NULL;
+ if (m->mapping.defined_kccgst_mask & (1u << kccgst)) {
+ scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
+ kccgst_sval.len, kccgst_sval.start);
+ m->mapping.skip = true;
+ return;
+ }
- if (strcmp(tok, "=") == 0) {
- nread--;
- continue;
- }
+ m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
+ m->mapping.defined_kccgst_mask |= 1u << kccgst;
+ m->mapping.num_kccgst++;
+}
- if (nread > mapping->num_maps) {
- WARN("Too many words on a line\n");
- ACTION("Extra word \"%s\" ignored\n", tok);
- continue;
- }
+static void
+matcher_mapping_verify(struct matcher *m, struct scanner *s)
+{
+ if (m->mapping.num_mlvo == 0) {
+ scanner_err(s, "invalid mapping: must have at least one value on the left hand side; ignoring rule set");
+ goto skip;
+ }
- names[mapping->map[nread].word] = tok;
- if (*tok == '+' || *tok == '|')
- append = true;
+ if (m->mapping.num_kccgst == 0) {
+ scanner_err(s, "invalid mapping: must have at least one value on the right hand side; ignoring rule set");
+ goto skip;
}
- if (nread < mapping->num_maps) {
- WARN("Too few words on a line: %s\n", line->line);
- ACTION("line ignored\n");
- return false;
+ /*
+ * This following is very stupid, but this is how it works.
+ * See the "Notes" section in the overview above.
+ */
+
+ if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) {
+ if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) {
+ if (darray_size(m->rmlvo.layouts) > 1)
+ goto skip;
+ }
+ else {
+ if (darray_size(m->rmlvo.layouts) == 1 ||
+ m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
+ goto skip;
+ }
}
- rule->flags = 0;
- rule->number = mapping->number;
-
- if (names[OPTION])
- rule->flags |= RULE_FLAG_OPTION;
- else if (append)
- rule->flags |= RULE_FLAG_APPEND;
- else
- rule->flags |= RULE_FLAG_NORMAL;
-
- rule->model = uDupString(names[MODEL]);
- rule->layout = uDupString(names[LAYOUT]);
- rule->variant = uDupString(names[VARIANT]);
- rule->option = uDupString(names[OPTION]);
-
- rule->keycodes = uDupString(names[KEYCODES]);
- rule->symbols = uDupString(names[SYMBOLS]);
- rule->types = uDupString(names[TYPES]);
- rule->compat = uDupString(names[COMPAT]);
- rule->keymap = uDupString(names[KEYMAP]);
-
- rule->layout_num = rule->variant_num = 0;
- for (i = 0; i < nread; i++) {
- if (mapping->map[i].index) {
- if (mapping->map[i].word == LAYOUT)
- rule->layout_num = mapping->map[i].index;
- if (mapping->map[i].word == VARIANT)
- rule->variant_num = mapping->map[i].index;
+ if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) {
+ if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) {
+ if (darray_size(m->rmlvo.variants) > 1)
+ goto skip;
+ }
+ else {
+ if (darray_size(m->rmlvo.variants) == 1 ||
+ m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
+ goto skip;
}
}
- return true;
+ return;
+
+skip:
+ m->mapping.skip = true;
}
-static bool
-match_line(struct input_line *line, struct mapping *mapping,
- struct rule *rule, struct group *group)
+static void
+matcher_rule_start_new(struct matcher *m)
{
- if (line->line[0] != '!')
- return match_rule_line(line, mapping, rule);
-
- if (line->line[1] == '$' || (line->line[1] == ' ' && line->line[2] == '$'))
- return match_group_line(line, group);
-
- match_mapping_line(line, mapping);
- return false;
+ memset(&m->rule, 0, sizeof(m->rule));
+ m->rule.skip = m->mapping.skip;
}
static void
-squeeze_spaces(char *p1)
+matcher_rule_set_mlvo_common(struct matcher *m, struct scanner *s,
+ struct sval ident,
+ enum mlvo_match_type match_type)
{
- char *p2;
- for (p2 = p1; *p2; p2++) {
- *p1 = *p2;
- if (*p1 != ' ') p1++;
- }
- *p1 = '\0';
+ if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
+ scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
+ m->rule.skip = true;
+ return;
+ }
+ m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
+ m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
+ m->rule.num_mlvo_values++;
}
-static bool
-MakeMultiDefs(struct multi_defs *mdefs, const struct var_defs *defs)
+static void
+matcher_rule_set_mlvo_wildcard(struct matcher *m, struct scanner *s)
{
- memset(mdefs, 0, sizeof(*mdefs));
- mdefs->model = defs->model;
- mdefs->options = uDupString(defs->options);
- if (mdefs->options) squeeze_spaces(mdefs->options);
-
- if (defs->layout) {
- if (!strchr(defs->layout, ',')) {
- mdefs->layout[0] = defs->layout;
- } else {
- char *p;
- int i;
- p = uDupString(defs->layout);
- if (p == NULL)
- return false;
- squeeze_spaces(p);
- mdefs->layout[1] = p;
- for (i = 2; i <= XkbNumKbdGroups; i++) {
- if ((p = strchr(p, ','))) {
- *p++ = '\0';
- mdefs->layout[i] = p;
- } else {
- break;
- }
- }
- if (p && (p = strchr(p, ',')))
- *p = '\0';
- }
- }
-
- if (defs->variant) {
- if (!strchr(defs->variant, ',')) {
- mdefs->variant[0] = defs->variant;
- } else {
- char *p;
- int i;
- p = uDupString(defs->variant);
- if (p == NULL)
- return false;
- squeeze_spaces(p);
- mdefs->variant[1] = p;
- for (i = 2; i <= XkbNumKbdGroups; i++) {
- if ((p = strchr(p, ','))) {
- *p++ = '\0';
- mdefs->variant[i] = p;
- } else {
- break;
- }
- }
- if (p && (p = strchr(p, ',')))
- *p = '\0';
- }
- }
- return true;
+ struct sval dummy = { NULL, 0 };
+ matcher_rule_set_mlvo_common(m, s, dummy, MLVO_MATCH_WILDCARD);
}
static void
-FreeMultiDefs(struct multi_defs *defs)
+matcher_rule_set_mlvo_group(struct matcher *m, struct scanner *s,
+ struct sval ident)
{
- free(defs->options);
- free(UNCONSTIFY(defs->layout[1]));
- free(UNCONSTIFY(defs->variant[1]));
+ matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_GROUP);
}
static void
-Apply(char *src, char **dst)
+matcher_rule_set_mlvo(struct matcher *m, struct scanner *s,
+ struct sval ident)
{
- int ret;
- char *tmp;
+ matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_NORMAL);
+}
- if (!src)
+static void
+matcher_rule_set_kccgst(struct matcher *m, struct scanner *s,
+ struct sval ident)
+{
+ if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
+ scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
+ m->rule.skip = true;
return;
+ }
+ m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
+ m->rule.num_kccgst_values++;
+}
- if (*src == '+' || *src == '!') {
- tmp = *dst;
- ret = asprintf(dst, "%s%s", *dst, src);
- if (ret < 0)
- *dst = NULL;
- free(tmp);
+static bool
+match_group(struct matcher *m, struct sval group_name, struct sval to)
+{
+ struct group *group;
+ struct sval *element;
+ bool found = false;
+
+ darray_foreach(group, m->groups) {
+ if (svaleq(group->name, group_name)) {
+ found = true;
+ break;
+ }
}
- else if (*dst == NULL) {
- *dst = uDupString(src);
+
+ if (!found) {
+ /*
+ * rules/evdev intentionally uses some undeclared group names
+ * in rules (e.g. commented group definitions which may be
+ * uncommented if needed). So we continue silently.
+ */
+ return false;
}
+
+ darray_foreach(element, group->elements)
+ if (svaleq(to, *element))
+ return true;
+
+ return false;
}
-static void
-XkbRF_ApplyRule(struct rule *rule, struct xkb_component_names *names)
+static bool
+match_value(struct matcher *m, struct sval val, struct sval to,
+ enum mlvo_match_type match_type)
{
- /* clear the flag because it's applied */
- rule->flags &= ~RULE_FLAG_PENDING_MATCH;
-
- Apply(rule->keycodes, &names->keycodes);
- Apply(rule->symbols, &names->symbols);
- Apply(rule->types, &names->types);
- Apply(rule->compat, &names->compat);
- Apply(rule->keymap, &names->keymap);
+ if (match_type == MLVO_MATCH_WILDCARD)
+ return true;
+ if (match_type == MLVO_MATCH_GROUP)
+ return match_group(m, val, to);
+ return svaleq(val, to);
}
static bool
-CheckGroup(struct rules *rules, const char *group_name, const char *name)
+match_value_and_mark(struct matcher *m, struct sval val,
+ struct matched_sval *to, enum mlvo_match_type match_type)
{
- int i;
- const char *p;
- struct group *group;
-
- for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
- if (! strcmp(group->name, group_name)) {
- break;
- }
- }
- if (i == rules->num_groups)
- return false;
- for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
- if (! strcmp(p, name)) {
- return true;
- }
- }
- return false;
+ bool matched = match_value(m, val, to->sval, match_type);
+ if (matched)
+ to->matched = true;
+ return matched;
}
+/*
+ * This function performs %-expansion on @value (see overview above),
+ * and appends the result to @to.
+ */
static bool
-MatchOneOf(char *wanted, char *vals_defined)
+append_expanded_kccgst_value(struct matcher *m, struct scanner *s,
+ darray_char *to, struct sval value)
{
- char *str, *next;
- int want_len = strlen(wanted);
-
- for (str = vals_defined, next = NULL; str != NULL; str = next) {
- int len;
- next = strchr(str, ',');
- if (next) {
- len = next-str;
- next++;
+ const char *str = value.start;
+ darray_char expanded = darray_new();
+ char ch;
+ bool expanded_plus, to_plus;
+
+ /*
+ * Some ugly hand-lexing here, but going through the scanner is more
+ * trouble than it's worth, and the format is ugly on its own merit.
+ */
+ for (unsigned i = 0; i < value.len; ) {
+ enum rules_mlvo mlv;
+ xkb_layout_index_t idx;
+ char pfx, sfx;
+ struct matched_sval *expanded_value;
+
+ /* Check if that's a start of an expansion. */
+ if (str[i] != '%') {
+ /* Just a normal character. */
+ darray_appends_nullterminate(expanded, &str[i++], 1);
+ continue;
}
- else {
- len = strlen(str);
+ if (++i >= value.len) goto error;
+
+ pfx = sfx = 0;
+
+ /* Check for prefix. */
+ if (str[i] == '(' || str[i] == '+' || str[i] == '|' ||
+ str[i] == '_' || str[i] == '-') {
+ pfx = str[i];
+ if (str[i] == '(') sfx = ')';
+ if (++i >= value.len) goto error;
}
- if (len == want_len && strncmp(wanted, str, len) == 0)
- return true;
- }
+ /* Mandatory model/layout/variant specifier. */
+ switch (str[i++]) {
+ case 'm': mlv = MLVO_MODEL; break;
+ case 'l': mlv = MLVO_LAYOUT; break;
+ case 'v': mlv = MLVO_VARIANT; break;
+ default: goto error;
+ }
- return false;
-}
+ /* Check for index. */
+ idx = XKB_LAYOUT_INVALID;
+ if (i < value.len && str[i] == '[') {
+ int consumed;
-static int
-XkbRF_CheckApplyRule(struct rule *rule, struct multi_defs *mdefs,
- struct xkb_component_names *names, struct rules *rules)
-{
- bool pending = false;
-
- if (rule->model != NULL) {
- if(mdefs->model == NULL)
- return 0;
- if (strcmp(rule->model, "*") == 0) {
- pending = true;
- } else {
- if (rule->model[0] == '$') {
- if (!CheckGroup(rules, rule->model, mdefs->model))
- return 0;
- } else {
- if (strcmp(rule->model, mdefs->model) != 0)
- return 0;
- }
- }
- }
- if (rule->option != NULL) {
- if (mdefs->options == NULL)
- return 0;
- if ((!MatchOneOf(rule->option,mdefs->options)))
- return 0;
- }
+ if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
+ scanner_err(s, "invalid index in %%-expansion; may only index layout or variant");
+ goto error;
+ }
- if (rule->layout != NULL) {
- if(mdefs->layout[rule->layout_num] == NULL ||
- *mdefs->layout[rule->layout_num] == '\0')
- return 0;
- if (strcmp(rule->layout, "*") == 0) {
- pending = true;
- } else {
- if (rule->layout[0] == '$') {
- if (!CheckGroup(rules, rule->layout,
- mdefs->layout[rule->layout_num]))
- return 0;
- } else {
- if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
- return 0;
- }
- }
- }
- if (rule->variant != NULL) {
- if (mdefs->variant[rule->variant_num] == NULL ||
- *mdefs->variant[rule->variant_num] == '\0')
- return 0;
- if (strcmp(rule->variant, "*") == 0) {
- pending = true;
- } else {
- if (rule->variant[0] == '$') {
- if (!CheckGroup(rules, rule->variant,
- mdefs->variant[rule->variant_num]))
- return 0;
- } else {
- if (strcmp(rule->variant,
- mdefs->variant[rule->variant_num]) != 0)
- return 0;
- }
- }
- }
- if (pending) {
- rule->flags |= RULE_FLAG_PENDING_MATCH;
- return rule->number;
- }
- /* exact match, apply it now */
- XkbRF_ApplyRule(rule,names);
- return rule->number;
-}
+ consumed = extract_layout_index(str + i, value.len - i, &idx);
+ if (consumed == -1) goto error;
+ i += consumed;
+ }
-static void
-XkbRF_ClearPartialMatches(struct rules *rules)
-{
- int i;
- struct rule *rule;
+ /* Check for suffix, if there supposed to be one. */
+ if (sfx != 0) {
+ if (i >= value.len) goto error;
+ if (str[i++] != sfx) goto error;
+ }
- for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
- rule->flags &= ~RULE_FLAG_PENDING_MATCH;
- }
-}
+ /* Get the expanded value. */
+ expanded_value = NULL;
+
+ if (mlv == MLVO_LAYOUT) {
+ if (idx != XKB_LAYOUT_INVALID &&
+ idx < darray_size(m->rmlvo.layouts) &&
+ darray_size(m->rmlvo.layouts) > 1)
+ expanded_value = &darray_item(m->rmlvo.layouts, idx);
+ else if (idx == XKB_LAYOUT_INVALID &&
+ darray_size(m->rmlvo.layouts) == 1)
+ expanded_value = &darray_item(m->rmlvo.layouts, 0);
+ }
+ else if (mlv == MLVO_VARIANT) {
+ if (idx != XKB_LAYOUT_INVALID &&
+ idx < darray_size(m->rmlvo.variants) &&
+ darray_size(m->rmlvo.variants) > 1)
+ expanded_value = &darray_item(m->rmlvo.variants, idx);
+ else if (idx == XKB_LAYOUT_INVALID &&
+ darray_size(m->rmlvo.variants) == 1)
+ expanded_value = &darray_item(m->rmlvo.variants, 0);
+ }
+ else if (mlv == MLVO_MODEL) {
+ expanded_value = &m->rmlvo.model;
+ }
-static void
-XkbRF_ApplyPartialMatches(struct rules *rules,
- struct xkb_component_names *names)
-{
- int i;
- struct rule *rule;
+ /* If we didn't get one, skip silently. */
+ if (!expanded_value || expanded_value->sval.len == 0)
+ continue;
- for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
- if ((rule->flags & RULE_FLAG_PENDING_MATCH)==0)
- continue;
- XkbRF_ApplyRule(rule,names);
+ if (pfx != 0)
+ darray_appends_nullterminate(expanded, &pfx, 1);
+ darray_appends_nullterminate(expanded,
+ expanded_value->sval.start,
+ expanded_value->sval.len);
+ if (sfx != 0)
+ darray_appends_nullterminate(expanded, &sfx, 1);
+ expanded_value->matched = true;
}
+
+ /*
+ * Appending bar to foo -> foo (not an error if this happens)
+ * Appending +bar to foo -> foo+bar
+ * Appending bar to +foo -> bar+foo
+ * Appending +bar to +foo -> +foo+bar
+ */
+
+ ch = (darray_empty(expanded) ? '\0' : darray_item(expanded, 0));
+ expanded_plus = (ch == '+' || ch == '|');
+ ch = (darray_empty(*to) ? '\0' : darray_item(*to, 0));
+ to_plus = (ch == '+' || ch == '|');
+
+ if (expanded_plus || darray_empty(*to))
+ darray_appends_nullterminate(*to, expanded.item, expanded.size);
+ else if (to_plus)
+ darray_prepends_nullterminate(*to, expanded.item, expanded.size);
+
+ darray_free(expanded);
+ return true;
+
+error:
+ darray_free(expanded);
+ scanner_err(s, "invalid %%-expansion in value; not used");
+ return false;
}
static void
-XkbRF_CheckApplyRules(struct rules *rules, struct multi_defs *mdefs,
- struct xkb_component_names *names, unsigned int flags)
+matcher_rule_verify(struct matcher *m, struct scanner *s)
{
- int i;
- struct rule *rule;
- int skip;
-
- for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
- if ((rule->flags & flags) != flags)
- continue;
- skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
- if (skip && !(flags & RULE_FLAG_OPTION)) {
- for ( ;(i < rules->num_rules) && (rule->number == skip);
- rule++, i++);
- rule--; i--;
- }
+ if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
+ m->rule.num_kccgst_values != m->mapping.num_kccgst) {
+ scanner_err(s, "invalid rule: must have same number of values as mapping line; ignoring rule");
+ m->rule.skip = true;
}
}
-/***====================================================================***/
-
-static char *
-XkbRF_SubstituteVars(char *name, struct multi_defs *mdefs)
+static void
+matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s)
{
- char *str, *outstr, *orig, *var;
- size_t len;
- int ndx;
-
- if (!name)
- return NULL;
-
- orig= name;
- str= strchr(name,'%');
- if (str==NULL)
- return name;
- len= strlen(name);
- while (str!=NULL) {
- char pfx= str[1];
- int extra_len= 0;
- if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
- extra_len= 1;
- str++;
- }
- else if (pfx=='(') {
- extra_len= 2;
- str++;
- }
- var = str + 1;
- str = get_index(var + 1, &ndx);
- if (ndx == -1) {
- str = strchr(str,'%');
- continue;
+ for (unsigned i = 0; i < m->mapping.num_mlvo; i++) {
+ enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i];
+ struct sval value = m->rule.mlvo_value_at_pos[i];
+ enum mlvo_match_type match_type = m->rule.match_type_at_pos[i];
+ struct matched_sval *to;
+ bool matched = false;
+
+ if (mlvo == MLVO_MODEL) {
+ to = &m->rmlvo.model;
+ matched = match_value_and_mark(m, value, to, match_type);
}
- if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
- len+= strlen(mdefs->layout[ndx])+extra_len;
- else if ((*var=='m')&&mdefs->model)
- len+= strlen(mdefs->model)+extra_len;
- else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
- len+= strlen(mdefs->variant[ndx])+extra_len;
- if ((pfx=='(')&&(*str==')')) {
- str++;
- }
- str= strchr(&str[0],'%');
- }
- name = malloc(len + 1);
- str= orig;
- outstr= name;
- while (*str!='\0') {
- if (str[0]=='%') {
- char pfx,sfx;
- str++;
- pfx= str[0];
- sfx= '\0';
- if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
- str++;
- }
- else if (pfx=='(') {
- sfx= ')';
- str++;
- }
- else pfx= '\0';
-
- var = str;
- str = get_index(var + 1, &ndx);
- if (ndx == -1) {
- continue;
+ else if (mlvo == MLVO_LAYOUT) {
+ xkb_layout_index_t idx = m->mapping.layout_idx;
+ idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
+ to = &darray_item(m->rmlvo.layouts, idx);
+ matched = match_value_and_mark(m, value, to, match_type);
+ }
+ else if (mlvo == MLVO_VARIANT) {
+ xkb_layout_index_t idx = m->mapping.layout_idx;
+ idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
+ to = &darray_item(m->rmlvo.variants, idx);
+ matched = match_value_and_mark(m, value, to, match_type);
+ }
+ else if (mlvo == MLVO_OPTION) {
+ darray_foreach(to, m->rmlvo.options) {
+ matched = match_value_and_mark(m, value, to, match_type);
+ if (matched)
+ break;
}
- if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
- if (pfx) *outstr++= pfx;
- strcpy(outstr,mdefs->layout[ndx]);
- outstr+= strlen(mdefs->layout[ndx]);
- if (sfx) *outstr++= sfx;
- }
- else if ((*var=='m')&&(mdefs->model)) {
- if (pfx) *outstr++= pfx;
- strcpy(outstr,mdefs->model);
- outstr+= strlen(mdefs->model);
- if (sfx) *outstr++= sfx;
- }
- else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
- if (pfx) *outstr++= pfx;
- strcpy(outstr,mdefs->variant[ndx]);
- outstr+= strlen(mdefs->variant[ndx]);
- if (sfx) *outstr++= sfx;
- }
- if ((pfx=='(')&&(*str==')'))
- str++;
- }
- else {
- *outstr++= *str++;
- }
+ }
+
+ if (!matched)
+ return;
}
- *outstr++= '\0';
- if (orig!=name)
- free(orig);
- return name;
-}
-/***====================================================================***/
+ for (unsigned i = 0; i < m->mapping.num_kccgst; i++) {
+ enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i];
+ struct sval value = m->rule.kccgst_value_at_pos[i];
+ append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value);
+ }
-static bool
-XkbcRF_GetComponents(struct rules *rules, const struct var_defs *defs,
- struct xkb_component_names *names)
-{
- struct multi_defs mdefs;
-
- MakeMultiDefs(&mdefs, defs);
-
- memset(names, 0, sizeof(struct xkb_component_names));
- XkbRF_ClearPartialMatches(rules);
- XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_NORMAL);
- XkbRF_ApplyPartialMatches(rules, names);
- XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_APPEND);
- XkbRF_ApplyPartialMatches(rules, names);
- XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_OPTION);
- XkbRF_ApplyPartialMatches(rules, names);
-
- names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
- names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
- names->types = XkbRF_SubstituteVars(names->types, &mdefs);
- names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
- names->keymap = XkbRF_SubstituteVars(names->keymap, &mdefs);
-
- FreeMultiDefs(&mdefs);
- return (names->keycodes && names->symbols && names->types &&
- names->compat) || names->keymap;
+ /*
+ * If a rule matches in a rule set, the rest of the set should be
+ * skipped. However, rule sets matching against options may contain
+ * several legitimate rules, so they are processed entirely.
+ */
+ if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
+ m->mapping.skip = true;
}
-static struct rule *
-XkbcRF_AddRule(struct rules *rules)
+static enum rules_token
+gettok(struct matcher *m, struct scanner *s)
{
- if (rules->sz_rules<1) {
- rules->sz_rules= 16;
- rules->num_rules= 0;
- rules->rules = calloc(rules->sz_rules, sizeof(*rules->rules));
- }
- else if (rules->num_rules>=rules->sz_rules) {
- rules->sz_rules*= 2;
- rules->rules = realloc(rules->rules,
- rules->sz_rules * sizeof(*rules->rules));
- }
- if (!rules->rules) {
- rules->sz_rules= rules->num_rules= 0;
- return NULL;
- }
- memset(&rules->rules[rules->num_rules], 0, sizeof(*rules->rules));
- return &rules->rules[rules->num_rules++];
+ return lex(s, &m->val);
}
-static struct group *
-XkbcRF_AddGroup(struct rules *rules)
+static bool
+matcher_match(struct matcher *m, struct scanner *s,
+ unsigned include_depth,
+ const char *string, size_t len,
+ const char *file_name)
{
- if (rules->sz_groups<1) {
- rules->sz_groups= 16;
- rules->num_groups= 0;
- rules->groups = calloc(rules->sz_groups, sizeof(*rules->groups));
- }
- else if (rules->num_groups >= rules->sz_groups) {
- rules->sz_groups *= 2;
- rules->groups = realloc(rules->groups,
- rules->sz_groups * sizeof(*rules->groups));
+ enum rules_token tok;
+
+ if (!m)
+ return false;
+
+initial:
+ switch (tok = gettok(m, s)) {
+ case TOK_BANG:
+ goto bang;
+ case TOK_END_OF_LINE:
+ goto initial;
+ case TOK_END_OF_FILE:
+ goto finish;
+ default:
+ goto unexpected;
}
- if (!rules->groups) {
- rules->sz_groups= rules->num_groups= 0;
- return NULL;
+
+bang:
+ switch (tok = gettok(m, s)) {
+ case TOK_GROUP_NAME:
+ matcher_group_start_new(m, m->val.string);
+ goto group_name;
+ case TOK_INCLUDE:
+ goto include_statement;
+ case TOK_IDENTIFIER:
+ matcher_mapping_start_new(m);
+ matcher_mapping_set_mlvo(m, s, m->val.string);
+ goto mapping_mlvo;
+ default:
+ goto unexpected;
}
- memset(&rules->groups[rules->num_groups], 0, sizeof(*rules->groups));
- return &rules->groups[rules->num_groups++];
-}
+group_name:
+ switch (tok = gettok(m, s)) {
+ case TOK_EQUALS:
+ goto group_element;
+ default:
+ goto unexpected;
+ }
-static struct rules *
-XkbcRF_LoadRules(FILE *file)
-{
- struct input_line line;
- struct mapping mapping;
- struct rule trule, *rule;
- struct group tgroup, *group;
- struct rules *rules;
+group_element:
+ switch (tok = gettok(m, s)) {
+ case TOK_IDENTIFIER:
+ matcher_group_add_element(m, s, m->val.string);
+ goto group_element;
+ case TOK_END_OF_LINE:
+ goto initial;
+ default:
+ goto unexpected;
+ }
- rules = calloc(1, sizeof(*rules));
- if (!rules)
- return NULL;
+include_statement:
+ switch (tok = gettok(m, s)) {
+ case TOK_IDENTIFIER:
+ matcher_include(m, s, include_depth, m->val.string);
+ goto initial;
+ default:
+ goto unexpected;
+ }
- memset(&mapping, 0, sizeof(mapping));
- memset(&tgroup, 0, sizeof(tgroup));
- input_line_init(&line);
-
- while (input_line_get(file, &line)) {
- if (match_line(&line, &mapping, &trule, &tgroup)) {
- if (tgroup.number) {
- if ((group= XkbcRF_AddGroup(rules))!=NULL) {
- *group= tgroup;
- memset(&tgroup, 0, sizeof(tgroup));
- }
- } else {
- if ((rule= XkbcRF_AddRule(rules))!=NULL) {
- *rule= trule;
- memset(&trule, 0, sizeof(trule));
- }
- }
- }
- line.offset = 0;
+mapping_mlvo:
+ switch (tok = gettok(m, s)) {
+ case TOK_IDENTIFIER:
+ if (!m->mapping.skip)
+ matcher_mapping_set_mlvo(m, s, m->val.string);
+ goto mapping_mlvo;
+ case TOK_EQUALS:
+ goto mapping_kccgst;
+ default:
+ goto unexpected;
}
- input_line_deinit(&line);
- return rules;
-}
-static void
-XkbRF_ClearVarDescriptions(struct describe_vars *var)
-{
- int i;
+mapping_kccgst:
+ switch (tok = gettok(m, s)) {
+ case TOK_IDENTIFIER:
+ if (!m->mapping.skip)
+ matcher_mapping_set_kccgst(m, s, m->val.string);
+ goto mapping_kccgst;
+ case TOK_END_OF_LINE:
+ if (!m->mapping.skip)
+ matcher_mapping_verify(m, s);
+ goto rule_mlvo_first;
+ default:
+ goto unexpected;
+ }
- for (i=0;i<var->num_desc;i++) {
- free(var->desc[i].name);
- free(var->desc[i].desc);
- var->desc[i].name= var->desc[i].desc= NULL;
+rule_mlvo_first:
+ switch (tok = gettok(m, s)) {
+ case TOK_BANG:
+ goto bang;
+ case TOK_END_OF_LINE:
+ goto rule_mlvo_first;
+ case TOK_END_OF_FILE:
+ goto finish;
+ default:
+ matcher_rule_start_new(m);
+ goto rule_mlvo_no_tok;
}
- free(var->desc);
- var->desc= NULL;
-}
-static void
-XkbcRF_Free(struct rules *rules)
-{
- int i;
- struct rule *rule;
- struct group *group;
+rule_mlvo:
+ tok = gettok(m, s);
+rule_mlvo_no_tok:
+ switch (tok) {
+ case TOK_IDENTIFIER:
+ if (!m->rule.skip)
+ matcher_rule_set_mlvo(m, s, m->val.string);
+ goto rule_mlvo;
+ case TOK_STAR:
+ if (!m->rule.skip)
+ matcher_rule_set_mlvo_wildcard(m, s);
+ goto rule_mlvo;
+ case TOK_GROUP_NAME:
+ if (!m->rule.skip)
+ matcher_rule_set_mlvo_group(m, s, m->val.string);
+ goto rule_mlvo;
+ case TOK_EQUALS:
+ goto rule_kccgst;
+ default:
+ goto unexpected;
+ }
- if (!rules)
- return;
- XkbRF_ClearVarDescriptions(&rules->models);
- XkbRF_ClearVarDescriptions(&rules->layouts);
- XkbRF_ClearVarDescriptions(&rules->variants);
- XkbRF_ClearVarDescriptions(&rules->options);
-
- for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
- free(rule->model);
- free(rule->layout);
- free(rule->variant);
- free(rule->option);
- free(rule->keycodes);
- free(rule->symbols);
- free(rule->types);
- free(rule->compat);
- free(rule->keymap);
+rule_kccgst:
+ switch (tok = gettok(m, s)) {
+ case TOK_IDENTIFIER:
+ if (!m->rule.skip)
+ matcher_rule_set_kccgst(m, s, m->val.string);
+ goto rule_kccgst;
+ case TOK_END_OF_LINE:
+ if (!m->rule.skip)
+ matcher_rule_verify(m, s);
+ if (!m->rule.skip)
+ matcher_rule_apply_if_matches(m, s);
+ goto rule_mlvo_first;
+ default:
+ goto unexpected;
}
- free(rules->rules);
- for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
- free(group->name);
- free(group->words);
+unexpected:
+ switch (tok) {
+ case TOK_ERROR:
+ goto error;
+ default:
+ goto state_error;
}
- free(rules->groups);
- free(rules);
+finish:
+ return true;
+
+state_error:
+ scanner_err(s, "unexpected token");
+error:
+ return false;
}
-struct xkb_component_names *
-xkb_components_from_rules(struct xkb_context *ctx,
- const struct xkb_rule_names *rmlvo)
+static bool
+read_rules_file(struct xkb_context *ctx,
+ struct matcher *matcher,
+ unsigned include_depth,
+ FILE *file,
+ const char *path)
{
- int i;
- FILE *rulesFile;
- char *rulesPath;
- struct rules *loaded;
- struct xkb_component_names *names = NULL;
- struct var_defs defs = {
- .model = rmlvo->model,
- .layout = rmlvo->layout,
- .variant = rmlvo->variant,
- .options = rmlvo->options,
- };
-
- rulesFile = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile,
- &rulesPath);
- if (!rulesFile) {
- ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
- ERROR("%d include paths searched:\n",
- xkb_context_num_include_paths(ctx));
- for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
- ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
- return NULL;
+ bool ret = false;
+ char *string;
+ size_t size;
+ struct scanner scanner;
+
+ ret = map_file(file, &string, &size);
+ if (!ret) {
+ log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+ "Couldn't read rules file \"%s\": %s\n",
+ path, strerror(errno));
+ goto out;
}
- loaded = XkbcRF_LoadRules(rulesFile);
- if (!loaded) {
- ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
- goto unwind_file;
- }
+ scanner_init(&scanner, matcher->ctx, string, size, path, NULL);
- names = calloc(1, sizeof(*names));
- if (!names) {
- ERROR("failed to allocate XKB components\n");
- goto unwind_file;
- }
+ ret = matcher_match(matcher, &scanner, include_depth, string, size, path);
- if (!XkbcRF_GetComponents(loaded, &defs, names)) {
- free(names->keymap);
- free(names->keycodes);
- free(names->types);
- free(names->compat);
- free(names->symbols);
- free(names);
- names = NULL;
- ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
+ unmap_file(string, size);
+out:
+ return ret;
+}
+
+bool
+xkb_components_from_rules(struct xkb_context *ctx,
+ const struct xkb_rule_names *rmlvo,
+ struct xkb_component_names *out)
+{
+ bool ret = false;
+ FILE *file;
+ char *path = NULL;
+ struct matcher *matcher = NULL;
+ struct matched_sval *mval;
+ unsigned int offset = 0;
+
+ file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path, &offset);
+ if (!file)
+ goto err_out;
+
+ matcher = matcher_new(ctx, rmlvo);
+
+ ret = read_rules_file(ctx, matcher, 0, file, path);
+ if (!ret ||
+ darray_empty(matcher->kccgst[KCCGST_KEYCODES]) ||
+ darray_empty(matcher->kccgst[KCCGST_TYPES]) ||
+ darray_empty(matcher->kccgst[KCCGST_COMPAT]) ||
+ /* darray_empty(matcher->kccgst[KCCGST_GEOMETRY]) || */
+ darray_empty(matcher->kccgst[KCCGST_SYMBOLS])) {
+ log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+ "No components returned from XKB rules \"%s\"\n", path);
+ ret = false;
+ goto err_out;
}
-unwind_file:
- XkbcRF_Free(loaded);
- if (rulesFile)
- fclose(rulesFile);
- free(rulesPath);
- return names;
+ darray_steal(matcher->kccgst[KCCGST_KEYCODES], &out->keycodes, NULL);
+ darray_steal(matcher->kccgst[KCCGST_TYPES], &out->types, NULL);
+ darray_steal(matcher->kccgst[KCCGST_COMPAT], &out->compat, NULL);
+ darray_steal(matcher->kccgst[KCCGST_SYMBOLS], &out->symbols, NULL);
+ darray_free(matcher->kccgst[KCCGST_GEOMETRY]);
+
+ mval = &matcher->rmlvo.model;
+ if (!mval->matched && mval->sval.len > 0)
+ log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
+ "Unrecognized RMLVO model \"%.*s\" was ignored\n",
+ mval->sval.len, mval->sval.start);
+ darray_foreach(mval, matcher->rmlvo.layouts)
+ if (!mval->matched && mval->sval.len > 0)
+ log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
+ "Unrecognized RMLVO layout \"%.*s\" was ignored\n",
+ mval->sval.len, mval->sval.start);
+ darray_foreach(mval, matcher->rmlvo.variants)
+ if (!mval->matched && mval->sval.len > 0)
+ log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
+ "Unrecognized RMLVO variant \"%.*s\" was ignored\n",
+ mval->sval.len, mval->sval.start);
+ darray_foreach(mval, matcher->rmlvo.options)
+ if (!mval->matched && mval->sval.len > 0)
+ log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
+ "Unrecognized RMLVO option \"%.*s\" was ignored\n",
+ mval->sval.len, mval->sval.start);
+
+err_out:
+ if (file)
+ fclose(file);
+ matcher_free(matcher);
+ free(path);
+ return ret;
}