diff options
| author | Haibo Huang <hhb@google.com> | 2020-09-08 17:10:03 -0700 |
|---|---|---|
| committer | Haibo Huang <hhb@google.com> | 2020-09-10 22:20:42 +0000 |
| commit | bffa8499cb8ce3cc4366055be8fe62d501d6a8e5 (patch) | |
| tree | 648dfaada5799a6227dd5f1af43d89ed8d71d96d /src/xkbcomp | |
| parent | e4e474780d90ed6166f7113a7464371baa275007 (diff) | |
| download | platform_external_libxkbcommon-master.tar.gz platform_external_libxkbcommon-master.tar.bz2 platform_external_libxkbcommon-master.zip | |
1. Run meson build locally:
meson config -Denable-x11=false -Denable-wayland=false -Denable-docs=false
2. Copy over generated parser.h / parser.c
3. Copy over config.h, and remove defines for not supported functions.
Change-Id: Id7f3c822c1d958fa541685344961507bcfa03b17
Diffstat (limited to 'src/xkbcomp')
| -rw-r--r-- | src/xkbcomp/action.c | 873 | ||||
| -rw-r--r-- | src/xkbcomp/action.h | 56 | ||||
| -rw-r--r-- | src/xkbcomp/ast-build.c | 830 | ||||
| -rw-r--r-- | src/xkbcomp/ast-build.h | 125 | ||||
| -rw-r--r-- | src/xkbcomp/ast.h | 360 | ||||
| -rw-r--r-- | src/xkbcomp/compat.c | 934 | ||||
| -rw-r--r-- | src/xkbcomp/expr.c | 694 | ||||
| -rw-r--r-- | src/xkbcomp/expr.h | 85 | ||||
| -rw-r--r-- | src/xkbcomp/include.c | 323 | ||||
| -rw-r--r-- | src/xkbcomp/include.h | 43 | ||||
| -rw-r--r-- | src/xkbcomp/keycodes.c | 671 | ||||
| -rw-r--r-- | src/xkbcomp/keymap-dump.c | 666 | ||||
| -rw-r--r-- | src/xkbcomp/keymap.c | 300 | ||||
| -rw-r--r-- | src/xkbcomp/keywords.c | 389 | ||||
| -rw-r--r-- | src/xkbcomp/keywords.gperf | 77 | ||||
| -rw-r--r-- | src/xkbcomp/parser-priv.h | 44 | ||||
| -rw-r--r-- | src/xkbcomp/parser.c | 3403 | ||||
| -rw-r--r-- | src/xkbcomp/parser.h | 169 | ||||
| -rw-r--r-- | src/xkbcomp/parser.y | 841 | ||||
| -rw-r--r-- | src/xkbcomp/rules.c | 1163 | ||||
| -rw-r--r-- | src/xkbcomp/rules.h | 32 | ||||
| -rw-r--r-- | src/xkbcomp/scanner.c | 212 | ||||
| -rw-r--r-- | src/xkbcomp/symbols.c | 1600 | ||||
| -rw-r--r-- | src/xkbcomp/types.c | 744 | ||||
| -rw-r--r-- | src/xkbcomp/vmod.c | 107 | ||||
| -rw-r--r-- | src/xkbcomp/vmod.h | 34 | ||||
| -rw-r--r-- | src/xkbcomp/xkbcomp-priv.h | 124 | ||||
| -rw-r--r-- | src/xkbcomp/xkbcomp.c | 141 |
28 files changed, 15040 insertions, 0 deletions
diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c new file mode 100644 index 0000000..605f159 --- /dev/null +++ b/src/xkbcomp/action.c @@ -0,0 +1,873 @@ +/************************************************************ + * Copyright (c) 1994 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 © 2012 Intel Corporation + * 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. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "action.h" + +static const ExprBoolean constTrue = { + .expr = { + .common = { .type = STMT_EXPR, .next = NULL }, + .op = EXPR_VALUE, + .value_type = EXPR_TYPE_BOOLEAN, + }, + .set = true, +}; + +static const ExprBoolean constFalse = { + .expr = { + .common = { .type = STMT_EXPR, .next = NULL }, + .op = EXPR_VALUE, + .value_type = EXPR_TYPE_BOOLEAN, + }, + .set = false, +}; + +enum action_field { + ACTION_FIELD_CLEAR_LOCKS, + ACTION_FIELD_LATCH_TO_LOCK, + ACTION_FIELD_GEN_KEY_EVENT, + ACTION_FIELD_REPORT, + ACTION_FIELD_DEFAULT, + ACTION_FIELD_AFFECT, + ACTION_FIELD_INCREMENT, + ACTION_FIELD_MODIFIERS, + ACTION_FIELD_GROUP, + ACTION_FIELD_X, + ACTION_FIELD_Y, + ACTION_FIELD_ACCEL, + ACTION_FIELD_BUTTON, + ACTION_FIELD_VALUE, + ACTION_FIELD_CONTROLS, + ACTION_FIELD_TYPE, + ACTION_FIELD_COUNT, + ACTION_FIELD_SCREEN, + ACTION_FIELD_SAME, + ACTION_FIELD_DATA, + ACTION_FIELD_DEVICE, + ACTION_FIELD_KEYCODE, + ACTION_FIELD_MODS_TO_CLEAR, +}; + +ActionsInfo * +NewActionsInfo(void) +{ + enum xkb_action_type type; + ActionsInfo *info; + + info = calloc(1, sizeof(*info)); + if (!info) + return NULL; + + for (type = 0; type < _ACTION_TYPE_NUM_ENTRIES; type++) + info->actions[type].type = type; + + /* Apply some "factory defaults". */ + + /* Increment default button. */ + info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.flags = 0; + info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.value = 1; + info->actions[ACTION_TYPE_PTR_MOVE].ptr.flags = ACTION_ACCEL; + info->actions[ACTION_TYPE_SWITCH_VT].screen.flags = ACTION_SAME_SCREEN; + + return info; +} + +void +FreeActionsInfo(ActionsInfo *info) +{ + free(info); +} + +static const LookupEntry fieldStrings[] = { + { "clearLocks", ACTION_FIELD_CLEAR_LOCKS }, + { "latchToLock", ACTION_FIELD_LATCH_TO_LOCK }, + { "genKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, + { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, + { "report", ACTION_FIELD_REPORT }, + { "default", ACTION_FIELD_DEFAULT }, + { "affect", ACTION_FIELD_AFFECT }, + { "increment", ACTION_FIELD_INCREMENT }, + { "modifiers", ACTION_FIELD_MODIFIERS }, + { "mods", ACTION_FIELD_MODIFIERS }, + { "group", ACTION_FIELD_GROUP }, + { "x", ACTION_FIELD_X }, + { "y", ACTION_FIELD_Y }, + { "accel", ACTION_FIELD_ACCEL }, + { "accelerate", ACTION_FIELD_ACCEL }, + { "repeat", ACTION_FIELD_ACCEL }, + { "button", ACTION_FIELD_BUTTON }, + { "value", ACTION_FIELD_VALUE }, + { "controls", ACTION_FIELD_CONTROLS }, + { "ctrls", ACTION_FIELD_CONTROLS }, + { "type", ACTION_FIELD_TYPE }, + { "count", ACTION_FIELD_COUNT }, + { "screen", ACTION_FIELD_SCREEN }, + { "same", ACTION_FIELD_SAME }, + { "sameServer", ACTION_FIELD_SAME }, + { "data", ACTION_FIELD_DATA }, + { "device", ACTION_FIELD_DEVICE }, + { "dev", ACTION_FIELD_DEVICE }, + { "key", ACTION_FIELD_KEYCODE }, + { "keycode", ACTION_FIELD_KEYCODE }, + { "kc", ACTION_FIELD_KEYCODE }, + { "clearmods", ACTION_FIELD_MODS_TO_CLEAR }, + { "clearmodifiers", ACTION_FIELD_MODS_TO_CLEAR }, + { NULL, 0 } +}; + +static bool +stringToAction(const char *str, enum xkb_action_type *type_rtrn) +{ + return LookupString(actionTypeNames, str, type_rtrn); +} + +static bool +stringToField(const char *str, enum action_field *field_rtrn) +{ + return LookupString(fieldStrings, str, field_rtrn); +} + +static const char * +fieldText(enum action_field field) +{ + return LookupValue(fieldStrings, field); +} + +/***====================================================================***/ + +static inline bool +ReportMismatch(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field, const char *type) +{ + log_err(ctx, + "Value of %s field must be of type %s; " + "Action %s definition ignored\n", + fieldText(field), type, ActionTypeText(action)); + return false; +} + +static inline bool +ReportIllegal(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field) +{ + log_err(ctx, + "Field %s is not defined for an action of type %s; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action)); + return false; +} + +static inline bool +ReportActionNotArray(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field) +{ + log_err(ctx, + "The %s field in the %s action is not an array; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action)); + return false; +} + +static bool +HandleNoAction(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) + +{ + return true; +} + +static bool +CheckBooleanFlag(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field, enum xkb_action_flags flag, + const ExprDef *array_ndx, const ExprDef *value, + enum xkb_action_flags *flags_inout) +{ + bool set; + + if (array_ndx) + return ReportActionNotArray(ctx, action, field); + + if (!ExprResolveBoolean(ctx, value, &set)) + return ReportMismatch(ctx, action, field, "boolean"); + + if (set) + *flags_inout |= flag; + else + *flags_inout &= ~flag; + + return true; +} + +static bool +CheckModifierField(struct xkb_context *ctx, const struct xkb_mod_set *mods, + enum xkb_action_type action, const ExprDef *array_ndx, + const ExprDef *value, enum xkb_action_flags *flags_inout, + xkb_mod_mask_t *mods_rtrn) +{ + if (array_ndx) + return ReportActionNotArray(ctx, action, ACTION_FIELD_MODIFIERS); + + if (value->expr.op == EXPR_IDENT) { + const char *valStr; + valStr = xkb_atom_text(ctx, value->ident.ident); + if (valStr && (istreq(valStr, "usemodmapmods") || + istreq(valStr, "modmapmods"))) { + *mods_rtrn = 0; + *flags_inout |= ACTION_MODS_LOOKUP_MODMAP; + return true; + } + } + + if (!ExprResolveModMask(ctx, value, MOD_BOTH, mods, mods_rtrn)) + return ReportMismatch(ctx, action, + ACTION_FIELD_MODIFIERS, "modifier mask"); + + *flags_inout &= ~ACTION_MODS_LOOKUP_MODMAP; + return true; +} + +static const LookupEntry lockWhich[] = { + { "both", 0 }, + { "lock", ACTION_LOCK_NO_UNLOCK }, + { "neither", (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK) }, + { "unlock", ACTION_LOCK_NO_LOCK }, + { NULL, 0 } +}; + +static bool +CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action, + const ExprDef *array_ndx, const ExprDef *value, + enum xkb_action_flags *flags_inout) +{ + enum xkb_action_flags flags; + + if (array_ndx) + return ReportActionNotArray(ctx, action, ACTION_FIELD_AFFECT); + + if (!ExprResolveEnum(ctx, value, &flags, lockWhich)) + return ReportMismatch(ctx, action, ACTION_FIELD_AFFECT, + "lock, unlock, both, neither"); + + *flags_inout &= ~(ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK); + *flags_inout |= flags; + return true; +} + +static bool +HandleSetLatchLockMods(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_mod_action *act = &action->mods; + const enum xkb_action_type type = action->type; + + if (field == ACTION_FIELD_MODIFIERS) + return CheckModifierField(ctx, mods, action->type, array_ndx, value, + &act->flags, &act->mods.mods); + if ((type == ACTION_TYPE_MOD_SET || type == ACTION_TYPE_MOD_LATCH) && + field == ACTION_FIELD_CLEAR_LOCKS) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LOCK_CLEAR, array_ndx, value, + &act->flags); + if (type == ACTION_TYPE_MOD_LATCH && + field == ACTION_FIELD_LATCH_TO_LOCK) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LATCH_TO_LOCK, array_ndx, value, + &act->flags); + if (type == ACTION_TYPE_MOD_LOCK && + field == ACTION_FIELD_AFFECT) + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + + return ReportIllegal(ctx, action->type, field); +} + +static bool +CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action, + const ExprDef *array_ndx, const ExprDef *value, + enum xkb_action_flags *flags_inout, int32_t *group_rtrn) +{ + const ExprDef *spec; + xkb_layout_index_t idx; + enum xkb_action_flags flags = *flags_inout; + + if (array_ndx) + return ReportActionNotArray(ctx, action, ACTION_FIELD_GROUP); + + if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) { + flags &= ~ACTION_ABSOLUTE_SWITCH; + spec = value->unary.child; + } + else { + flags |= ACTION_ABSOLUTE_SWITCH; + spec = value; + } + + if (!ExprResolveGroup(ctx, spec, &idx)) + return ReportMismatch(ctx, 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) { + *group_rtrn = (int32_t) idx; + if (value->expr.op == EXPR_NEGATE) + *group_rtrn = -*group_rtrn; + } + else { + *group_rtrn = (int32_t) (idx - 1); + } + *flags_inout = flags; + return true; +} + +static bool +HandleSetLatchLockGroup(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_group_action *act = &action->group; + const enum xkb_action_type type = action->type; + + if (field == ACTION_FIELD_GROUP) + return CheckGroupField(ctx, action->type, array_ndx, value, + &act->flags, &act->group); + if ((type == ACTION_TYPE_GROUP_SET || type == ACTION_TYPE_GROUP_LATCH) && + field == ACTION_FIELD_CLEAR_LOCKS) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LOCK_CLEAR, array_ndx, value, + &act->flags); + if (type == ACTION_TYPE_GROUP_LATCH && + field == ACTION_FIELD_LATCH_TO_LOCK) + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LATCH_TO_LOCK, array_ndx, value, + &act->flags); + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_pointer_action *act = &action->ptr; + + if (field == ACTION_FIELD_X || field == ACTION_FIELD_Y) { + int val; + const bool absolute = (value->expr.op != EXPR_NEGATE && + value->expr.op != EXPR_UNARY_PLUS); + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveInteger(ctx, value, &val)) + return ReportMismatch(ctx, action->type, field, "integer"); + + if (val < INT16_MIN || val > INT16_MAX) { + log_err(ctx, + "The %s field in the %s action must be in range %d..%d; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action->type), + INT16_MIN, INT16_MAX); + return false; + } + + if (field == ACTION_FIELD_X) { + if (absolute) + act->flags |= ACTION_ABSOLUTE_X; + act->x = (int16_t) val; + } + else { + if (absolute) + act->flags |= ACTION_ABSOLUTE_Y; + act->y = (int16_t) val; + } + + return true; + } + else if (field == ACTION_FIELD_ACCEL) { + return CheckBooleanFlag(ctx, action->type, field, + ACTION_ACCEL, array_ndx, value, &act->flags); + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_pointer_button_action *act = &action->btn; + + if (field == ACTION_FIELD_BUTTON) { + int btn; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveButton(ctx, value, &btn)) + return ReportMismatch(ctx, action->type, field, + "integer (range 1..5)"); + + if (btn < 0 || btn > 5) { + log_err(ctx, + "Button must specify default or be in the range 1..5; " + "Illegal button value %d ignored\n", btn); + return false; + } + + act->button = btn; + return true; + } + else if (action->type == ACTION_TYPE_PTR_LOCK && + field == ACTION_FIELD_AFFECT) { + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + } + else if (field == ACTION_FIELD_COUNT) { + int val; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveInteger(ctx, value, &val)) + return ReportMismatch(ctx, action->type, field, "integer"); + + if (val < 0 || val > 255) { + log_err(ctx, + "The count field must have a value in the range 0..255; " + "Illegal count %d ignored\n", val); + return false; + } + + act->count = (uint8_t) val; + return true; + } + + return ReportIllegal(ctx, action->type, field); +} + +static const LookupEntry ptrDflts[] = { + { "dfltbtn", 1 }, + { "defaultbutton", 1 }, + { "button", 1 }, + { NULL, 0 } +}; + +static bool +HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_pointer_default_action *act = &action->dflt; + + if (field == ACTION_FIELD_AFFECT) { + unsigned int val; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveEnum(ctx, value, &val, ptrDflts)) + return ReportMismatch(ctx, action->type, field, + "pointer component"); + return true; + } + else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) { + const ExprDef *button; + int btn; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (value->expr.op == EXPR_NEGATE || + value->expr.op == EXPR_UNARY_PLUS) { + act->flags &= ~ACTION_ABSOLUTE_SWITCH; + button = value->unary.child; + } + else { + act->flags |= ACTION_ABSOLUTE_SWITCH; + button = value; + } + + if (!ExprResolveButton(ctx, button, &btn)) + return ReportMismatch(ctx, action->type, field, + "integer (range 1..5)"); + + if (btn < 0 || btn > 5) { + log_err(ctx, + "New default button value must be in the range 1..5; " + "Illegal default button value %d ignored\n", btn); + return false; + } + if (btn == 0) { + log_err(ctx, + "Cannot set default pointer button to \"default\"; " + "Illegal default button setting ignored\n"); + return false; + } + + act->value = (value->expr.op == EXPR_NEGATE ? -btn: btn); + return true; + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_switch_screen_action *act = &action->screen; + + if (field == ACTION_FIELD_SCREEN) { + const ExprDef *scrn; + int val; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (value->expr.op == EXPR_NEGATE || + value->expr.op == EXPR_UNARY_PLUS) { + act->flags &= ~ACTION_ABSOLUTE_SWITCH; + scrn = value->unary.child; + } + else { + act->flags |= ACTION_ABSOLUTE_SWITCH; + scrn = value; + } + + if (!ExprResolveInteger(ctx, scrn, &val)) + return ReportMismatch(ctx, action->type, field, + "integer (0..255)"); + + if (val < 0 || val > 255) { + log_err(ctx, + "Screen index must be in the range 1..255; " + "Illegal screen value %d ignored\n", val); + return false; + } + + act->screen = (value->expr.op == EXPR_NEGATE ? -val : val); + return true; + } + else if (field == ACTION_FIELD_SAME) { + return CheckBooleanFlag(ctx, action->type, field, + ACTION_SAME_SCREEN, array_ndx, value, + &act->flags); + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_controls_action *act = &action->ctrls; + + if (field == ACTION_FIELD_CONTROLS) { + enum xkb_action_controls mask; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveMask(ctx, value, &mask, ctrlMaskNames)) + return ReportMismatch(ctx, action->type, field, + "controls mask"); + + act->ctrls = mask; + return true; + } + else if (field == ACTION_FIELD_AFFECT) { + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + } + + return ReportIllegal(ctx, action->type, field); +} + +static bool +HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods, + union xkb_action *action, enum action_field field, + const ExprDef *array_ndx, const ExprDef *value) +{ + struct xkb_private_action *act = &action->priv; + + if (field == ACTION_FIELD_TYPE) { + int type; + + if (array_ndx) + return ReportActionNotArray(ctx, action->type, field); + + if (!ExprResolveInteger(ctx, value, &type)) + return ReportMismatch(ctx, ACTION_TYPE_PRIVATE, field, "integer"); + + if (type < 0 || type > 255) { + log_err(ctx, + "Private action type must be in the range 0..255; " + "Illegal type %d ignored\n", type); + return false; + } + + /* + * It's possible for someone to write something like this: + * actions = [ Private(type=3,data[0]=1,data[1]=3,data[2]=3) ] + * where the type refers to some existing action type, e.g. LockMods. + * This assumes that this action's struct is laid out in memory + * exactly as described in the XKB specification and libraries. + * We, however, have changed these structs in various ways, so this + * assumption is no longer true. Since this is a lousy "feature", we + * make actions like these no-ops for now. + */ + if (type < ACTION_TYPE_PRIVATE) { + log_info(ctx, + "Private actions of type %s are not supported; Ignored\n", + ActionTypeText(type)); + act->type = ACTION_TYPE_NONE; + } + else { + act->type = (enum xkb_action_type) type; + } + + return true; + } + else if (field == ACTION_FIELD_DATA) { + if (array_ndx == NULL) { + xkb_atom_t val; + const char *str; + size_t len; + + if (!ExprResolveString(ctx, value, &val)) + return ReportMismatch(ctx, action->type, field, "string"); + + str = xkb_atom_text(ctx, val); + len = strlen(str); + if (len < 1 || len > 7) { + log_warn(ctx, + "A private action has 7 data bytes; " + "Illegal data ignored\n"); + return false; + } + + /* act->data may not be null-terminated, this is intentional */ + strncpy((char *) act->data, str, sizeof(act->data)); + return true; + } + else { + int ndx, datum; + + if (!ExprResolveInteger(ctx, array_ndx, &ndx)) { + log_err(ctx, + "Array subscript must be integer; " + "Illegal subscript ignored\n"); + return false; + } + + if (ndx < 0 || (size_t) ndx >= sizeof(act->data)) { + log_err(ctx, + "The data for a private action is %lu bytes long; " + "Attempt to use data[%d] ignored\n", + (unsigned long) sizeof(act->data), ndx); + return false; + } + + if (!ExprResolveInteger(ctx, value, &datum)) + return ReportMismatch(ctx, act->type, field, "integer"); + + if (datum < 0 || datum > 255) { + log_err(ctx, + "All data for a private action must be 0..255; " + "Illegal datum %d ignored\n", datum); + return false; + } + + act->data[ndx] = (uint8_t) datum; + return true; + } + } + + return ReportIllegal(ctx, ACTION_TYPE_NONE, field); +} + +typedef bool (*actionHandler)(struct xkb_context *ctx, + const struct xkb_mod_set *mods, + union xkb_action *action, + enum action_field field, + const ExprDef *array_ndx, + const ExprDef *value); + +static const actionHandler handleAction[_ACTION_TYPE_NUM_ENTRIES] = { + [ACTION_TYPE_NONE] = HandleNoAction, + [ACTION_TYPE_MOD_SET] = HandleSetLatchLockMods, + [ACTION_TYPE_MOD_LATCH] = HandleSetLatchLockMods, + [ACTION_TYPE_MOD_LOCK] = HandleSetLatchLockMods, + [ACTION_TYPE_GROUP_SET] = HandleSetLatchLockGroup, + [ACTION_TYPE_GROUP_LATCH] = HandleSetLatchLockGroup, + [ACTION_TYPE_GROUP_LOCK] = HandleSetLatchLockGroup, + [ACTION_TYPE_PTR_MOVE] = HandleMovePtr, + [ACTION_TYPE_PTR_BUTTON] = HandlePtrBtn, + [ACTION_TYPE_PTR_LOCK] = HandlePtrBtn, + [ACTION_TYPE_PTR_DEFAULT] = HandleSetPtrDflt, + [ACTION_TYPE_TERMINATE] = HandleNoAction, + [ACTION_TYPE_SWITCH_VT] = HandleSwitchScreen, + [ACTION_TYPE_CTRL_SET] = HandleSetLockControls, + [ACTION_TYPE_CTRL_LOCK] = HandleSetLockControls, + [ACTION_TYPE_PRIVATE] = HandlePrivate, +}; + +/***====================================================================***/ + +bool +HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, + const struct xkb_mod_set *mods, ExprDef *def, + union xkb_action *action) +{ + ExprDef *arg; + const char *str; + enum xkb_action_type handler_type; + + if (def->expr.op != EXPR_ACTION_DECL) { + log_err(ctx, "Expected an action definition, found %s\n", + expr_op_type_to_string(def->expr.op)); + return false; + } + + str = xkb_atom_text(ctx, def->action.name); + if (!stringToAction(str, &handler_type)) { + log_err(ctx, "Unknown action %s\n", str); + return false; + } + + /* + * Get the default values for this action type, as modified by + * statements such as: + * latchMods.clearLocks = True; + */ + *action = info->actions[handler_type]; + + /* + * Now change the action properties as specified for this + * particular instance, e.g. "modifiers" and "clearLocks" in: + * SetMods(modifiers=Alt,clearLocks); + */ + for (arg = def->action.args; arg != NULL; + arg = (ExprDef *) arg->common.next) { + const ExprDef *value; + ExprDef *field, *arrayRtrn; + const char *elemRtrn, *fieldRtrn; + enum action_field fieldNdx; + + if (arg->expr.op == EXPR_ASSIGN) { + field = arg->binary.left; + value = arg->binary.right; + } + else if (arg->expr.op == EXPR_NOT || arg->expr.op == EXPR_INVERT) { + field = arg->unary.child; + value = (const ExprDef *) &constFalse; + } + else { + field = arg; + value = (const ExprDef *) &constTrue; + } + + if (!ExprResolveLhs(ctx, field, &elemRtrn, &fieldRtrn, &arrayRtrn)) + return false; + + if (elemRtrn) { + log_err(ctx, + "Cannot change defaults in an action definition; " + "Ignoring attempt to change %s.%s\n", + elemRtrn, fieldRtrn); + return false; + } + + if (!stringToField(fieldRtrn, &fieldNdx)) { + log_err(ctx, "Unknown field name %s\n", fieldRtrn); + return false; + } + + if (!handleAction[handler_type](ctx, mods, action, fieldNdx, + arrayRtrn, value)) + return false; + } + + return true; +} + +bool +SetActionField(struct xkb_context *ctx, ActionsInfo *info, + struct xkb_mod_set *mods, const char *elem, + const char *field, ExprDef *array_ndx, ExprDef *value) +{ + enum xkb_action_type action; + enum action_field action_field; + + if (!stringToAction(elem, &action)) + return false; + + if (!stringToField(field, &action_field)) { + log_err(ctx, "\"%s\" is not a legal field name\n", field); + return false; + } + + return handleAction[action](ctx, mods, &info->actions[action], + action_field, array_ndx, value); +} diff --git a/src/xkbcomp/action.h b/src/xkbcomp/action.h new file mode 100644 index 0000000..1f92e7b --- /dev/null +++ b/src/xkbcomp/action.h @@ -0,0 +1,56 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_ACTION_H +#define XKBCOMP_ACTION_H + +/* + * This struct contains the default values which every new action + * (e.g. in an interpret statement) starts off with. It can be + * modified within the files (see calls to SetActionField). + */ +typedef struct { + union xkb_action actions[_ACTION_TYPE_NUM_ENTRIES]; +} ActionsInfo; + +ActionsInfo * +NewActionsInfo(void); + +void +FreeActionsInfo(ActionsInfo *info); + +bool +HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, + const struct xkb_mod_set *mods, ExprDef *def, + union xkb_action *action); + +bool +SetActionField(struct xkb_context *ctx, ActionsInfo *info, + struct xkb_mod_set *mods, const char *elem, + const char *field, ExprDef *array_ndx, ExprDef *value); + + +#endif diff --git a/src/xkbcomp/ast-build.c b/src/xkbcomp/ast-build.c new file mode 100644 index 0000000..7ee13d0 --- /dev/null +++ b/src/xkbcomp/ast-build.c @@ -0,0 +1,830 @@ +/************************************************************ + * Copyright (c) 1994 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 © 2012 Intel Corporation + * 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. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "ast-build.h" +#include "include.h" + +static ExprDef * +ExprCreate(enum expr_op_type op, enum expr_value_type type, size_t size) +{ + ExprDef *expr = malloc(size); + if (!expr) + return NULL; + + expr->common.type = STMT_EXPR; + expr->common.next = NULL; + expr->expr.op = op; + expr->expr.value_type = type; + + return expr; +} + +ExprDef * +ExprCreateString(xkb_atom_t str) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_STRING, sizeof(ExprString)); + if (!expr) + return NULL; + expr->string.str = str; + return expr; +} + +ExprDef * +ExprCreateInteger(int ival) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_INT, sizeof(ExprInteger)); + if (!expr) + return NULL; + expr->integer.ival = ival; + return expr; +} + +ExprDef * +ExprCreateFloat(void) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_FLOAT, sizeof(ExprFloat)); + if (!expr) + return NULL; + return expr; +} + +ExprDef * +ExprCreateBoolean(bool set) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_BOOLEAN, sizeof(ExprBoolean)); + if (!expr) + return NULL; + expr->boolean.set = set; + return expr; +} + +ExprDef * +ExprCreateKeyName(xkb_atom_t key_name) +{ + ExprDef *expr = ExprCreate(EXPR_VALUE, EXPR_TYPE_KEYNAME, sizeof(ExprKeyName)); + if (!expr) + return NULL; + expr->key_name.key_name = key_name; + return expr; +} + +ExprDef * +ExprCreateIdent(xkb_atom_t ident) +{ + ExprDef *expr = ExprCreate(EXPR_IDENT, EXPR_TYPE_UNKNOWN, sizeof(ExprIdent)); + if (!expr) + return NULL; + expr->ident.ident = ident; + return expr; +} + +ExprDef * +ExprCreateUnary(enum expr_op_type op, enum expr_value_type type, + ExprDef *child) +{ + ExprDef *expr = ExprCreate(op, type, sizeof(ExprUnary)); + if (!expr) + return NULL; + expr->unary.child = child; + return expr; +} + +ExprDef * +ExprCreateBinary(enum expr_op_type op, ExprDef *left, ExprDef *right) +{ + ExprDef *expr = ExprCreate(op, EXPR_TYPE_UNKNOWN, sizeof(ExprBinary)); + if (!expr) + return NULL; + + if (op == EXPR_ASSIGN || left->expr.value_type == EXPR_TYPE_UNKNOWN) + expr->expr.value_type = right->expr.value_type; + else if (left->expr.value_type == right->expr.value_type || + right->expr.value_type == EXPR_TYPE_UNKNOWN) + expr->expr.value_type = left->expr.value_type; + expr->binary.left = left; + expr->binary.right = right; + + return expr; +} + +ExprDef * +ExprCreateFieldRef(xkb_atom_t element, xkb_atom_t field) +{ + ExprDef *expr = ExprCreate(EXPR_FIELD_REF, EXPR_TYPE_UNKNOWN, sizeof(ExprFieldRef)); + if (!expr) + return NULL; + expr->field_ref.element = element; + expr->field_ref.field = field; + return expr; +} + +ExprDef * +ExprCreateArrayRef(xkb_atom_t element, xkb_atom_t field, ExprDef *entry) +{ + ExprDef *expr = ExprCreate(EXPR_ARRAY_REF, EXPR_TYPE_UNKNOWN, sizeof(ExprArrayRef)); + if (!expr) + return NULL; + expr->array_ref.element = element; + expr->array_ref.field = field; + expr->array_ref.entry = entry; + return expr; +} + +ExprDef * +ExprCreateAction(xkb_atom_t name, ExprDef *args) +{ + ExprDef *expr = ExprCreate(EXPR_ACTION_DECL, EXPR_TYPE_UNKNOWN, sizeof(ExprAction)); + if (!expr) + return NULL; + expr->action.name = name; + expr->action.args = args; + return expr; +} + +ExprDef * +ExprCreateActionList(ExprDef *actions) +{ + ExprDef *expr = ExprCreate(EXPR_ACTION_LIST, EXPR_TYPE_ACTIONS, sizeof(ExprActionList)); + if (!expr) + return NULL; + expr->actions.actions = actions; + return expr; +} + +ExprDef * +ExprCreateKeysymList(xkb_keysym_t sym) +{ + ExprDef *expr = ExprCreate(EXPR_KEYSYM_LIST, EXPR_TYPE_SYMBOLS, sizeof(ExprKeysymList)); + if (!expr) + return NULL; + + darray_init(expr->keysym_list.syms); + darray_init(expr->keysym_list.symsMapIndex); + darray_init(expr->keysym_list.symsNumEntries); + + darray_append(expr->keysym_list.syms, sym); + darray_append(expr->keysym_list.symsMapIndex, 0); + darray_append(expr->keysym_list.symsNumEntries, 1); + + return expr; +} + +ExprDef * +ExprCreateMultiKeysymList(ExprDef *expr) +{ + unsigned nLevels = darray_size(expr->keysym_list.symsMapIndex); + + darray_resize(expr->keysym_list.symsMapIndex, 1); + darray_resize(expr->keysym_list.symsNumEntries, 1); + darray_item(expr->keysym_list.symsMapIndex, 0) = 0; + darray_item(expr->keysym_list.symsNumEntries, 0) = nLevels; + + return expr; +} + +ExprDef * +ExprAppendKeysymList(ExprDef *expr, xkb_keysym_t sym) +{ + unsigned nSyms = darray_size(expr->keysym_list.syms); + + darray_append(expr->keysym_list.symsMapIndex, nSyms); + darray_append(expr->keysym_list.symsNumEntries, 1); + darray_append(expr->keysym_list.syms, sym); + + return expr; +} + +ExprDef * +ExprAppendMultiKeysymList(ExprDef *expr, ExprDef *append) +{ + unsigned nSyms = darray_size(expr->keysym_list.syms); + unsigned numEntries = darray_size(append->keysym_list.syms); + + darray_append(expr->keysym_list.symsMapIndex, nSyms); + darray_append(expr->keysym_list.symsNumEntries, numEntries); + darray_concat(expr->keysym_list.syms, append->keysym_list.syms); + + FreeStmt((ParseCommon *) append); + + return expr; +} + +KeycodeDef * +KeycodeCreate(xkb_atom_t name, int64_t value) +{ + KeycodeDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_KEYCODE; + def->common.next = NULL; + def->name = name; + def->value = value; + + return def; +} + +KeyAliasDef * +KeyAliasCreate(xkb_atom_t alias, xkb_atom_t real) +{ + KeyAliasDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_ALIAS; + def->common.next = NULL; + def->alias = alias; + def->real = real; + + return def; +} + +VModDef * +VModCreate(xkb_atom_t name, ExprDef *value) +{ + VModDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_VMOD; + def->common.next = NULL; + def->name = name; + def->value = value; + + return def; +} + +VarDef * +VarCreate(ExprDef *name, ExprDef *value) +{ + VarDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_VAR; + def->common.next = NULL; + def->name = name; + def->value = value; + + return def; +} + +VarDef * +BoolVarCreate(xkb_atom_t ident, bool set) +{ + ExprDef *name, *value; + VarDef *def; + if (!(name = ExprCreateIdent(ident))) { + return NULL; + } + if (!(value = ExprCreateBoolean(set))) { + FreeStmt((ParseCommon *) name); + return NULL; + } + if (!(def = VarCreate(name, value))) { + FreeStmt((ParseCommon *) name); + FreeStmt((ParseCommon *) value); + return NULL; + } + return def; +} + +InterpDef * +InterpCreate(xkb_keysym_t sym, ExprDef *match) +{ + InterpDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_INTERP; + def->common.next = NULL; + def->sym = sym; + def->match = match; + def->def = NULL; + + return def; +} + +KeyTypeDef * +KeyTypeCreate(xkb_atom_t name, VarDef *body) +{ + KeyTypeDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_TYPE; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->name = name; + def->body = body; + + return def; +} + +SymbolsDef * +SymbolsCreate(xkb_atom_t keyName, VarDef *symbols) +{ + SymbolsDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_SYMBOLS; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->keyName = keyName; + def->symbols = symbols; + + return def; +} + +GroupCompatDef * +GroupCompatCreate(unsigned group, ExprDef *val) +{ + GroupCompatDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_GROUP_COMPAT; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->group = group; + def->def = val; + + return def; +} + +ModMapDef * +ModMapCreate(xkb_atom_t modifier, ExprDef *keys) +{ + ModMapDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_MODMAP; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->modifier = modifier; + def->keys = keys; + + return def; +} + +LedMapDef * +LedMapCreate(xkb_atom_t name, VarDef *body) +{ + LedMapDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_LED_MAP; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->name = name; + def->body = body; + + return def; +} + +LedNameDef * +LedNameCreate(unsigned ndx, ExprDef *name, bool virtual) +{ + LedNameDef *def = malloc(sizeof(*def)); + if (!def) + return NULL; + + def->common.type = STMT_LED_NAME; + def->common.next = NULL; + def->merge = MERGE_DEFAULT; + def->ndx = ndx; + def->name = name; + def->virtual = virtual; + + return def; +} + +static void +FreeInclude(IncludeStmt *incl); + +IncludeStmt * +IncludeCreate(struct xkb_context *ctx, char *str, enum merge_mode merge) +{ + IncludeStmt *incl, *first; + char *file, *map, *stmt, *tmp, *extra_data; + char nextop; + + incl = first = NULL; + file = map = NULL; + tmp = str; + stmt = strdup_safe(str); + while (tmp && *tmp) + { + if (!ParseIncludeMap(&tmp, &file, &map, &nextop, &extra_data)) + goto err; + + /* + * Given an RMLVO (here layout) like 'us,,fr', the rules parser + * will give out something like 'pc+us+:2+fr:3+inet(evdev)'. + * We should just skip the ':2' in this case and leave it to the + * appropriate section to deal with the empty group. + */ + if (isempty(file)) { + free(file); + free(map); + free(extra_data); + continue; + } + + if (first == NULL) { + first = incl = malloc(sizeof(*first)); + } else { + incl->next_incl = malloc(sizeof(*first)); + incl = incl->next_incl; + } + + if (!incl) + break; + + incl->common.type = STMT_INCLUDE; + incl->common.next = NULL; + incl->merge = merge; + incl->stmt = NULL; + incl->file = file; + incl->map = map; + incl->modifier = extra_data; + incl->next_incl = NULL; + + if (nextop == '|') + merge = MERGE_AUGMENT; + else + merge = MERGE_OVERRIDE; + } + + if (first) + first->stmt = stmt; + else + free(stmt); + + return first; + +err: + log_err(ctx, "Illegal include statement \"%s\"; Ignored\n", stmt); + FreeInclude(first); + free(stmt); + return NULL; +} + +XkbFile * +XkbFileCreate(enum xkb_file_type type, char *name, ParseCommon *defs, + enum xkb_map_flags flags) +{ + XkbFile *file; + + file = calloc(1, sizeof(*file)); + if (!file) + return NULL; + + XkbEscapeMapName(name); + file->file_type = type; + file->name = name ? name : strdup("(unnamed)"); + file->defs = defs; + file->flags = flags; + + return file; +} + +XkbFile * +XkbFileFromComponents(struct xkb_context *ctx, + const struct xkb_component_names *kkctgs) +{ + char *const components[] = { + kkctgs->keycodes, kkctgs->types, + kkctgs->compat, kkctgs->symbols, + }; + enum xkb_file_type type; + IncludeStmt *include = NULL; + XkbFile *file = NULL; + ParseCommon *defs = NULL, *defsLast = NULL; + + for (type = FIRST_KEYMAP_FILE_TYPE; type <= LAST_KEYMAP_FILE_TYPE; type++) { + include = IncludeCreate(ctx, components[type], MERGE_DEFAULT); + if (!include) + goto err; + + file = XkbFileCreate(type, NULL, (ParseCommon *) include, 0); + if (!file) { + FreeInclude(include); + goto err; + } + + if (!defs) + defsLast = defs = &file->common; + else + defsLast = defsLast->next = &file->common; + } + + file = XkbFileCreate(FILE_TYPE_KEYMAP, NULL, defs, 0); + if (!file) + goto err; + + return file; + +err: + FreeXkbFile((XkbFile *) defs); + return NULL; +} + +static void +FreeExpr(ExprDef *expr) +{ + if (!expr) + return; + + switch (expr->expr.op) { + case EXPR_NEGATE: + case EXPR_UNARY_PLUS: + case EXPR_NOT: + case EXPR_INVERT: + FreeStmt((ParseCommon *) expr->unary.child); + break; + + case EXPR_DIVIDE: + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_ASSIGN: + FreeStmt((ParseCommon *) expr->binary.left); + FreeStmt((ParseCommon *) expr->binary.right); + break; + + case EXPR_ACTION_DECL: + FreeStmt((ParseCommon *) expr->action.args); + break; + + case EXPR_ACTION_LIST: + FreeStmt((ParseCommon *) expr->actions.actions); + break; + + case EXPR_ARRAY_REF: + FreeStmt((ParseCommon *) expr->array_ref.entry); + break; + + case EXPR_KEYSYM_LIST: + darray_free(expr->keysym_list.syms); + darray_free(expr->keysym_list.symsMapIndex); + darray_free(expr->keysym_list.symsNumEntries); + break; + + default: + break; + } +} + +static void +FreeInclude(IncludeStmt *incl) +{ + IncludeStmt *next; + + while (incl) + { + next = incl->next_incl; + + free(incl->file); + free(incl->map); + free(incl->modifier); + free(incl->stmt); + + free(incl); + incl = next; + } +} + +void +FreeStmt(ParseCommon *stmt) +{ + ParseCommon *next; + + while (stmt) + { + next = stmt->next; + + switch (stmt->type) { + case STMT_INCLUDE: + FreeInclude((IncludeStmt *) stmt); + /* stmt is already free'd here. */ + stmt = NULL; + break; + case STMT_EXPR: + FreeExpr((ExprDef *) stmt); + break; + case STMT_VAR: + FreeStmt((ParseCommon *) ((VarDef *) stmt)->name); + FreeStmt((ParseCommon *) ((VarDef *) stmt)->value); + break; + case STMT_TYPE: + FreeStmt((ParseCommon *) ((KeyTypeDef *) stmt)->body); + break; + case STMT_INTERP: + FreeStmt((ParseCommon *) ((InterpDef *) stmt)->match); + FreeStmt((ParseCommon *) ((InterpDef *) stmt)->def); + break; + case STMT_VMOD: + FreeStmt((ParseCommon *) ((VModDef *) stmt)->value); + break; + case STMT_SYMBOLS: + FreeStmt((ParseCommon *) ((SymbolsDef *) stmt)->symbols); + break; + case STMT_MODMAP: + FreeStmt((ParseCommon *) ((ModMapDef *) stmt)->keys); + break; + case STMT_GROUP_COMPAT: + FreeStmt((ParseCommon *) ((GroupCompatDef *) stmt)->def); + break; + case STMT_LED_MAP: + FreeStmt((ParseCommon *) ((LedMapDef *) stmt)->body); + break; + case STMT_LED_NAME: + FreeStmt((ParseCommon *) ((LedNameDef *) stmt)->name); + break; + default: + break; + } + + free(stmt); + stmt = next; + } +} + +void +FreeXkbFile(XkbFile *file) +{ + XkbFile *next; + + while (file) + { + next = (XkbFile *) file->common.next; + + switch (file->file_type) { + case FILE_TYPE_KEYMAP: + FreeXkbFile((XkbFile *) file->defs); + break; + + case FILE_TYPE_TYPES: + case FILE_TYPE_COMPAT: + case FILE_TYPE_SYMBOLS: + case FILE_TYPE_KEYCODES: + case FILE_TYPE_GEOMETRY: + FreeStmt(file->defs); + break; + + default: + break; + } + + free(file->name); + free(file); + file = next; + } +} + +static const char *xkb_file_type_strings[_FILE_TYPE_NUM_ENTRIES] = { + [FILE_TYPE_KEYCODES] = "xkb_keycodes", + [FILE_TYPE_TYPES] = "xkb_types", + [FILE_TYPE_COMPAT] = "xkb_compatibility", + [FILE_TYPE_SYMBOLS] = "xkb_symbols", + [FILE_TYPE_GEOMETRY] = "xkb_geometry", + [FILE_TYPE_KEYMAP] = "xkb_keymap", + [FILE_TYPE_RULES] = "rules", +}; + +const char * +xkb_file_type_to_string(enum xkb_file_type type) +{ + if (type >= _FILE_TYPE_NUM_ENTRIES) + return "unknown"; + return xkb_file_type_strings[type]; +} + +static const char *stmt_type_strings[_STMT_NUM_VALUES] = { + [STMT_UNKNOWN] = "unknown statement", + [STMT_INCLUDE] = "include statement", + [STMT_KEYCODE] = "key name definition", + [STMT_ALIAS] = "key alias definition", + [STMT_EXPR] = "expression", + [STMT_VAR] = "variable definition", + [STMT_TYPE] = "key type definition", + [STMT_INTERP] = "symbol interpretation definition", + [STMT_VMOD] = "virtual modifiers definition", + [STMT_SYMBOLS] = "key symbols definition", + [STMT_MODMAP] = "modifier map declaration", + [STMT_GROUP_COMPAT] = "group declaration", + [STMT_LED_MAP] = "indicator map declaration", + [STMT_LED_NAME] = "indicator name declaration", +}; + +const char * +stmt_type_to_string(enum stmt_type type) +{ + if (type >= _STMT_NUM_VALUES) + return NULL; + return stmt_type_strings[type]; +} + +static const char *expr_op_type_strings[_EXPR_NUM_VALUES] = { + [EXPR_VALUE] = "literal", + [EXPR_IDENT] = "identifier", + [EXPR_ACTION_DECL] = "action declaration", + [EXPR_FIELD_REF] = "field reference", + [EXPR_ARRAY_REF] = "array reference", + [EXPR_KEYSYM_LIST] = "list of keysyms", + [EXPR_ACTION_LIST] = "list of actions", + [EXPR_ADD] = "addition", + [EXPR_SUBTRACT] = "subtraction", + [EXPR_MULTIPLY] = "multiplication", + [EXPR_DIVIDE] = "division", + [EXPR_ASSIGN] = "assignment", + [EXPR_NOT] = "logical negation", + [EXPR_NEGATE] = "arithmetic negation", + [EXPR_INVERT] = "bitwise inversion", + [EXPR_UNARY_PLUS] = "unary plus", +}; + +const char * +expr_op_type_to_string(enum expr_op_type type) +{ + if (type >= _EXPR_NUM_VALUES) + return NULL; + return expr_op_type_strings[type]; +} + +static const char *expr_value_type_strings[_EXPR_TYPE_NUM_VALUES] = { + [EXPR_TYPE_UNKNOWN] = "unknown", + [EXPR_TYPE_BOOLEAN] = "boolean", + [EXPR_TYPE_INT] = "int", + [EXPR_TYPE_FLOAT] = "float", + [EXPR_TYPE_STRING] = "string", + [EXPR_TYPE_ACTION] = "action", + [EXPR_TYPE_ACTIONS] = "actions", + [EXPR_TYPE_KEYNAME] = "keyname", + [EXPR_TYPE_SYMBOLS] = "symbols", +}; + +const char * +expr_value_type_to_string(enum expr_value_type type) +{ + if (type >= _EXPR_TYPE_NUM_VALUES) + return NULL; + return expr_value_type_strings[type]; +} diff --git a/src/xkbcomp/ast-build.h b/src/xkbcomp/ast-build.h new file mode 100644 index 0000000..3afb080 --- /dev/null +++ b/src/xkbcomp/ast-build.h @@ -0,0 +1,125 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_AST_BUILD_H +#define XKBCOMP_AST_BUILD_H + +ExprDef * +ExprCreateString(xkb_atom_t str); + +ExprDef * +ExprCreateInteger(int ival); + +ExprDef * +ExprCreateFloat(void); + +ExprDef * +ExprCreateBoolean(bool set); + +ExprDef * +ExprCreateKeyName(xkb_atom_t key_name); + +ExprDef * +ExprCreateIdent(xkb_atom_t ident); + +ExprDef * +ExprCreateUnary(enum expr_op_type op, enum expr_value_type type, + ExprDef *child); + +ExprDef * +ExprCreateBinary(enum expr_op_type op, ExprDef *left, ExprDef *right); + +ExprDef * +ExprCreateFieldRef(xkb_atom_t element, xkb_atom_t field); + +ExprDef * +ExprCreateArrayRef(xkb_atom_t element, xkb_atom_t field, ExprDef *entry); + +ExprDef * +ExprCreateAction(xkb_atom_t name, ExprDef *args); + +ExprDef * +ExprCreateActionList(ExprDef *actions); + +ExprDef * +ExprCreateMultiKeysymList(ExprDef *list); + +ExprDef * +ExprCreateKeysymList(xkb_keysym_t sym); + +ExprDef * +ExprAppendMultiKeysymList(ExprDef *list, ExprDef *append); + +ExprDef * +ExprAppendKeysymList(ExprDef *list, xkb_keysym_t sym); + +KeycodeDef * +KeycodeCreate(xkb_atom_t name, int64_t value); + +KeyAliasDef * +KeyAliasCreate(xkb_atom_t alias, xkb_atom_t real); + +VModDef * +VModCreate(xkb_atom_t name, ExprDef *value); + +VarDef * +VarCreate(ExprDef *name, ExprDef *value); + +VarDef * +BoolVarCreate(xkb_atom_t ident, bool set); + +InterpDef * +InterpCreate(xkb_keysym_t sym, ExprDef *match); + +KeyTypeDef * +KeyTypeCreate(xkb_atom_t name, VarDef *body); + +SymbolsDef * +SymbolsCreate(xkb_atom_t keyName, VarDef *symbols); + +GroupCompatDef * +GroupCompatCreate(unsigned group, ExprDef *def); + +ModMapDef * +ModMapCreate(xkb_atom_t modifier, ExprDef *keys); + +LedMapDef * +LedMapCreate(xkb_atom_t name, VarDef *body); + +LedNameDef * +LedNameCreate(unsigned ndx, ExprDef *name, bool virtual); + +IncludeStmt * +IncludeCreate(struct xkb_context *ctx, char *str, enum merge_mode merge); + +XkbFile * +XkbFileCreate(enum xkb_file_type type, char *name, ParseCommon *defs, + enum xkb_map_flags flags); + +void +FreeStmt(ParseCommon *stmt); + +#endif diff --git a/src/xkbcomp/ast.h b/src/xkbcomp/ast.h new file mode 100644 index 0000000..ee61106 --- /dev/null +++ b/src/xkbcomp/ast.h @@ -0,0 +1,360 @@ +/************************************************************ + * Copyright (c) 1994 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 © 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. + */ + +#ifndef XKBCOMP_AST_H +#define XKBCOMP_AST_H + +enum xkb_file_type { + /* Component files, by order of compilation. */ + FILE_TYPE_KEYCODES = 0, + FILE_TYPE_TYPES = 1, + FILE_TYPE_COMPAT = 2, + FILE_TYPE_SYMBOLS = 3, + /* Geometry is not compiled any more. */ + FILE_TYPE_GEOMETRY = 4, + + /* A top level file which includes the above files. */ + FILE_TYPE_KEYMAP, + +/* File types which must be found in a keymap file. */ +#define FIRST_KEYMAP_FILE_TYPE FILE_TYPE_KEYCODES +#define LAST_KEYMAP_FILE_TYPE FILE_TYPE_SYMBOLS + + /* This one doesn't mix with the others, but useful here as well. */ + FILE_TYPE_RULES, + + _FILE_TYPE_NUM_ENTRIES +}; + +enum stmt_type { + STMT_UNKNOWN = 0, + STMT_INCLUDE, + STMT_KEYCODE, + STMT_ALIAS, + STMT_EXPR, + STMT_VAR, + STMT_TYPE, + STMT_INTERP, + STMT_VMOD, + STMT_SYMBOLS, + STMT_MODMAP, + STMT_GROUP_COMPAT, + STMT_LED_MAP, + STMT_LED_NAME, + + _STMT_NUM_VALUES +}; + +enum expr_value_type { + EXPR_TYPE_UNKNOWN = 0, + EXPR_TYPE_BOOLEAN, + EXPR_TYPE_INT, + EXPR_TYPE_FLOAT, + EXPR_TYPE_STRING, + EXPR_TYPE_ACTION, + EXPR_TYPE_ACTIONS, + EXPR_TYPE_KEYNAME, + EXPR_TYPE_SYMBOLS, + + _EXPR_TYPE_NUM_VALUES +}; + +enum expr_op_type { + EXPR_VALUE, + EXPR_IDENT, + EXPR_ACTION_DECL, + EXPR_FIELD_REF, + EXPR_ARRAY_REF, + EXPR_KEYSYM_LIST, + EXPR_ACTION_LIST, + EXPR_ADD, + EXPR_SUBTRACT, + EXPR_MULTIPLY, + EXPR_DIVIDE, + EXPR_ASSIGN, + EXPR_NOT, + EXPR_NEGATE, + EXPR_INVERT, + EXPR_UNARY_PLUS, + + _EXPR_NUM_VALUES +}; + +enum merge_mode { + MERGE_DEFAULT, + MERGE_AUGMENT, + MERGE_OVERRIDE, + MERGE_REPLACE, +}; + +const char * +xkb_file_type_to_string(enum xkb_file_type type); + +const char * +stmt_type_to_string(enum stmt_type type); + +const char * +expr_op_type_to_string(enum expr_op_type type); + +const char * +expr_value_type_to_string(enum expr_value_type type); + +typedef struct _ParseCommon { + struct _ParseCommon *next; + enum stmt_type type; +} ParseCommon; + +typedef struct _IncludeStmt { + ParseCommon common; + enum merge_mode merge; + char *stmt; + char *file; + char *map; + char *modifier; + struct _IncludeStmt *next_incl; +} IncludeStmt; + +typedef struct { + ParseCommon common; + enum expr_op_type op; + enum expr_value_type value_type; +} ExprCommon; + +typedef union ExprDef ExprDef; + +typedef struct { + ExprCommon expr; + xkb_atom_t ident; +} ExprIdent; + +typedef struct { + ExprCommon expr; + xkb_atom_t str; +} ExprString; + +typedef struct { + ExprCommon expr; + bool set; +} ExprBoolean; + +typedef struct { + ExprCommon expr; + int ival; +} ExprInteger; + +typedef struct { + ExprCommon expr; + /* We don't support floats, but we still represnt them in the AST, in + * order to provide proper error messages. */ +} ExprFloat; + +typedef struct { + ExprCommon expr; + xkb_atom_t key_name; +} ExprKeyName; + +typedef struct { + ExprCommon expr; + ExprDef *left; + ExprDef *right; +} ExprBinary; + +typedef struct { + ExprCommon expr; + ExprDef *child; +} ExprUnary; + +typedef struct { + ExprCommon expr; + xkb_atom_t element; + xkb_atom_t field; +} ExprFieldRef; + +typedef struct { + ExprCommon expr; + xkb_atom_t element; + xkb_atom_t field; + ExprDef *entry; +} ExprArrayRef; + +typedef struct { + ExprCommon expr; + xkb_atom_t name; + ExprDef *args; +} ExprAction; + +typedef struct { + ExprCommon expr; + ExprDef *actions; +} ExprActionList; + +typedef struct { + ExprCommon expr; + darray(xkb_keysym_t) syms; + darray(unsigned int) symsMapIndex; + darray(unsigned int) symsNumEntries; +} ExprKeysymList; + +union ExprDef { + ParseCommon common; + /* Maybe someday we can use C11 anonymous struct for ExprCommon here. */ + ExprCommon expr; + ExprIdent ident; + ExprString string; + ExprBoolean boolean; + ExprInteger integer; + ExprKeyName key_name; + ExprBinary binary; + ExprUnary unary; + ExprFieldRef field_ref; + ExprArrayRef array_ref; + ExprAction action; + ExprActionList actions; + ExprKeysymList keysym_list; +}; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + ExprDef *name; + ExprDef *value; +} VarDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + ExprDef *value; +} VModDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + int64_t value; +} KeycodeDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t alias; + xkb_atom_t real; +} KeyAliasDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + VarDef *body; +} KeyTypeDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t keyName; + VarDef *symbols; +} SymbolsDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t modifier; + ExprDef *keys; +} ModMapDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + unsigned group; + ExprDef *def; +} GroupCompatDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_keysym_t sym; + ExprDef *match; + VarDef *def; +} InterpDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + unsigned ndx; + ExprDef *name; + bool virtual; +} LedNameDef; + +typedef struct { + ParseCommon common; + enum merge_mode merge; + xkb_atom_t name; + VarDef *body; +} LedMapDef; + +enum xkb_map_flags { + MAP_IS_DEFAULT = (1 << 0), + MAP_IS_PARTIAL = (1 << 1), + MAP_IS_HIDDEN = (1 << 2), + MAP_HAS_ALPHANUMERIC = (1 << 3), + MAP_HAS_MODIFIER = (1 << 4), + MAP_HAS_KEYPAD = (1 << 5), + MAP_HAS_FN = (1 << 6), + MAP_IS_ALTGR = (1 << 7), +}; + +typedef struct { + ParseCommon common; + enum xkb_file_type file_type; + char *name; + ParseCommon *defs; + enum xkb_map_flags flags; +} XkbFile; + +#endif diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c new file mode 100644 index 0000000..26b2bb7 --- /dev/null +++ b/src/xkbcomp/compat.c @@ -0,0 +1,934 @@ +/************************************************************ + * Copyright (c) 1994 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 © 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 "text.h" +#include "expr.h" +#include "action.h" +#include "vmod.h" +#include "include.h" + +enum si_field { + SI_FIELD_VIRTUAL_MOD = (1 << 0), + SI_FIELD_ACTION = (1 << 1), + SI_FIELD_AUTO_REPEAT = (1 << 2), + SI_FIELD_LEVEL_ONE_ONLY = (1 << 3), +}; + +typedef struct { + enum si_field defined; + enum merge_mode merge; + + struct xkb_sym_interpret interp; +} SymInterpInfo; + +enum led_field { + LED_FIELD_MODS = (1 << 0), + LED_FIELD_GROUPS = (1 << 1), + LED_FIELD_CTRLS = (1 << 2), +}; + +typedef struct { + enum led_field defined; + enum merge_mode merge; + + struct xkb_led led; +} LedInfo; + +typedef struct { + char *name; + int errorCount; + SymInterpInfo default_interp; + darray(SymInterpInfo) interps; + LedInfo default_led; + LedInfo leds[XKB_MAX_LEDS]; + unsigned int num_leds; + ActionsInfo *actions; + struct xkb_mod_set mods; + + struct xkb_context *ctx; +} CompatInfo; + +static const char * +siText(SymInterpInfo *si, CompatInfo *info) +{ + char *buf = xkb_context_get_buffer(info->ctx, 128); + + if (si == &info->default_interp) + return "default"; + + snprintf(buf, 128, "%s+%s(%s)", + KeysymText(info->ctx, si->interp.sym), + SIMatchText(si->interp.match), + ModMaskText(info->ctx, &info->mods, si->interp.mods)); + + return buf; +} + +static inline bool +ReportSINotArray(CompatInfo *info, SymInterpInfo *si, const char *field) +{ + return ReportNotArray(info->ctx, "symbol interpretation", field, + siText(si, info)); +} + +static inline bool +ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field, + const char *wanted) +{ + return ReportBadType(info->ctx, "symbol interpretation", field, + siText(si, info), wanted); +} + +static inline bool +ReportLedBadType(CompatInfo *info, LedInfo *ledi, const char *field, + const char *wanted) +{ + return ReportBadType(info->ctx, "indicator map", field, + xkb_atom_text(info->ctx, ledi->led.name), + wanted); +} + +static inline bool +ReportLedNotArray(CompatInfo *info, LedInfo *ledi, const char *field) +{ + return ReportNotArray(info->ctx, "indicator map", field, + xkb_atom_text(info->ctx, ledi->led.name)); +} + +static void +InitCompatInfo(CompatInfo *info, struct xkb_context *ctx, + ActionsInfo *actions, const struct xkb_mod_set *mods) +{ + memset(info, 0, sizeof(*info)); + info->ctx = ctx; + info->actions = actions; + info->mods = *mods; + info->default_interp.merge = MERGE_OVERRIDE; + info->default_interp.interp.virtual_mod = XKB_MOD_INVALID; + info->default_led.merge = MERGE_OVERRIDE; +} + +static void +ClearCompatInfo(CompatInfo *info) +{ + free(info->name); + darray_free(info->interps); +} + +static SymInterpInfo * +FindMatchingInterp(CompatInfo *info, SymInterpInfo *new) +{ + SymInterpInfo *old; + + darray_foreach(old, info->interps) + if (old->interp.sym == new->interp.sym && + old->interp.mods == new->interp.mods && + old->interp.match == new->interp.match) + return old; + + return NULL; +} + +static bool +UseNewInterpField(enum si_field field, SymInterpInfo *old, SymInterpInfo *new, + bool report, enum si_field *collide) +{ + if (!(old->defined & field)) + return true; + + if (new->defined & field) { + if (report) + *collide |= field; + + if (new->merge != MERGE_AUGMENT) + return true; + } + + return false; +} + +static bool +AddInterp(CompatInfo *info, SymInterpInfo *new, bool same_file) +{ + SymInterpInfo *old = FindMatchingInterp(info, new); + if (old) { + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + enum si_field collide = 0; + + if (new->merge == MERGE_REPLACE) { + if (report) + log_warn(info->ctx, + "Multiple definitions for \"%s\"; " + "Earlier interpretation ignored\n", + siText(new, info)); + *old = *new; + return true; + } + + if (UseNewInterpField(SI_FIELD_VIRTUAL_MOD, old, new, report, + &collide)) { + old->interp.virtual_mod = new->interp.virtual_mod; + old->defined |= SI_FIELD_VIRTUAL_MOD; + } + if (UseNewInterpField(SI_FIELD_ACTION, old, new, report, + &collide)) { + old->interp.action = new->interp.action; + old->defined |= SI_FIELD_ACTION; + } + if (UseNewInterpField(SI_FIELD_AUTO_REPEAT, old, new, report, + &collide)) { + old->interp.repeat = new->interp.repeat; + old->defined |= SI_FIELD_AUTO_REPEAT; + } + if (UseNewInterpField(SI_FIELD_LEVEL_ONE_ONLY, old, new, report, + &collide)) { + old->interp.level_one_only = new->interp.level_one_only; + old->defined |= SI_FIELD_LEVEL_ONE_ONLY; + } + + if (collide) { + log_warn(info->ctx, + "Multiple interpretations of \"%s\"; " + "Using %s definition for duplicate fields\n", + siText(new, info), + (new->merge != MERGE_AUGMENT ? "last" : "first")); + } + + return true; + } + + darray_append(info->interps, *new); + return true; +} + +/***====================================================================***/ + +static bool +ResolveStateAndPredicate(ExprDef *expr, enum xkb_match_operation *pred_rtrn, + xkb_mod_mask_t *mods_rtrn, CompatInfo *info) +{ + if (expr == NULL) { + *pred_rtrn = MATCH_ANY_OR_NONE; + *mods_rtrn = MOD_REAL_MASK_ALL; + return true; + } + + *pred_rtrn = MATCH_EXACTLY; + if (expr->expr.op == EXPR_ACTION_DECL) { + const char *pred_txt = xkb_atom_text(info->ctx, expr->action.name); + if (!LookupString(symInterpretMatchMaskNames, pred_txt, pred_rtrn) || + !expr->action.args || expr->action.args->common.next) { + log_err(info->ctx, + "Illegal modifier predicate \"%s\"; Ignored\n", pred_txt); + return false; + } + expr = expr->action.args; + } + else if (expr->expr.op == EXPR_IDENT) { + const char *pred_txt = xkb_atom_text(info->ctx, expr->ident.ident); + if (pred_txt && istreq(pred_txt, "any")) { + *pred_rtrn = MATCH_ANY; + *mods_rtrn = MOD_REAL_MASK_ALL; + return true; + } + } + + return ExprResolveModMask(info->ctx, expr, MOD_REAL, &info->mods, + mods_rtrn); +} + +/***====================================================================***/ + +static bool +UseNewLEDField(enum led_field field, LedInfo *old, LedInfo *new, + bool report, enum led_field *collide) +{ + if (!(old->defined & field)) + return true; + + if (new->defined & field) { + if (report) + *collide |= field; + + if (new->merge != MERGE_AUGMENT) + return true; + } + + return false; +} + +static bool +AddLedMap(CompatInfo *info, LedInfo *new, bool same_file) +{ + enum led_field collide; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + + for (xkb_led_index_t i = 0; i < info->num_leds; i++) { + LedInfo *old = &info->leds[i]; + + if (old->led.name != new->led.name) + continue; + + if (old->led.mods.mods == new->led.mods.mods && + old->led.groups == new->led.groups && + old->led.ctrls == new->led.ctrls && + old->led.which_mods == new->led.which_mods && + old->led.which_groups == new->led.which_groups) { + old->defined |= new->defined; + return true; + } + + if (new->merge == MERGE_REPLACE) { + if (report) + log_warn(info->ctx, + "Map for indicator %s redefined; " + "Earlier definition ignored\n", + xkb_atom_text(info->ctx, old->led.name)); + *old = *new; + return true; + } + + collide = 0; + if (UseNewLEDField(LED_FIELD_MODS, old, new, report, &collide)) { + old->led.which_mods = new->led.which_mods; + old->led.mods = new->led.mods; + old->defined |= LED_FIELD_MODS; + } + if (UseNewLEDField(LED_FIELD_GROUPS, old, new, report, &collide)) { + old->led.which_groups = new->led.which_groups; + old->led.groups = new->led.groups; + old->defined |= LED_FIELD_GROUPS; + } + if (UseNewLEDField(LED_FIELD_CTRLS, old, new, report, &collide)) { + old->led.ctrls = new->led.ctrls; + old->defined |= LED_FIELD_CTRLS; + } + + if (collide) { + log_warn(info->ctx, + "Map for indicator %s redefined; " + "Using %s definition for duplicate fields\n", + xkb_atom_text(info->ctx, old->led.name), + (new->merge == MERGE_AUGMENT ? "first" : "last")); + } + + return true; + } + + if (info->num_leds >= XKB_MAX_LEDS) { + log_err(info->ctx, + "Too many LEDs defined (maximum %d)\n", + XKB_MAX_LEDS); + return false; + } + info->leds[info->num_leds++] = *new; + return true; +} + +static void +MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from, + enum merge_mode merge) +{ + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + into->mods = from->mods; + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + if (darray_empty(into->interps)) { + into->interps = from->interps; + darray_init(from->interps); + } + else { + SymInterpInfo *si; + darray_foreach(si, from->interps) { + si->merge = (merge == MERGE_DEFAULT ? si->merge : merge); + if (!AddInterp(into, si, false)) + into->errorCount++; + } + } + + if (into->num_leds == 0) { + memcpy(into->leds, from->leds, sizeof(*from->leds) * from->num_leds); + into->num_leds = from->num_leds; + from->num_leds = 0; + } + else { + for (xkb_led_index_t i = 0; i < from->num_leds; i++) { + LedInfo *ledi = &from->leds[i]; + ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge); + if (!AddLedMap(into, ledi, false)) + into->errorCount++; + } + } +} + +static void +HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *include) +{ + CompatInfo included; + + InitCompatInfo(&included, info->ctx, info->actions, &info->mods); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + CompatInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_COMPAT); + if (!file) { + info->errorCount += 10; + ClearCompatInfo(&included); + return false; + } + + InitCompatInfo(&next_incl, info->ctx, info->actions, &included.mods); + next_incl.default_interp = info->default_interp; + next_incl.default_interp.merge = stmt->merge; + next_incl.default_led = info->default_led; + next_incl.default_led.merge = stmt->merge; + + HandleCompatMapFile(&next_incl, file, MERGE_OVERRIDE); + + MergeIncludedCompatMaps(&included, &next_incl, stmt->merge); + + ClearCompatInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedCompatMaps(info, &included, include->merge); + ClearCompatInfo(&included); + + return (info->errorCount == 0); +} + +static bool +SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field, + ExprDef *arrayNdx, ExprDef *value) +{ + xkb_mod_index_t ndx; + + if (istreq(field, "action")) { + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!HandleActionDef(info->ctx, info->actions, &info->mods, + value, &si->interp.action)) + return false; + + si->defined |= SI_FIELD_ACTION; + } + else if (istreq(field, "virtualmodifier") || + istreq(field, "virtualmod")) { + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!ExprResolveMod(info->ctx, value, MOD_VIRT, &info->mods, &ndx)) + return ReportSIBadType(info, si, field, "virtual modifier"); + + si->interp.virtual_mod = ndx; + si->defined |= SI_FIELD_VIRTUAL_MOD; + } + else if (istreq(field, "repeat")) { + bool set; + + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!ExprResolveBoolean(info->ctx, value, &set)) + return ReportSIBadType(info, si, field, "boolean"); + + si->interp.repeat = set; + + si->defined |= SI_FIELD_AUTO_REPEAT; + } + else if (istreq(field, "locking")) { + log_dbg(info->ctx, + "The \"locking\" field in symbol interpretation is unsupported; " + "Ignored\n"); + } + else if (istreq(field, "usemodmap") || + istreq(field, "usemodmapmods")) { + unsigned int val; + + if (arrayNdx) + return ReportSINotArray(info, si, field); + + if (!ExprResolveEnum(info->ctx, value, &val, useModMapValueNames)) + return ReportSIBadType(info, si, field, "level specification"); + + si->interp.level_one_only = val; + si->defined |= SI_FIELD_LEVEL_ONE_ONLY; + } + else { + return ReportBadField(info->ctx, "symbol interpretation", field, + siText(si, info)); + } + + return true; +} + +static bool +SetLedMapField(CompatInfo *info, LedInfo *ledi, const char *field, + ExprDef *arrayNdx, ExprDef *value) +{ + bool ok = true; + + if (istreq(field, "modifiers") || istreq(field, "mods")) { + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveModMask(info->ctx, value, MOD_BOTH, + &info->mods, &ledi->led.mods.mods)) + return ReportLedBadType(info, ledi, field, "modifier mask"); + + ledi->defined |= LED_FIELD_MODS; + } + else if (istreq(field, "groups")) { + unsigned int mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, groupMaskNames)) + return ReportLedBadType(info, ledi, field, "group mask"); + + ledi->led.groups = mask; + ledi->defined |= LED_FIELD_GROUPS; + } + else if (istreq(field, "controls") || istreq(field, "ctrls")) { + unsigned int mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, ctrlMaskNames)) + return ReportLedBadType(info, ledi, field, "controls mask"); + + ledi->led.ctrls = mask; + ledi->defined |= LED_FIELD_CTRLS; + } + else if (istreq(field, "allowexplicit")) { + log_dbg(info->ctx, + "The \"allowExplicit\" field in indicator statements is unsupported; " + "Ignored\n"); + } + else if (istreq(field, "whichmodstate") || + istreq(field, "whichmodifierstate")) { + unsigned int mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, + modComponentMaskNames)) + return ReportLedBadType(info, ledi, field, + "mask of modifier state components"); + + ledi->led.which_mods = mask; + } + else if (istreq(field, "whichgroupstate")) { + unsigned mask; + + if (arrayNdx) + return ReportLedNotArray(info, ledi, field); + + if (!ExprResolveMask(info->ctx, value, &mask, + groupComponentMaskNames)) + return ReportLedBadType(info, ledi, field, + "mask of group state components"); + + ledi->led.which_groups = mask; + } + else if (istreq(field, "driveskbd") || + istreq(field, "driveskeyboard") || + istreq(field, "leddriveskbd") || + istreq(field, "leddriveskeyboard") || + istreq(field, "indicatordriveskbd") || + istreq(field, "indicatordriveskeyboard")) { + log_dbg(info->ctx, + "The \"%s\" field in indicator statements is unsupported; " + "Ignored\n", field); + } + else if (istreq(field, "index")) { + /* Users should see this, it might cause unexpected behavior. */ + log_err(info->ctx, + "The \"index\" field in indicator statements is unsupported; " + "Ignored\n"); + } + else { + log_err(info->ctx, + "Unknown field %s in map for %s indicator; " + "Definition ignored\n", + field, xkb_atom_text(info->ctx, ledi->led.name)); + ok = false; + } + + return ok; +} + +static bool +HandleGlobalVar(CompatInfo *info, VarDef *stmt) +{ + const char *elem, *field; + ExprDef *ndx; + bool ret; + + if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &ndx)) + ret = false; + else if (elem && istreq(elem, "interpret")) + ret = SetInterpField(info, &info->default_interp, field, ndx, + stmt->value); + else if (elem && istreq(elem, "indicator")) + ret = SetLedMapField(info, &info->default_led, field, ndx, + stmt->value); + else + ret = SetActionField(info->ctx, info->actions, &info->mods, + elem, field, ndx, stmt->value); + return ret; +} + +static bool +HandleInterpBody(CompatInfo *info, VarDef *def, SymInterpInfo *si) +{ + bool ok = true; + const char *elem, *field; + ExprDef *arrayNdx; + + for (; def; def = (VarDef *) def->common.next) { + if (def->name && def->name->expr.op == EXPR_FIELD_REF) { + log_err(info->ctx, + "Cannot set a global default value from within an interpret statement; " + "Move statements to the global file scope\n"); + ok = false; + continue; + } + + ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, &arrayNdx); + if (!ok) + continue; + + ok = SetInterpField(info, si, field, arrayNdx, def->value); + } + + return ok; +} + +static bool +HandleInterpDef(CompatInfo *info, InterpDef *def, enum merge_mode merge) +{ + enum xkb_match_operation pred; + xkb_mod_mask_t mods; + SymInterpInfo si; + + if (!ResolveStateAndPredicate(def->match, &pred, &mods, info)) { + log_err(info->ctx, + "Couldn't determine matching modifiers; " + "Symbol interpretation ignored\n"); + return false; + } + + si = info->default_interp; + si.merge = merge = (def->merge == MERGE_DEFAULT ? merge : def->merge); + si.interp.sym = def->sym; + si.interp.match = pred; + si.interp.mods = mods; + + if (!HandleInterpBody(info, def->def, &si)) { + info->errorCount++; + return false; + } + + if (!AddInterp(info, &si, true)) { + info->errorCount++; + return false; + } + + return true; +} + +static bool +HandleLedMapDef(CompatInfo *info, LedMapDef *def, enum merge_mode merge) +{ + LedInfo ledi; + VarDef *var; + bool ok; + + if (def->merge != MERGE_DEFAULT) + merge = def->merge; + + ledi = info->default_led; + ledi.merge = merge; + ledi.led.name = def->name; + + ok = true; + for (var = def->body; var != NULL; var = (VarDef *) var->common.next) { + const char *elem, *field; + ExprDef *arrayNdx; + if (!ExprResolveLhs(info->ctx, var->name, &elem, &field, &arrayNdx)) { + ok = false; + continue; + } + + if (elem) { + log_err(info->ctx, + "Cannot set defaults for \"%s\" element in indicator map; " + "Assignment to %s.%s ignored\n", elem, elem, field); + ok = false; + } + else { + ok = SetLedMapField(info, &ledi, field, arrayNdx, var->value) && ok; + } + } + + if (ok) + return AddLedMap(info, &ledi, true); + + return false; +} + +static void +HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge); + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt); + break; + case STMT_INTERP: + ok = HandleInterpDef(info, (InterpDef *) stmt, merge); + break; + case STMT_GROUP_COMPAT: + log_dbg(info->ctx, + "The \"group\" statement in compat is unsupported; " + "Ignored\n"); + ok = true; + break; + case STMT_LED_MAP: + ok = HandleLedMapDef(info, (LedMapDef *) stmt, merge); + break; + case STMT_VAR: + ok = HandleGlobalVar(info, (VarDef *) stmt); + break; + case STMT_VMOD: + ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge); + break; + default: + log_err(info->ctx, + "Compat files may not include other types; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, + "Abandoning compatibility map \"%s\"\n", file->name); + break; + } + } +} + +/* Temporary struct for CopyInterps. */ +struct collect { + darray(struct xkb_sym_interpret) sym_interprets; +}; + +static void +CopyInterps(CompatInfo *info, bool needSymbol, enum xkb_match_operation pred, + struct collect *collect) +{ + SymInterpInfo *si; + + darray_foreach(si, info->interps) + if (si->interp.match == pred && + (si->interp.sym != XKB_KEY_NoSymbol) == needSymbol) + darray_append(collect->sym_interprets, si->interp); +} + +static void +CopyLedMapDefsToKeymap(struct xkb_keymap *keymap, CompatInfo *info) +{ + for (xkb_led_index_t idx = 0; idx < info->num_leds; idx++) { + LedInfo *ledi = &info->leds[idx]; + xkb_led_index_t i; + struct xkb_led *led; + + /* + * Find the LED with the given name, if it was already declared + * in keycodes. + */ + xkb_leds_enumerate(i, led, keymap) + if (led->name == ledi->led.name) + break; + + /* Not previously declared; create it with next free index. */ + if (i >= keymap->num_leds) { + log_dbg(keymap->ctx, + "Indicator name \"%s\" was not declared in the keycodes section; " + "Adding new indicator\n", + xkb_atom_text(keymap->ctx, ledi->led.name)); + + xkb_leds_enumerate(i, led, keymap) + if (led->name == XKB_ATOM_NONE) + break; + + if (i >= keymap->num_leds) { + /* Not place to put it; ignore. */ + if (i >= XKB_MAX_LEDS) { + log_err(keymap->ctx, + "Too many indicators (maximum is %d); " + "Indicator name \"%s\" ignored\n", + XKB_MAX_LEDS, + xkb_atom_text(keymap->ctx, ledi->led.name)); + continue; + } + + /* Add a new LED. */ + led = &keymap->leds[keymap->num_leds++]; + } + } + + *led = ledi->led; + if (led->groups != 0 && led->which_groups == 0) + led->which_groups = XKB_STATE_LAYOUT_EFFECTIVE; + if (led->mods.mods != 0 && led->which_mods == 0) + led->which_mods = XKB_STATE_MODS_EFFECTIVE; + } +} + +static bool +CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info) +{ + keymap->compat_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->compat_section_name); + + keymap->mods = info->mods; + + if (!darray_empty(info->interps)) { + struct collect collect; + darray_init(collect.sym_interprets); + + /* Most specific to least specific. */ + CopyInterps(info, true, MATCH_EXACTLY, &collect); + CopyInterps(info, true, MATCH_ALL, &collect); + CopyInterps(info, true, MATCH_NONE, &collect); + CopyInterps(info, true, MATCH_ANY, &collect); + CopyInterps(info, true, MATCH_ANY_OR_NONE, &collect); + CopyInterps(info, false, MATCH_EXACTLY, &collect); + CopyInterps(info, false, MATCH_ALL, &collect); + CopyInterps(info, false, MATCH_NONE, &collect); + CopyInterps(info, false, MATCH_ANY, &collect); + CopyInterps(info, false, MATCH_ANY_OR_NONE, &collect); + + darray_steal(collect.sym_interprets, + &keymap->sym_interprets, &keymap->num_sym_interprets); + } + + CopyLedMapDefsToKeymap(keymap, info); + + return true; +} + +bool +CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + CompatInfo info; + ActionsInfo *actions; + + actions = NewActionsInfo(); + if (!actions) + return false; + + InitCompatInfo(&info, keymap->ctx, actions, &keymap->mods); + info.default_interp.merge = merge; + info.default_led.merge = merge; + + HandleCompatMapFile(&info, file, merge); + if (info.errorCount != 0) + goto err_info; + + if (!CopyCompatToKeymap(keymap, &info)) + goto err_info; + + ClearCompatInfo(&info); + FreeActionsInfo(actions); + return true; + +err_info: + ClearCompatInfo(&info); + FreeActionsInfo(actions); + return false; +} diff --git a/src/xkbcomp/expr.c b/src/xkbcomp/expr.c new file mode 100644 index 0000000..dbdf734 --- /dev/null +++ b/src/xkbcomp/expr.c @@ -0,0 +1,694 @@ +/************************************************************ + * Copyright (c) 1994 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 "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" + +typedef bool (*IdentLookupFunc)(struct xkb_context *ctx, const void *priv, + xkb_atom_t field, enum expr_value_type type, + unsigned int *val_rtrn); + +bool +ExprResolveLhs(struct xkb_context *ctx, const ExprDef *expr, + const char **elem_rtrn, const char **field_rtrn, + ExprDef **index_rtrn) +{ + switch (expr->expr.op) { + case EXPR_IDENT: + *elem_rtrn = NULL; + *field_rtrn = xkb_atom_text(ctx, expr->ident.ident); + *index_rtrn = NULL; + return (*field_rtrn != NULL); + case EXPR_FIELD_REF: + *elem_rtrn = xkb_atom_text(ctx, expr->field_ref.element); + *field_rtrn = xkb_atom_text(ctx, expr->field_ref.field); + *index_rtrn = NULL; + return (*elem_rtrn != NULL && *field_rtrn != NULL); + case EXPR_ARRAY_REF: + *elem_rtrn = xkb_atom_text(ctx, expr->array_ref.element); + *field_rtrn = xkb_atom_text(ctx, expr->array_ref.field); + *index_rtrn = expr->array_ref.entry; + if (expr->array_ref.element != XKB_ATOM_NONE && *elem_rtrn == NULL) + return false; + if (*field_rtrn == NULL) + return false; + return true; + default: + break; + } + log_wsgo(ctx, "Unexpected operator %d in ResolveLhs\n", expr->expr.op); + return false; +} + +static bool +SimpleLookup(struct xkb_context *ctx, const void *priv, xkb_atom_t field, + enum expr_value_type type, unsigned int *val_rtrn) +{ + const LookupEntry *entry; + const char *str; + + if (!priv || field == XKB_ATOM_NONE || type != EXPR_TYPE_INT) + return false; + + str = xkb_atom_text(ctx, field); + for (entry = priv; entry && entry->name; entry++) { + if (istreq(str, entry->name)) { + *val_rtrn = entry->value; + return true; + } + } + + return false; +} + +/* Data passed in the *priv argument for LookupModMask. */ +typedef struct { + const struct xkb_mod_set *mods; + enum mod_type mod_type; +} LookupModMaskPriv; + +static bool +LookupModMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field, + enum expr_value_type type, xkb_mod_mask_t *val_rtrn) +{ + const char *str; + xkb_mod_index_t ndx; + const LookupModMaskPriv *arg = priv; + const struct xkb_mod_set *mods = arg->mods; + enum mod_type mod_type = arg->mod_type; + + if (type != EXPR_TYPE_INT) + return false; + + str = xkb_atom_text(ctx, field); + if (!str) + return false; + + if (istreq(str, "all")) { + *val_rtrn = MOD_REAL_MASK_ALL; + return true; + } + + if (istreq(str, "none")) { + *val_rtrn = 0; + return true; + } + + ndx = XkbModNameToIndex(mods, field, mod_type); + if (ndx == XKB_MOD_INVALID) + return false; + + *val_rtrn = (1u << ndx); + return true; +} + +bool +ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr, + bool *set_rtrn) +{ + bool ok = false; + const char *ident; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_BOOLEAN) { + log_err(ctx, + "Found constant of type %s where boolean was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + *set_rtrn = expr->boolean.set; + return true; + + case EXPR_IDENT: + ident = xkb_atom_text(ctx, expr->ident.ident); + if (ident) { + if (istreq(ident, "true") || + istreq(ident, "yes") || + istreq(ident, "on")) { + *set_rtrn = true; + return true; + } + else if (istreq(ident, "false") || + istreq(ident, "no") || + istreq(ident, "off")) { + *set_rtrn = false; + return true; + } + } + log_err(ctx, "Identifier \"%s\" of type boolean is unknown\n", ident); + return false; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type boolean is unknown\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_INVERT: + case EXPR_NOT: + ok = ExprResolveBoolean(ctx, expr->unary.child, set_rtrn); + if (ok) + *set_rtrn = !*set_rtrn; + return ok; + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + case EXPR_ASSIGN: + case EXPR_NEGATE: + case EXPR_UNARY_PLUS: + case EXPR_ACTION_DECL: + case EXPR_ACTION_LIST: + case EXPR_KEYSYM_LIST: + log_err(ctx, "%s of boolean values not permitted\n", + expr_op_type_to_string(expr->expr.op)); + break; + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveBoolean\n", + expr->expr.op); + break; + } + + return false; +} + +bool +ExprResolveKeyCode(struct xkb_context *ctx, const ExprDef *expr, + xkb_keycode_t *kc) +{ + xkb_keycode_t leftRtrn, rightRtrn; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_INT) { + log_err(ctx, + "Found constant of type %s where an int was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + + *kc = (xkb_keycode_t) expr->integer.ival; + return true; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + if (!ExprResolveKeyCode(ctx, expr->binary.left, &leftRtrn) || + !ExprResolveKeyCode(ctx, expr->binary.right, &rightRtrn)) + return false; + + switch (expr->expr.op) { + case EXPR_ADD: + *kc = leftRtrn + rightRtrn; + break; + case EXPR_SUBTRACT: + *kc = leftRtrn - rightRtrn; + break; + case EXPR_MULTIPLY: + *kc = leftRtrn * rightRtrn; + break; + case EXPR_DIVIDE: + if (rightRtrn == 0) { + log_err(ctx, "Cannot divide by zero: %d / %d\n", + leftRtrn, rightRtrn); + return false; + } + + *kc = leftRtrn / rightRtrn; + break; + default: + break; + } + + return true; + + case EXPR_NEGATE: + if (!ExprResolveKeyCode(ctx, expr->unary.child, &leftRtrn)) + return false; + + *kc = ~leftRtrn; + return true; + + case EXPR_UNARY_PLUS: + return ExprResolveKeyCode(ctx, expr->unary.child, kc); + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveKeyCode\n", + expr->expr.op); + break; + } + + return false; +} + +/** + * This function returns ... something. It's a bit of a guess, really. + * + * If an integer is given in value ctx, it will be returned in ival. + * If an ident or field reference is given, the lookup function (if given) + * will be called. At the moment, only SimpleLookup use this, and they both + * return the results in uval. And don't support field references. + * + * Cool. + */ +static bool +ExprResolveIntegerLookup(struct xkb_context *ctx, const ExprDef *expr, + int *val_rtrn, IdentLookupFunc lookup, + const void *lookupPriv) +{ + bool ok = false; + int l, r; + unsigned u; + ExprDef *left, *right; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_INT) { + log_err(ctx, + "Found constant of type %s where an int was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + + *val_rtrn = expr->integer.ival; + return true; + + case EXPR_IDENT: + if (lookup) + ok = lookup(ctx, lookupPriv, expr->ident.ident, EXPR_TYPE_INT, &u); + + if (!ok) + log_err(ctx, "Identifier \"%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->ident.ident)); + else + *val_rtrn = (int) u; + + return ok; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + left = expr->binary.left; + right = expr->binary.right; + if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv) || + !ExprResolveIntegerLookup(ctx, right, &r, lookup, lookupPriv)) + return false; + + switch (expr->expr.op) { + case EXPR_ADD: + *val_rtrn = l + r; + break; + case EXPR_SUBTRACT: + *val_rtrn = l - r; + break; + case EXPR_MULTIPLY: + *val_rtrn = l * r; + break; + case EXPR_DIVIDE: + if (r == 0) { + log_err(ctx, "Cannot divide by zero: %d / %d\n", l, r); + return false; + } + *val_rtrn = l / r; + break; + default: + log_err(ctx, "%s of integers not permitted\n", + expr_op_type_to_string(expr->expr.op)); + return false; + } + + return true; + + case EXPR_ASSIGN: + log_wsgo(ctx, "Assignment operator not implemented yet\n"); + break; + + case EXPR_NOT: + log_err(ctx, "The ! operator cannot be applied to an integer\n"); + return false; + + case EXPR_INVERT: + case EXPR_NEGATE: + left = expr->unary.child; + if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv)) + return false; + + *val_rtrn = (expr->expr.op == EXPR_NEGATE ? -l : ~l); + return true; + + case EXPR_UNARY_PLUS: + left = expr->unary.child; + return ExprResolveIntegerLookup(ctx, left, val_rtrn, lookup, + lookupPriv); + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveInteger\n", + expr->expr.op); + break; + } + + return false; +} + +bool +ExprResolveInteger(struct xkb_context *ctx, const ExprDef *expr, + int *val_rtrn) +{ + return ExprResolveIntegerLookup(ctx, expr, val_rtrn, NULL, NULL); +} + +bool +ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr, + xkb_layout_index_t *group_rtrn) +{ + bool ok; + int result; + + ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup, + groupNames); + if (!ok) + 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); + return false; + } + + *group_rtrn = (xkb_layout_index_t) result; + return true; +} + +bool +ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr, + xkb_level_index_t *level_rtrn) +{ + bool ok; + int result; + + ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup, + levelNames); + if (!ok) + return false; + + if (result < 1) { + log_err(ctx, "Shift level %d is out of range\n", result); + return false; + } + + /* Level is zero-indexed from now on. */ + *level_rtrn = (unsigned int) (result - 1); + return true; +} + +bool +ExprResolveButton(struct xkb_context *ctx, const ExprDef *expr, int *btn_rtrn) +{ + return ExprResolveIntegerLookup(ctx, expr, btn_rtrn, SimpleLookup, + buttonNames); +} + +bool +ExprResolveString(struct xkb_context *ctx, const ExprDef *expr, + xkb_atom_t *val_rtrn) +{ + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_STRING) { + log_err(ctx, "Found constant of type %s, expected a string\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + + *val_rtrn = expr->string.str; + return true; + + case EXPR_IDENT: + log_err(ctx, "Identifier \"%s\" of type string not found\n", + xkb_atom_text(ctx, expr->ident.ident)); + return false; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type string not found\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + case EXPR_ASSIGN: + case EXPR_NEGATE: + case EXPR_INVERT: + case EXPR_NOT: + case EXPR_UNARY_PLUS: + case EXPR_ACTION_DECL: + case EXPR_ACTION_LIST: + case EXPR_KEYSYM_LIST: + log_err(ctx, "%s of strings not permitted\n", + expr_op_type_to_string(expr->expr.op)); + return false; + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveString\n", + expr->expr.op); + break; + } + return false; +} + +bool +ExprResolveEnum(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *val_rtrn, const LookupEntry *values) +{ + if (expr->expr.op != EXPR_IDENT) { + log_err(ctx, "Found a %s where an enumerated value was expected\n", + expr_op_type_to_string(expr->expr.op)); + return false; + } + + if (!SimpleLookup(ctx, values, expr->ident.ident, EXPR_TYPE_INT, + val_rtrn)) { + log_err(ctx, "Illegal identifier %s; expected one of:\n", + xkb_atom_text(ctx, expr->ident.ident)); + while (values && values->name) + { + log_err(ctx, "\t%s\n", values->name); + values++; + } + return false; + } + + return true; +} + +static bool +ExprResolveMaskLookup(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *val_rtrn, IdentLookupFunc lookup, + const void *lookupPriv) +{ + bool ok = false; + unsigned int l = 0, r = 0; + int v; + ExprDef *left, *right; + const char *bogus = NULL; + + switch (expr->expr.op) { + case EXPR_VALUE: + if (expr->expr.value_type != EXPR_TYPE_INT) { + log_err(ctx, + "Found constant of type %s where a mask was expected\n", + expr_value_type_to_string(expr->expr.value_type)); + return false; + } + *val_rtrn = (unsigned int) expr->integer.ival; + return true; + + case EXPR_IDENT: + ok = lookup(ctx, lookupPriv, expr->ident.ident, EXPR_TYPE_INT, + val_rtrn); + if (!ok) + log_err(ctx, "Identifier \"%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->ident.ident)); + return ok; + + case EXPR_FIELD_REF: + log_err(ctx, "Default \"%s.%s\" of type int is unknown\n", + xkb_atom_text(ctx, expr->field_ref.element), + xkb_atom_text(ctx, expr->field_ref.field)); + return false; + + case EXPR_ARRAY_REF: + bogus = "array reference"; + /* fallthrough */ + case EXPR_ACTION_DECL: + if (bogus == NULL) + bogus = "function use"; + log_err(ctx, + "Unexpected %s in mask expression; Expression Ignored\n", + bogus); + return false; + + case EXPR_ADD: + case EXPR_SUBTRACT: + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + left = expr->binary.left; + right = expr->binary.right; + if (!ExprResolveMaskLookup(ctx, left, &l, lookup, lookupPriv) || + !ExprResolveMaskLookup(ctx, right, &r, lookup, lookupPriv)) + return false; + + switch (expr->expr.op) { + case EXPR_ADD: + *val_rtrn = l | r; + break; + case EXPR_SUBTRACT: + *val_rtrn = l & (~r); + break; + case EXPR_MULTIPLY: + case EXPR_DIVIDE: + log_err(ctx, "Cannot %s masks; Illegal operation ignored\n", + (expr->expr.op == EXPR_DIVIDE ? "divide" : "multiply")); + return false; + default: + break; + } + + return true; + + case EXPR_ASSIGN: + log_wsgo(ctx, "Assignment operator not implemented yet\n"); + break; + + case EXPR_INVERT: + left = expr->unary.child; + if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv)) + return false; + + *val_rtrn = ~v; + return true; + + case EXPR_UNARY_PLUS: + case EXPR_NEGATE: + case EXPR_NOT: + left = expr->unary.child; + if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv)) + log_err(ctx, "The %s operator cannot be used with a mask\n", + (expr->expr.op == EXPR_NEGATE ? "-" : "!")); + return false; + + default: + log_wsgo(ctx, "Unknown operator %d in ResolveMask\n", + expr->expr.op); + break; + } + + return false; +} + +bool +ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *mask_rtrn, const LookupEntry *values) +{ + return ExprResolveMaskLookup(ctx, expr, mask_rtrn, SimpleLookup, values); +} + +bool +ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_mask_t *mask_rtrn) +{ + LookupModMaskPriv priv = { .mods = mods, .mod_type = mod_type }; + return ExprResolveMaskLookup(ctx, expr, mask_rtrn, LookupModMask, &priv); +} + +bool +ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, + xkb_keysym_t *sym_rtrn) +{ + int val; + + if (expr->expr.op == EXPR_IDENT) { + const char *str = xkb_atom_text(ctx, expr->ident.ident); + *sym_rtrn = xkb_keysym_from_name(str, 0); + if (*sym_rtrn != XKB_KEY_NoSymbol) + return true; + } + + if (!ExprResolveInteger(ctx, expr, &val)) + return false; + + if (val < 0 || val >= 10) + return false; + + *sym_rtrn = XKB_KEY_0 + (xkb_keysym_t) val; + return true; +} + +bool +ExprResolveMod(struct xkb_context *ctx, const ExprDef *def, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_index_t *ndx_rtrn) +{ + xkb_mod_index_t ndx; + xkb_atom_t name; + + if (def->expr.op != EXPR_IDENT) { + log_err(ctx, + "Cannot resolve virtual modifier: " + "found %s where a virtual modifier name was expected\n", + expr_op_type_to_string(def->expr.op)); + return false; + } + + name = def->ident.ident; + ndx = XkbModNameToIndex(mods, name, mod_type); + if (ndx == XKB_MOD_INVALID) { + log_err(ctx, + "Cannot resolve virtual modifier: " + "\"%s\" was not previously declared\n", + xkb_atom_text(ctx, name)); + return false; + } + + *ndx_rtrn = ndx; + return true; +} diff --git a/src/xkbcomp/expr.h b/src/xkbcomp/expr.h new file mode 100644 index 0000000..9882b8c --- /dev/null +++ b/src/xkbcomp/expr.h @@ -0,0 +1,85 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_EXPR_H +#define XKBCOMP_EXPR_H + +bool +ExprResolveLhs(struct xkb_context *ctx, const ExprDef *expr, + const char **elem_rtrn, const char **field_rtrn, + ExprDef **index_rtrn); + +bool +ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_mask_t *mask_rtrn); + +bool +ExprResolveMod(struct xkb_context *ctx, const ExprDef *def, + enum mod_type mod_type, const struct xkb_mod_set *mods, + xkb_mod_index_t *ndx_rtrn); + +bool +ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr, + bool *set_rtrn); + +bool +ExprResolveKeyCode(struct xkb_context *ctx, const ExprDef *expr, + xkb_keycode_t *kc); + +bool +ExprResolveInteger(struct xkb_context *ctx, const ExprDef *expr, + int *val_rtrn); + +bool +ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr, + xkb_level_index_t *level_rtrn); + +bool +ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr, + xkb_layout_index_t *group_rtrn); + +bool +ExprResolveButton(struct xkb_context *ctx, const ExprDef *expr, + int *btn_rtrn); + +bool +ExprResolveString(struct xkb_context *ctx, const ExprDef *expr, + xkb_atom_t *val_rtrn); + +bool +ExprResolveEnum(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *val_rtrn, const LookupEntry *values); + +bool +ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr, + unsigned int *mask_rtrn, const LookupEntry *values); + +bool +ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, + xkb_keysym_t *sym_rtrn); + +#endif diff --git a/src/xkbcomp/include.c b/src/xkbcomp/include.c new file mode 100644 index 0000000..d39be89 --- /dev/null +++ b/src/xkbcomp/include.c @@ -0,0 +1,323 @@ +/************************************************************ + * Copyright (c) 1994 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 © 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 <errno.h> +#include <limits.h> +#include <stdio.h> + +#include "xkbcomp-priv.h" +#include "include.h" + +/** + * Parse an include statement. Each call returns a file name, along with + * (possibly) a specific map in the file, an explicit group designator, and + * the separator from the next file, used to determine the merge mode. + * + * @param str_inout Input statement, modified in-place. Should be passed in + * repeatedly. If str_inout is NULL, the parsing has completed. + * + * @param file_rtrn Set to the name of the include file to be used. Combined + * with an enum xkb_file_type, this determines which file to look for in the + * include path. + * + * @param map_rtrn Set to the string between '(' and ')', if any. This will + * result in the compilation of a specific named map within the file (e.g. + * xkb_symbols "basic" { ... }) , as opposed to the default map of the file. + * + * @param nextop_rtrn Set to the next operation in the complete statement, + * which is '\0' if it's the last file or '+' or '|' if there are more. + * Separating the files with '+' sets the merge mode to MERGE_MODE_OVERRIDE, + * while '|' sets the merge mode to MERGE_MODE_AUGMENT. + * + * @param extra_data Set to the string after ':', if any. Currently the + * extra data is only used for setting an explicit group index for a symbols + * file. + * + * @return true if parsing was successful, false for an illegal string. + * + * Example: "evdev+aliases(qwerty):2" + * str_inout = "aliases(qwerty):2" + * file_rtrn = "evdev" + * map_rtrn = NULL + * nextop_retrn = "+" + * extra_data = NULL + * + * 2nd run with "aliases(qwerty):2" + * str_inout = NULL + * file_rtrn = "aliases" + * map_rtrn = "qwerty" + * nextop_retrn = "" + * extra_data = "2" + * + */ +bool +ParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn, + char *nextop_rtrn, char **extra_data) +{ + char *tmp, *str, *next; + + str = *str_inout; + + /* + * Find the position in the string where the next file is included, + * if there is more than one left in the statement. + */ + next = strpbrk(str, "|+"); + if (next) { + /* Got more files, this function will be called again. */ + *nextop_rtrn = *next; + /* Separate the string, for strchr etc. to work on this file only. */ + *next++ = '\0'; + } + else { + /* This is the last file in this statement, won't be called again. */ + *nextop_rtrn = '\0'; + next = NULL; + } + + /* + * Search for the explicit group designator, if any. If it's there, + * it goes after the file name and map. + */ + tmp = strchr(str, ':'); + if (tmp != NULL) { + *tmp++ = '\0'; + *extra_data = strdup(tmp); + } + else { + *extra_data = NULL; + } + + /* Look for a map, if any. */ + tmp = strchr(str, '('); + if (tmp == NULL) { + /* No map. */ + *file_rtrn = strdup(str); + *map_rtrn = NULL; + } + else if (str[0] == '(') { + /* Map without file - invalid. */ + free(*extra_data); + return false; + } + else { + /* Got a map; separate the file and the map for the strdup's. */ + *tmp++ = '\0'; + *file_rtrn = strdup(str); + str = tmp; + tmp = strchr(str, ')'); + if (tmp == NULL || tmp[1] != '\0') { + free(*file_rtrn); + free(*extra_data); + return false; + } + *tmp++ = '\0'; + *map_rtrn = strdup(str); + } + + /* Set up the next file for the next call, if any. */ + if (*nextop_rtrn == '\0') + *str_inout = NULL; + else if (*nextop_rtrn == '|' || *nextop_rtrn == '+') + *str_inout = next; + else + return false; + + return true; +} + +static const char *xkb_file_type_include_dirs[_FILE_TYPE_NUM_ENTRIES] = { + [FILE_TYPE_KEYCODES] = "keycodes", + [FILE_TYPE_TYPES] = "types", + [FILE_TYPE_COMPAT] = "compat", + [FILE_TYPE_SYMBOLS] = "symbols", + [FILE_TYPE_GEOMETRY] = "geometry", + [FILE_TYPE_KEYMAP] = "keymap", + [FILE_TYPE_RULES] = "rules", +}; + +/** + * Return the xkb directory based on the type. + */ +static const char * +DirectoryForInclude(enum xkb_file_type type) +{ + if (type >= _FILE_TYPE_NUM_ENTRIES) + return ""; + return xkb_file_type_include_dirs[type]; +} + +static void +LogIncludePaths(struct xkb_context *ctx) +{ + unsigned int i; + + if (xkb_context_num_include_paths(ctx) > 0) { + log_err(ctx, "%d include paths searched:\n", + xkb_context_num_include_paths(ctx)); + for (i = 0; i < xkb_context_num_include_paths(ctx); i++) + log_err(ctx, "\t%s\n", + xkb_context_include_path_get(ctx, i)); + } + else { + log_err(ctx, "There are no include paths to search\n"); + } + + if (xkb_context_num_failed_include_paths(ctx) > 0) { + log_err(ctx, "%d include paths could not be added:\n", + xkb_context_num_failed_include_paths(ctx)); + for (i = 0; i < xkb_context_num_failed_include_paths(ctx); i++) + log_err(ctx, "\t%s\n", + xkb_context_failed_include_path_get(ctx, i)); + } +} + +/** + * Return an open file handle to the first file (counting from offset) with the + * given name in the include paths, starting at the offset. + * + * offset must be zero the first time this is called and is set to the index the + * file was found. Call again with offset+1 to keep searching through the + * include paths. + * + * If this function returns NULL, no more files are available. + */ +FILE * +FindFileInXkbPath(struct xkb_context *ctx, const char *name, + enum xkb_file_type type, char **pathRtrn, + unsigned int *offset) +{ + unsigned int i; + FILE *file = NULL; + char *buf = NULL; + const char *typeDir; + + typeDir = DirectoryForInclude(type); + + for (i = *offset; i < xkb_context_num_include_paths(ctx); i++) { + buf = asprintf_safe("%s/%s/%s", xkb_context_include_path_get(ctx, i), + typeDir, name); + if (!buf) { + log_err(ctx, "Failed to alloc buffer for (%s/%s/%s)\n", + xkb_context_include_path_get(ctx, i), typeDir, name); + continue; + } + + file = fopen(buf, "rb"); + if (file) { + if (pathRtrn) { + *pathRtrn = buf; + buf = NULL; + } + *offset = i; + goto out; + } + } + + /* We only print warnings if we can't find the file on the first lookup */ + if (*offset == 0) { + log_err(ctx, "Couldn't find file \"%s/%s\" in include paths\n", + typeDir, name); + LogIncludePaths(ctx); + } + +out: + free(buf); + return file; +} + +XkbFile * +ProcessIncludeFile(struct xkb_context *ctx, IncludeStmt *stmt, + enum xkb_file_type file_type) +{ + FILE *file; + XkbFile *xkb_file = NULL; + unsigned int offset = 0; + + file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL, &offset); + if (!file) + return NULL; + + while (file) { + xkb_file = XkbParseFile(ctx, file, stmt->file, stmt->map); + fclose(file); + + if (xkb_file) { + if (xkb_file->file_type != file_type) { + log_err(ctx, + "Include file of wrong type (expected %s, got %s); " + "Include file \"%s\" ignored\n", + xkb_file_type_to_string(file_type), + xkb_file_type_to_string(xkb_file->file_type), stmt->file); + FreeXkbFile(xkb_file); + xkb_file = NULL; + } else { + break; + } + } + + offset++; + file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL, &offset); + } + + if (!xkb_file) { + if (stmt->map) + log_err(ctx, "Couldn't process include statement for '%s(%s)'\n", + stmt->file, stmt->map); + else + log_err(ctx, "Couldn't process include statement for '%s'\n", + stmt->file); + } + + /* FIXME: we have to check recursive includes here (or somewhere) */ + + return xkb_file; +} diff --git a/src/xkbcomp/include.h b/src/xkbcomp/include.h new file mode 100644 index 0000000..8f360e3 --- /dev/null +++ b/src/xkbcomp/include.h @@ -0,0 +1,43 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_INCLUDE_H +#define XKBCOMP_INCLUDE_H + +bool +ParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn, + char *nextop_rtrn, char **extra_data); + +FILE * +FindFileInXkbPath(struct xkb_context *ctx, const char *name, + enum xkb_file_type type, char **pathRtrn, + unsigned int *offset); + +XkbFile * +ProcessIncludeFile(struct xkb_context *ctx, IncludeStmt *stmt, + enum xkb_file_type file_type); + +#endif diff --git a/src/xkbcomp/keycodes.c b/src/xkbcomp/keycodes.c new file mode 100644 index 0000000..b8abf36 --- /dev/null +++ b/src/xkbcomp/keycodes.c @@ -0,0 +1,671 @@ +/************************************************************ + * Copyright (c) 1994 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 "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "include.h" + +typedef struct { + enum merge_mode merge; + + xkb_atom_t alias; + xkb_atom_t real; +} AliasInfo; + +typedef struct { + enum merge_mode merge; + + xkb_atom_t name; +} LedNameInfo; + +typedef struct { + char *name; + int errorCount; + + xkb_keycode_t min_key_code; + xkb_keycode_t max_key_code; + darray(xkb_atom_t) key_names; + LedNameInfo led_names[XKB_MAX_LEDS]; + unsigned int num_led_names; + darray(AliasInfo) aliases; + + struct xkb_context *ctx; +} KeyNamesInfo; + +/***====================================================================***/ + +static void +InitAliasInfo(AliasInfo *info, enum merge_mode merge, + xkb_atom_t alias, xkb_atom_t real) +{ + memset(info, 0, sizeof(*info)); + info->merge = merge; + info->alias = alias; + info->real = real; +} + +static LedNameInfo * +FindLedByName(KeyNamesInfo *info, xkb_atom_t name, + xkb_led_index_t *idx_out) +{ + for (xkb_led_index_t idx = 0; idx < info->num_led_names; idx++) { + LedNameInfo *ledi = &info->led_names[idx]; + if (ledi->name == name) { + *idx_out = idx; + return ledi; + } + } + + return NULL; +} + +static bool +AddLedName(KeyNamesInfo *info, enum merge_mode merge, bool same_file, + LedNameInfo *new, xkb_led_index_t new_idx) +{ + xkb_led_index_t old_idx; + LedNameInfo *old; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + const bool replace = (merge == MERGE_REPLACE || merge == MERGE_OVERRIDE); + + /* LED with the same name already exists. */ + old = FindLedByName(info, new->name, &old_idx); + if (old) { + if (old_idx == new_idx) { + log_warn(info->ctx, + "Multiple indicators named \"%s\"; " + "Identical definitions ignored\n", + xkb_atom_text(info->ctx, new->name)); + return true; + } + + if (report) { + xkb_led_index_t use = (replace ? new_idx + 1 : old_idx + 1); + xkb_led_index_t ignore = (replace ? old_idx + 1 : new_idx + 1); + log_warn(info->ctx, + "Multiple indicators named %s; Using %d, ignoring %d\n", + xkb_atom_text(info->ctx, new->name), use, ignore); + } + + if (replace) + *old = *new; + + return true; + } + + if (new_idx >= info->num_led_names) + info->num_led_names = new_idx + 1; + + /* LED with the same index already exists. */ + old = &info->led_names[new_idx]; + if (old->name != XKB_ATOM_NONE) { + if (report) { + const xkb_atom_t use = (replace ? new->name : old->name); + const xkb_atom_t ignore = (replace ? old->name : new->name); + log_warn(info->ctx, "Multiple names for indicator %d; " + "Using %s, ignoring %s\n", new_idx + 1, + xkb_atom_text(info->ctx, use), + xkb_atom_text(info->ctx, ignore)); + } + + if (replace) + *old = *new; + + return true; + } + + *old = *new; + return true; +} + +static void +ClearKeyNamesInfo(KeyNamesInfo *info) +{ + free(info->name); + darray_free(info->key_names); + darray_free(info->aliases); +} + +static void +InitKeyNamesInfo(KeyNamesInfo *info, struct xkb_context *ctx) +{ + memset(info, 0, sizeof(*info)); + info->ctx = ctx; + info->min_key_code = XKB_KEYCODE_INVALID; +#if XKB_KEYCODE_INVALID < XKB_KEYCODE_MAX +#error "Hey, you can't be changing stuff like that." +#endif +} + +static xkb_keycode_t +FindKeyByName(KeyNamesInfo *info, xkb_atom_t name) +{ + xkb_keycode_t i; + + for (i = info->min_key_code; i <= info->max_key_code; i++) + if (darray_item(info->key_names, i) == name) + return i; + + return XKB_KEYCODE_INVALID; +} + +static bool +AddKeyName(KeyNamesInfo *info, xkb_keycode_t kc, xkb_atom_t name, + enum merge_mode merge, bool same_file, bool report) +{ + xkb_atom_t old_name; + xkb_keycode_t old_kc; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + + report = report && ((same_file && verbosity > 0) || verbosity > 7); + + if (kc >= darray_size(info->key_names)) + darray_resize0(info->key_names, kc + 1); + + info->min_key_code = MIN(info->min_key_code, kc); + info->max_key_code = MAX(info->max_key_code, kc); + + /* There's already a key with this keycode. */ + old_name = darray_item(info->key_names, kc); + if (old_name != XKB_ATOM_NONE) { + const char *lname = KeyNameText(info->ctx, old_name); + const char *kname = KeyNameText(info->ctx, name); + + if (old_name == name) { + if (report) + log_warn(info->ctx, + "Multiple identical key name definitions; " + "Later occurrences of \"%s = %d\" ignored\n", + lname, kc); + return true; + } + else if (merge == MERGE_AUGMENT) { + if (report) + log_warn(info->ctx, + "Multiple names for keycode %d; " + "Using %s, ignoring %s\n", kc, lname, kname); + return true; + } + else { + if (report) + log_warn(info->ctx, + "Multiple names for keycode %d; " + "Using %s, ignoring %s\n", kc, kname, lname); + darray_item(info->key_names, kc) = XKB_ATOM_NONE; + } + } + + /* There's already a key with this name. */ + old_kc = FindKeyByName(info, name); + if (old_kc != XKB_KEYCODE_INVALID && old_kc != kc) { + const char *kname = KeyNameText(info->ctx, name); + + if (merge == MERGE_OVERRIDE) { + darray_item(info->key_names, old_kc) = XKB_ATOM_NONE; + if (report) + log_warn(info->ctx, + "Key name %s assigned to multiple keys; " + "Using %d, ignoring %d\n", kname, kc, old_kc); + } + else { + if (report) + log_vrb(info->ctx, 3, + "Key name %s assigned to multiple keys; " + "Using %d, ignoring %d\n", kname, old_kc, kc); + return true; + } + } + + darray_item(info->key_names, kc) = name; + return true; +} + +/***====================================================================***/ + +static bool +HandleAliasDef(KeyNamesInfo *info, KeyAliasDef *def, enum merge_mode merge); + +static void +MergeIncludedKeycodes(KeyNamesInfo *into, KeyNamesInfo *from, + enum merge_mode merge) +{ + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + /* Merge key names. */ + if (darray_empty(into->key_names)) { + into->key_names = from->key_names; + darray_init(from->key_names); + into->min_key_code = from->min_key_code; + into->max_key_code = from->max_key_code; + } + else { + if (darray_size(into->key_names) < darray_size(from->key_names)) + darray_resize0(into->key_names, darray_size(from->key_names)); + + for (unsigned i = from->min_key_code; i <= from->max_key_code; i++) { + xkb_atom_t name = darray_item(from->key_names, i); + if (name == XKB_ATOM_NONE) + continue; + + if (!AddKeyName(into, i, name, merge, true, false)) + into->errorCount++; + } + } + + /* Merge key aliases. */ + if (darray_empty(into->aliases)) { + into->aliases = from->aliases; + darray_init(from->aliases); + } + else { + AliasInfo *alias; + + darray_foreach(alias, from->aliases) { + KeyAliasDef def; + + def.merge = (merge == MERGE_DEFAULT ? alias->merge : merge); + def.alias = alias->alias; + def.real = alias->real; + + if (!HandleAliasDef(into, &def, def.merge)) + into->errorCount++; + } + } + + /* Merge LED names. */ + if (into->num_led_names == 0) { + memcpy(into->led_names, from->led_names, + sizeof(*from->led_names) * from->num_led_names); + into->num_led_names = from->num_led_names; + from->num_led_names = 0; + } + else { + for (xkb_led_index_t idx = 0; idx < from->num_led_names; idx++) { + LedNameInfo *ledi = &from->led_names[idx]; + + if (ledi->name == XKB_ATOM_NONE) + continue; + + ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge); + if (!AddLedName(into, ledi->merge, false, ledi, idx)) + into->errorCount++; + } + } +} + +static void +HandleKeycodesFile(KeyNamesInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeKeycodes(KeyNamesInfo *info, IncludeStmt *include) +{ + KeyNamesInfo included; + + InitKeyNamesInfo(&included, info->ctx); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + KeyNamesInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_KEYCODES); + if (!file) { + info->errorCount += 10; + ClearKeyNamesInfo(&included); + return false; + } + + InitKeyNamesInfo(&next_incl, info->ctx); + + HandleKeycodesFile(&next_incl, file, MERGE_OVERRIDE); + + MergeIncludedKeycodes(&included, &next_incl, stmt->merge); + + ClearKeyNamesInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedKeycodes(info, &included, include->merge); + ClearKeyNamesInfo(&included); + + return (info->errorCount == 0); +} + +static bool +HandleKeycodeDef(KeyNamesInfo *info, KeycodeDef *stmt, enum merge_mode merge) +{ + if (stmt->merge != MERGE_DEFAULT) { + if (stmt->merge == MERGE_REPLACE) + merge = MERGE_OVERRIDE; + else + merge = stmt->merge; + } + + if (stmt->value < 0 || stmt->value > XKB_KEYCODE_MAX) { + log_err(info->ctx, + "Illegal keycode %lld: must be between 0..%u; " + "Key ignored\n", (long long) stmt->value, XKB_KEYCODE_MAX); + return false; + } + + return AddKeyName(info, (xkb_keycode_t) stmt->value, + stmt->name, merge, false, true); +} + +static bool +HandleAliasDef(KeyNamesInfo *info, KeyAliasDef *def, enum merge_mode merge) +{ + AliasInfo *old, new; + + darray_foreach(old, info->aliases) { + if (old->alias == def->alias) { + if (def->real == old->real) { + log_vrb(info->ctx, 1, + "Alias of %s for %s declared more than once; " + "First definition ignored\n", + KeyNameText(info->ctx, def->alias), + KeyNameText(info->ctx, def->real)); + } + else { + xkb_atom_t use, ignore; + + use = (merge == MERGE_AUGMENT ? old->real : def->real); + ignore = (merge == MERGE_AUGMENT ? def->real : old->real); + + log_warn(info->ctx, + "Multiple definitions for alias %s; " + "Using %s, ignoring %s\n", + KeyNameText(info->ctx, old->alias), + KeyNameText(info->ctx, use), + KeyNameText(info->ctx, ignore)); + + old->real = use; + } + + old->merge = merge; + return true; + } + } + + InitAliasInfo(&new, merge, def->alias, def->real); + darray_append(info->aliases, new); + return true; +} + +static bool +HandleKeyNameVar(KeyNamesInfo *info, VarDef *stmt) +{ + const char *elem, *field; + ExprDef *arrayNdx; + + if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &arrayNdx)) + return false; + + if (elem) { + log_err(info->ctx, "Unknown element %s encountered; " + "Default for field %s ignored\n", elem, field); + return false; + } + + if (!istreq(field, "minimum") && !istreq(field, "maximum")) { + log_err(info->ctx, "Unknown field encountered; " + "Assignment to field %s ignored\n", field); + return false; + } + + /* We ignore explicit min/max statements, we always use computed. */ + return true; +} + +static bool +HandleLedNameDef(KeyNamesInfo *info, LedNameDef *def, + enum merge_mode merge) +{ + LedNameInfo ledi; + xkb_atom_t name; + + if (def->ndx < 1 || def->ndx > XKB_MAX_LEDS) { + info->errorCount++; + log_err(info->ctx, + "Illegal indicator index (%d) specified; must be between 1 .. %d; " + "Ignored\n", def->ndx, XKB_MAX_LEDS); + return false; + } + + if (!ExprResolveString(info->ctx, def->name, &name)) { + char buf[20]; + snprintf(buf, sizeof(buf), "%u", def->ndx); + info->errorCount++; + return ReportBadType(info->ctx, "indicator", "name", buf, "string"); + } + + ledi.merge = merge; + ledi.name = name; + return AddLedName(info, merge, true, &ledi, def->ndx - 1); +} + +static void +HandleKeycodesFile(KeyNamesInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeKeycodes(info, (IncludeStmt *) stmt); + break; + case STMT_KEYCODE: + ok = HandleKeycodeDef(info, (KeycodeDef *) stmt, merge); + break; + case STMT_ALIAS: + ok = HandleAliasDef(info, (KeyAliasDef *) stmt, merge); + break; + case STMT_VAR: + ok = HandleKeyNameVar(info, (VarDef *) stmt); + break; + case STMT_LED_NAME: + ok = HandleLedNameDef(info, (LedNameDef *) stmt, merge); + break; + default: + log_err(info->ctx, + "Keycode files may define key and indicator names only; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, "Abandoning keycodes file \"%s\"\n", + file->name); + break; + } + } +} + +/***====================================================================***/ + +static bool +CopyKeyNamesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + struct xkb_key *keys; + xkb_keycode_t min_key_code, max_key_code, kc; + + min_key_code = info->min_key_code; + max_key_code = info->max_key_code; + /* If the keymap has no keys, let's just use the safest pair we know. */ + if (min_key_code == XKB_KEYCODE_INVALID) { + min_key_code = 8; + max_key_code = 255; + } + + keys = calloc(max_key_code + 1, sizeof(*keys)); + if (!keys) + return false; + + for (kc = min_key_code; kc <= max_key_code; kc++) + keys[kc].keycode = kc; + + for (kc = info->min_key_code; kc <= info->max_key_code; kc++) + keys[kc].name = darray_item(info->key_names, kc); + + keymap->min_key_code = min_key_code; + keymap->max_key_code = max_key_code; + keymap->keys = keys; + return true; +} + +static bool +CopyKeyAliasesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + AliasInfo *alias; + unsigned i, num_key_aliases; + struct xkb_key_alias *key_aliases; + + /* + * Do some sanity checking on the aliases. We can't do it before + * because keys and their aliases may be added out-of-order. + */ + num_key_aliases = 0; + darray_foreach(alias, info->aliases) { + /* Check that ->real is a key. */ + if (!XkbKeyByName(keymap, alias->real, false)) { + log_vrb(info->ctx, 5, + "Attempt to alias %s to non-existent key %s; Ignored\n", + KeyNameText(info->ctx, alias->alias), + KeyNameText(info->ctx, alias->real)); + alias->real = XKB_ATOM_NONE; + continue; + } + + /* Check that ->alias is not a key. */ + if (XkbKeyByName(keymap, alias->alias, false)) { + log_vrb(info->ctx, 5, + "Attempt to create alias with the name of a real key; " + "Alias \"%s = %s\" ignored\n", + KeyNameText(info->ctx, alias->alias), + KeyNameText(info->ctx, alias->real)); + alias->real = XKB_ATOM_NONE; + continue; + } + + num_key_aliases++; + } + + /* Copy key aliases. */ + key_aliases = NULL; + if (num_key_aliases > 0) { + key_aliases = calloc(num_key_aliases, sizeof(*key_aliases)); + if (!key_aliases) + return false; + + i = 0; + darray_foreach(alias, info->aliases) { + if (alias->real != XKB_ATOM_NONE) { + key_aliases[i].alias = alias->alias; + key_aliases[i].real = alias->real; + i++; + } + } + } + + keymap->num_key_aliases = num_key_aliases; + keymap->key_aliases = key_aliases; + return true; +} + +static bool +CopyLedNamesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + keymap->num_leds = info->num_led_names; + for (xkb_led_index_t idx = 0; idx < info->num_led_names; idx++) { + LedNameInfo *ledi = &info->led_names[idx]; + + if (ledi->name == XKB_ATOM_NONE) + continue; + + keymap->leds[idx].name = ledi->name; + } + + return true; +} + +static bool +CopyKeyNamesInfoToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info) +{ + /* This function trashes keymap on error, but that's OK. */ + if (!CopyKeyNamesToKeymap(keymap, info) || + !CopyKeyAliasesToKeymap(keymap, info) || + !CopyLedNamesToKeymap(keymap, info)) + return false; + + keymap->keycodes_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->keycodes_section_name); + return true; +} + +/***====================================================================***/ + +bool +CompileKeycodes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + KeyNamesInfo info; + + InitKeyNamesInfo(&info, keymap->ctx); + + HandleKeycodesFile(&info, file, merge); + if (info.errorCount != 0) + goto err_info; + + if (!CopyKeyNamesInfoToKeymap(keymap, &info)) + goto err_info; + + ClearKeyNamesInfo(&info); + return true; + +err_info: + ClearKeyNamesInfo(&info); + return false; +} diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c new file mode 100644 index 0000000..e6b438a --- /dev/null +++ b/src/xkbcomp/keymap-dump.c @@ -0,0 +1,666 @@ +/************************************************************ + * Copyright (c) 1994 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 © 2012 Intel Corporation + * + * 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. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" + +#define BUF_CHUNK_SIZE 4096 + +struct buf { + char *buf; + size_t size; + size_t alloc; +}; + +static bool +do_realloc(struct buf *buf, size_t at_least) +{ + char *new; + + buf->alloc += BUF_CHUNK_SIZE; + if (at_least >= BUF_CHUNK_SIZE) + buf->alloc += at_least; + + new = realloc(buf->buf, buf->alloc); + if (!new) + return false; + + buf->buf = new; + return true; +} + +ATTR_PRINTF(2, 3) static bool +check_write_buf(struct buf *buf, const char *fmt, ...) +{ + va_list args; + int printed; + size_t available; + + available = buf->alloc - buf->size; + va_start(args, fmt); + printed = vsnprintf(buf->buf + buf->size, available, fmt, args); + va_end(args); + + if (printed < 0) + goto err; + + if ((size_t) printed >= available) + if (!do_realloc(buf, printed)) + goto err; + + /* The buffer has enough space now. */ + + available = buf->alloc - buf->size; + va_start(args, fmt); + printed = vsnprintf(buf->buf + buf->size, available, fmt, args); + va_end(args); + + if (printed < 0 || (size_t) printed >= available) + goto err; + + buf->size += printed; + return true; + +err: + free(buf->buf); + buf->buf = NULL; + return false; +} + +#define write_buf(buf, ...) do { \ + if (!check_write_buf(buf, __VA_ARGS__)) \ + return false; \ +} while (0) + +static bool +write_vmods(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_mod *mod; + xkb_mod_index_t num_vmods = 0; + + xkb_mods_foreach(mod, &keymap->mods) { + if (mod->type != MOD_VIRT) + continue; + + if (num_vmods == 0) + write_buf(buf, "\tvirtual_modifiers "); + else + write_buf(buf, ","); + write_buf(buf, "%s", xkb_atom_text(keymap->ctx, mod->name)); + num_vmods++; + } + + if (num_vmods > 0) + write_buf(buf, ";\n\n"); + + return true; +} + +static bool +write_keycodes(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_key *key; + xkb_led_index_t idx; + const struct xkb_led *led; + + if (keymap->keycodes_section_name) + write_buf(buf, "xkb_keycodes \"%s\" {\n", + keymap->keycodes_section_name); + else + write_buf(buf, "xkb_keycodes {\n"); + + /* xkbcomp and X11 really want to see keymaps with a minimum of 8, and + * a maximum of at least 255, else XWayland really starts hating life. + * If this is a problem and people really need strictly bounded keymaps, + * we should probably control this with a flag. */ + write_buf(buf, "\tminimum = %u;\n", MIN(keymap->min_key_code, 8)); + write_buf(buf, "\tmaximum = %u;\n", MAX(keymap->max_key_code, 255)); + + xkb_keys_foreach(key, keymap) { + if (key->name == XKB_ATOM_NONE) + continue; + + write_buf(buf, "\t%-20s = %u;\n", + KeyNameText(keymap->ctx, key->name), key->keycode); + } + + xkb_leds_enumerate(idx, led, keymap) + if (led->name != XKB_ATOM_NONE) + write_buf(buf, "\tindicator %u = \"%s\";\n", + idx + 1, xkb_atom_text(keymap->ctx, led->name)); + + + for (unsigned i = 0; i < keymap->num_key_aliases; i++) + write_buf(buf, "\talias %-14s = %s;\n", + KeyNameText(keymap->ctx, keymap->key_aliases[i].alias), + KeyNameText(keymap->ctx, keymap->key_aliases[i].real)); + + write_buf(buf, "};\n\n"); + return true; +} + +static bool +write_types(struct xkb_keymap *keymap, struct buf *buf) +{ + if (keymap->types_section_name) + write_buf(buf, "xkb_types \"%s\" {\n", + keymap->types_section_name); + else + write_buf(buf, "xkb_types {\n"); + + write_vmods(keymap, buf); + + for (unsigned i = 0; i < keymap->num_types; i++) { + const struct xkb_key_type *type = &keymap->types[i]; + + write_buf(buf, "\ttype \"%s\" {\n", + xkb_atom_text(keymap->ctx, type->name)); + + write_buf(buf, "\t\tmodifiers= %s;\n", + ModMaskText(keymap->ctx, &keymap->mods, type->mods.mods)); + + for (unsigned j = 0; j < type->num_entries; j++) { + const char *str; + const struct xkb_key_type_entry *entry = &type->entries[j]; + + /* + * Printing level 1 entries is redundant, it's the default, + * unless there's preserve info. + */ + if (entry->level == 0 && entry->preserve.mods == 0) + continue; + + str = ModMaskText(keymap->ctx, &keymap->mods, entry->mods.mods); + write_buf(buf, "\t\tmap[%s]= %u;\n", + str, entry->level + 1); + + if (entry->preserve.mods) + write_buf(buf, "\t\tpreserve[%s]= %s;\n", + str, ModMaskText(keymap->ctx, &keymap->mods, + entry->preserve.mods)); + } + + for (xkb_level_index_t n = 0; n < type->num_level_names; n++) + if (type->level_names[n]) + write_buf(buf, "\t\tlevel_name[%u]= \"%s\";\n", n + 1, + xkb_atom_text(keymap->ctx, type->level_names[n])); + + write_buf(buf, "\t};\n"); + } + + write_buf(buf, "};\n\n"); + return true; +} + +static bool +write_led_map(struct xkb_keymap *keymap, struct buf *buf, + const struct xkb_led *led) +{ + write_buf(buf, "\tindicator \"%s\" {\n", + xkb_atom_text(keymap->ctx, led->name)); + + if (led->which_groups) { + if (led->which_groups != XKB_STATE_LAYOUT_EFFECTIVE) { + write_buf(buf, "\t\twhichGroupState= %s;\n", + LedStateMaskText(keymap->ctx, led->which_groups)); + } + write_buf(buf, "\t\tgroups= 0x%02x;\n", + led->groups); + } + + if (led->which_mods) { + if (led->which_mods != XKB_STATE_MODS_EFFECTIVE) { + write_buf(buf, "\t\twhichModState= %s;\n", + LedStateMaskText(keymap->ctx, led->which_mods)); + } + write_buf(buf, "\t\tmodifiers= %s;\n", + ModMaskText(keymap->ctx, &keymap->mods, led->mods.mods)); + } + + if (led->ctrls) { + write_buf(buf, "\t\tcontrols= %s;\n", + ControlMaskText(keymap->ctx, led->ctrls)); + } + + write_buf(buf, "\t};\n"); + return true; +} + +static const char * +affect_lock_text(enum xkb_action_flags flags) +{ + switch (flags & (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK)) { + case ACTION_LOCK_NO_UNLOCK: + return ",affect=lock"; + case ACTION_LOCK_NO_LOCK: + return ",affect=unlock"; + case ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK: + return ",affect=neither"; + } + return ""; +} + +static bool +write_action(struct xkb_keymap *keymap, struct buf *buf, + const union xkb_action *action, + const char *prefix, const char *suffix) +{ + const char *type; + const char *args = NULL; + + if (!prefix) + prefix = ""; + if (!suffix) + suffix = ""; + + type = ActionTypeText(action->type); + + switch (action->type) { + case ACTION_TYPE_MOD_SET: + case ACTION_TYPE_MOD_LATCH: + case ACTION_TYPE_MOD_LOCK: + if (action->mods.flags & ACTION_MODS_LOOKUP_MODMAP) + args = "modMapMods"; + else + args = ModMaskText(keymap->ctx, &keymap->mods, + action->mods.mods.mods); + write_buf(buf, "%s%s(modifiers=%s%s%s%s)%s", prefix, type, args, + (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", + (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", + (action->type == ACTION_TYPE_MOD_LOCK) ? affect_lock_text(action->mods.flags) : "", + suffix); + break; + + case ACTION_TYPE_GROUP_SET: + case ACTION_TYPE_GROUP_LATCH: + case ACTION_TYPE_GROUP_LOCK: + write_buf(buf, "%s%s(group=%s%d%s%s)%s", prefix, type, + (!(action->group.flags & ACTION_ABSOLUTE_SWITCH) && action->group.group > 0) ? "+" : "", + (action->group.flags & ACTION_ABSOLUTE_SWITCH) ? action->group.group + 1 : action->group.group, + (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", + (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", + suffix); + break; + + case ACTION_TYPE_TERMINATE: + write_buf(buf, "%s%s()%s", prefix, type, suffix); + break; + + case ACTION_TYPE_PTR_MOVE: + write_buf(buf, "%s%s(x=%s%d,y=%s%d%s)%s", prefix, type, + (!(action->ptr.flags & ACTION_ABSOLUTE_X) && action->ptr.x >= 0) ? "+" : "", + action->ptr.x, + (!(action->ptr.flags & ACTION_ABSOLUTE_Y) && action->ptr.y >= 0) ? "+" : "", + action->ptr.y, + (action->ptr.flags & ACTION_ACCEL) ? "" : ",!accel", + suffix); + break; + + case ACTION_TYPE_PTR_LOCK: + args = affect_lock_text(action->btn.flags); + /* fallthrough */ + case ACTION_TYPE_PTR_BUTTON: + write_buf(buf, "%s%s(button=", prefix, type); + if (action->btn.button > 0 && action->btn.button <= 5) + write_buf(buf, "%d", action->btn.button); + else + write_buf(buf, "default"); + if (action->btn.count) + write_buf(buf, ",count=%d", action->btn.count); + if (args) + write_buf(buf, "%s", args); + write_buf(buf, ")%s", suffix); + break; + + case ACTION_TYPE_PTR_DEFAULT: + write_buf(buf, "%s%s(", prefix, type); + write_buf(buf, "affect=button,button=%s%d", + (!(action->dflt.flags & ACTION_ABSOLUTE_SWITCH) && action->dflt.value >= 0) ? "+" : "", + action->dflt.value); + write_buf(buf, ")%s", suffix); + break; + + case ACTION_TYPE_SWITCH_VT: + write_buf(buf, "%s%s(screen=%s%d,%ssame)%s", prefix, type, + (!(action->screen.flags & ACTION_ABSOLUTE_SWITCH) && action->screen.screen >= 0) ? "+" : "", + action->screen.screen, + (action->screen.flags & ACTION_SAME_SCREEN) ? "" : "!", + suffix); + break; + + case ACTION_TYPE_CTRL_SET: + case ACTION_TYPE_CTRL_LOCK: + write_buf(buf, "%s%s(controls=%s%s)%s", prefix, type, + ControlMaskText(keymap->ctx, action->ctrls.ctrls), + (action->type == ACTION_TYPE_CTRL_LOCK) ? affect_lock_text(action->ctrls.flags) : "", + suffix); + break; + + case ACTION_TYPE_NONE: + write_buf(buf, "%sNoAction()%s", prefix, suffix); + break; + + default: + write_buf(buf, + "%s%s(type=0x%02x,data[0]=0x%02x,data[1]=0x%02x,data[2]=0x%02x,data[3]=0x%02x,data[4]=0x%02x,data[5]=0x%02x,data[6]=0x%02x)%s", + prefix, type, action->type, action->priv.data[0], + action->priv.data[1], action->priv.data[2], + action->priv.data[3], action->priv.data[4], + action->priv.data[5], action->priv.data[6], + suffix); + break; + } + + return true; +} + +static bool +write_compat(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_led *led; + + if (keymap->compat_section_name) + write_buf(buf, "xkb_compatibility \"%s\" {\n", + keymap->compat_section_name); + else + write_buf(buf, "xkb_compatibility {\n"); + + write_vmods(keymap, buf); + + write_buf(buf, "\tinterpret.useModMapMods= AnyLevel;\n"); + write_buf(buf, "\tinterpret.repeat= False;\n"); + + for (unsigned i = 0; i < keymap->num_sym_interprets; i++) { + const struct xkb_sym_interpret *si = &keymap->sym_interprets[i]; + + write_buf(buf, "\tinterpret %s+%s(%s) {\n", + si->sym ? KeysymText(keymap->ctx, si->sym) : "Any", + SIMatchText(si->match), + ModMaskText(keymap->ctx, &keymap->mods, si->mods)); + + if (si->virtual_mod != XKB_MOD_INVALID) + write_buf(buf, "\t\tvirtualModifier= %s;\n", + ModIndexText(keymap->ctx, &keymap->mods, + si->virtual_mod)); + + if (si->level_one_only) + write_buf(buf, "\t\tuseModMapMods=level1;\n"); + + if (si->repeat) + write_buf(buf, "\t\trepeat= True;\n"); + + write_action(keymap, buf, &si->action, "\t\taction= ", ";\n"); + write_buf(buf, "\t};\n"); + } + + xkb_leds_foreach(led, keymap) + if (led->which_groups || led->groups || led->which_mods || + led->mods.mods || led->ctrls) + write_led_map(keymap, buf, led); + + write_buf(buf, "};\n\n"); + + return true; +} + +static bool +write_keysyms(struct xkb_keymap *keymap, struct buf *buf, + const struct xkb_key *key, xkb_layout_index_t group) +{ + for (xkb_level_index_t level = 0; level < XkbKeyNumLevels(key, group); + level++) { + const xkb_keysym_t *syms; + int num_syms; + + if (level != 0) + write_buf(buf, ", "); + + num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, + group, level, &syms); + if (num_syms == 0) { + write_buf(buf, "%15s", "NoSymbol"); + } + else if (num_syms == 1) { + write_buf(buf, "%15s", KeysymText(keymap->ctx, syms[0])); + } + else { + write_buf(buf, "{ "); + for (int s = 0; s < num_syms; s++) { + if (s != 0) + write_buf(buf, ", "); + write_buf(buf, "%s", KeysymText(keymap->ctx, syms[s])); + } + write_buf(buf, " }"); + } + } + + return true; +} + +static bool +write_key(struct xkb_keymap *keymap, struct buf *buf, + const struct xkb_key *key) +{ + xkb_layout_index_t group; + bool simple = true; + bool explicit_types = false; + bool multi_type = false; + bool show_actions; + + write_buf(buf, "\tkey %-20s {", KeyNameText(keymap->ctx, key->name)); + + for (group = 0; group < key->num_groups; group++) { + if (key->groups[group].explicit_type) + explicit_types = true; + + if (group != 0 && key->groups[group].type != key->groups[0].type) + multi_type = true; + } + + if (explicit_types) { + const struct xkb_key_type *type; + simple = false; + + if (multi_type) { + for (group = 0; group < key->num_groups; group++) { + if (!key->groups[group].explicit_type) + continue; + + type = key->groups[group].type; + write_buf(buf, "\n\t\ttype[Group%u]= \"%s\",", + group + 1, + xkb_atom_text(keymap->ctx, type->name)); + } + } + else { + type = key->groups[0].type; + write_buf(buf, "\n\t\ttype= \"%s\",", + xkb_atom_text(keymap->ctx, type->name)); + } + } + + if (key->explicit & EXPLICIT_REPEAT) { + if (key->repeats) + write_buf(buf, "\n\t\trepeat= Yes,"); + else + write_buf(buf, "\n\t\trepeat= No,"); + simple = false; + } + + if (key->vmodmap && (key->explicit & EXPLICIT_VMODMAP)) + write_buf(buf, "\n\t\tvirtualMods= %s,", + ModMaskText(keymap->ctx, &keymap->mods, key->vmodmap)); + + switch (key->out_of_range_group_action) { + case RANGE_SATURATE: + write_buf(buf, "\n\t\tgroupsClamp,"); + break; + + case RANGE_REDIRECT: + write_buf(buf, "\n\t\tgroupsRedirect= Group%u,", + key->out_of_range_group_number + 1); + break; + + default: + break; + } + + show_actions = (key->explicit & EXPLICIT_INTERP); + + if (key->num_groups > 1 || show_actions) + simple = false; + + if (simple) { + write_buf(buf, "\t[ "); + if (!write_keysyms(keymap, buf, key, 0)) + return false; + write_buf(buf, " ] };\n"); + } + else { + xkb_level_index_t level; + + for (group = 0; group < key->num_groups; group++) { + if (group != 0) + write_buf(buf, ","); + write_buf(buf, "\n\t\tsymbols[Group%u]= [ ", group + 1); + if (!write_keysyms(keymap, buf, key, group)) + return false; + write_buf(buf, " ]"); + if (show_actions) { + write_buf(buf, ",\n\t\tactions[Group%u]= [ ", group + 1); + for (level = 0; level < XkbKeyNumLevels(key, group); level++) { + if (level != 0) + write_buf(buf, ", "); + write_action(keymap, buf, + &key->groups[group].levels[level].action, + NULL, NULL); + } + write_buf(buf, " ]"); + } + } + write_buf(buf, "\n\t};\n"); + } + + return true; +} + +static bool +write_symbols(struct xkb_keymap *keymap, struct buf *buf) +{ + const struct xkb_key *key; + xkb_layout_index_t group; + xkb_mod_index_t i; + const struct xkb_mod *mod; + + if (keymap->symbols_section_name) + write_buf(buf, "xkb_symbols \"%s\" {\n", + keymap->symbols_section_name); + else + write_buf(buf, "xkb_symbols {\n"); + + for (group = 0; group < keymap->num_group_names; group++) + if (keymap->group_names[group]) + write_buf(buf, + "\tname[Group%u]=\"%s\";\n", group + 1, + xkb_atom_text(keymap->ctx, keymap->group_names[group])); + if (group > 0) + write_buf(buf, "\n"); + + xkb_keys_foreach(key, keymap) + if (key->num_groups > 0) + write_key(keymap, buf, key); + + xkb_mods_enumerate(i, mod, &keymap->mods) { + bool had_any = false; + xkb_keys_foreach(key, keymap) { + if (key->modmap & (1u << i)) { + if (!had_any) + write_buf(buf, "\tmodifier_map %s { ", + xkb_atom_text(keymap->ctx, mod->name)); + write_buf(buf, "%s%s", + had_any ? ", " : "", + KeyNameText(keymap->ctx, key->name)); + had_any = true; + } + } + if (had_any) + write_buf(buf, " };\n"); + } + + write_buf(buf, "};\n\n"); + return true; +} + +static bool +write_keymap(struct xkb_keymap *keymap, struct buf *buf) +{ + return (check_write_buf(buf, "xkb_keymap {\n") && + write_keycodes(keymap, buf) && + write_types(keymap, buf) && + write_compat(keymap, buf) && + write_symbols(keymap, buf) && + check_write_buf(buf, "};\n")); +} + +char * +text_v1_keymap_get_as_string(struct xkb_keymap *keymap) +{ + struct buf buf = { NULL, 0, 0 }; + + if (!write_keymap(keymap, &buf)) { + free(buf.buf); + return NULL; + } + + return buf.buf; +} diff --git a/src/xkbcomp/keymap.c b/src/xkbcomp/keymap.c new file mode 100644 index 0000000..0d14913 --- /dev/null +++ b/src/xkbcomp/keymap.c @@ -0,0 +1,300 @@ +/* + * Copyright © 2009 Dan Nicholson + * Copyright © 2012 Intel Corporation + * 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. + * + * Author: Dan Nicholson <dbn.lists@gmail.com> + * Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" + +static void +ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods) +{ + mods->mask = mod_mask_get_effective(keymap, mods->mods); +} + +static void +UpdateActionMods(struct xkb_keymap *keymap, union xkb_action *act, + xkb_mod_mask_t modmap) +{ + switch (act->type) { + case ACTION_TYPE_MOD_SET: + case ACTION_TYPE_MOD_LATCH: + case ACTION_TYPE_MOD_LOCK: + if (act->mods.flags & ACTION_MODS_LOOKUP_MODMAP) + act->mods.mods.mods = modmap; + ComputeEffectiveMask(keymap, &act->mods.mods); + break; + default: + break; + } +} + +static const struct xkb_sym_interpret default_interpret = { + .sym = XKB_KEY_NoSymbol, + .repeat = true, + .match = MATCH_ANY_OR_NONE, + .mods = 0, + .virtual_mod = XKB_MOD_INVALID, + .action = { .type = ACTION_TYPE_NONE }, +}; + +/** + * Find an interpretation which applies to this particular level, either by + * finding an exact match for the symbol and modifier combination, or a + * generic XKB_KEY_NoSymbol match. + */ +static const struct xkb_sym_interpret * +FindInterpForKey(struct xkb_keymap *keymap, const struct xkb_key *key, + xkb_layout_index_t group, xkb_level_index_t level) +{ + const xkb_keysym_t *syms; + int num_syms; + + num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, group, + level, &syms); + if (num_syms == 0) + return NULL; + + /* + * There may be multiple matchings interprets; we should always return + * the most specific. Here we rely on compat.c to set up the + * sym_interprets array from the most specific to the least specific, + * such that when we find a match we return immediately. + */ + for (unsigned i = 0; i < keymap->num_sym_interprets; i++) { + const struct xkb_sym_interpret *interp = &keymap->sym_interprets[i]; + + xkb_mod_mask_t mods; + bool found = false; + + if ((num_syms > 1 || interp->sym != syms[0]) && + interp->sym != XKB_KEY_NoSymbol) + continue; + + if (interp->level_one_only && level != 0) + mods = 0; + else + mods = key->modmap; + + switch (interp->match) { + case MATCH_NONE: + found = !(interp->mods & mods); + break; + case MATCH_ANY_OR_NONE: + found = (!mods || (interp->mods & mods)); + break; + case MATCH_ANY: + found = (interp->mods & mods); + break; + case MATCH_ALL: + found = ((interp->mods & mods) == interp->mods); + break; + case MATCH_EXACTLY: + found = (interp->mods == mods); + break; + } + + if (found) + return interp; + } + + return &default_interpret; +} + +static bool +ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key) +{ + xkb_mod_mask_t vmodmap = 0; + xkb_layout_index_t group; + xkb_level_index_t level; + + /* If we've been told not to bind interps to this key, then don't. */ + if (key->explicit & EXPLICIT_INTERP) + return true; + + for (group = 0; group < key->num_groups; group++) { + for (level = 0; level < XkbKeyNumLevels(key, group); level++) { + const struct xkb_sym_interpret *interp; + + interp = FindInterpForKey(keymap, key, group, level); + if (!interp) + continue; + + /* Infer default key behaviours from the base level. */ + if (group == 0 && level == 0) + if (!(key->explicit & EXPLICIT_REPEAT) && interp->repeat) + key->repeats = true; + + if ((group == 0 && level == 0) || !interp->level_one_only) + if (interp->virtual_mod != XKB_MOD_INVALID) + vmodmap |= (1u << interp->virtual_mod); + + if (interp->action.type != ACTION_TYPE_NONE) + key->groups[group].levels[level].action = interp->action; + } + } + + if (!(key->explicit & EXPLICIT_VMODMAP)) + key->vmodmap = vmodmap; + + return true; +} + +/** + * This collects a bunch of disparate functions which was done in the server + * at various points that really should've been done within xkbcomp. Turns out + * your actions and types are a lot more useful when any of your modifiers + * other than Shift actually do something ... + */ +static bool +UpdateDerivedKeymapFields(struct xkb_keymap *keymap) +{ + struct xkb_key *key; + struct xkb_mod *mod; + struct xkb_led *led; + unsigned int i, j; + + /* Find all the interprets for the key and bind them to actions, + * which will also update the vmodmap. */ + xkb_keys_foreach(key, keymap) + if (!ApplyInterpsToKey(keymap, key)) + return false; + + /* Update keymap->mods, the virtual -> real mod mapping. */ + xkb_keys_foreach(key, keymap) + xkb_mods_enumerate(i, mod, &keymap->mods) + if (key->vmodmap & (1u << i)) + mod->mapping |= key->modmap; + + /* Now update the level masks for all the types to reflect the vmods. */ + for (i = 0; i < keymap->num_types; i++) { + ComputeEffectiveMask(keymap, &keymap->types[i].mods); + + for (j = 0; j < keymap->types[i].num_entries; j++) { + ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].mods); + ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].preserve); + } + } + + /* Update action modifiers. */ + xkb_keys_foreach(key, keymap) + for (i = 0; i < key->num_groups; i++) + for (j = 0; j < XkbKeyNumLevels(key, i); j++) + UpdateActionMods(keymap, &key->groups[i].levels[j].action, + key->modmap); + + /* Update vmod -> led maps. */ + xkb_leds_foreach(led, keymap) + ComputeEffectiveMask(keymap, &led->mods); + + /* Find maximum number of groups out of all keys in the keymap. */ + xkb_keys_foreach(key, keymap) + keymap->num_groups = MAX(keymap->num_groups, key->num_groups); + + return true; +} + +typedef bool (*compile_file_fn)(XkbFile *file, + struct xkb_keymap *keymap, + enum merge_mode merge); + +static const compile_file_fn compile_file_fns[LAST_KEYMAP_FILE_TYPE + 1] = { + [FILE_TYPE_KEYCODES] = CompileKeycodes, + [FILE_TYPE_TYPES] = CompileKeyTypes, + [FILE_TYPE_COMPAT] = CompileCompatMap, + [FILE_TYPE_SYMBOLS] = CompileSymbols, +}; + +bool +CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, enum merge_mode merge) +{ + bool ok; + XkbFile *files[LAST_KEYMAP_FILE_TYPE + 1] = { NULL }; + enum xkb_file_type type; + struct xkb_context *ctx = keymap->ctx; + + /* Collect section files and check for duplicates. */ + for (file = (XkbFile *) file->defs; file; + file = (XkbFile *) file->common.next) { + if (file->file_type < FIRST_KEYMAP_FILE_TYPE || + file->file_type > LAST_KEYMAP_FILE_TYPE) { + if (file->file_type == FILE_TYPE_GEOMETRY) { + log_vrb(ctx, 1, + "Geometry sections are not supported; ignoring\n"); + } else { + log_err(ctx, "Cannot define %s in a keymap file\n", + xkb_file_type_to_string(file->file_type)); + } + continue; + } + + if (files[file->file_type]) { + log_err(ctx, + "More than one %s section in keymap file; " + "All sections after the first ignored\n", + xkb_file_type_to_string(file->file_type)); + continue; + } + + files[file->file_type] = file; + } + + /* + * Check that all required section were provided. + * Report everything before failing. + */ + ok = true; + for (type = FIRST_KEYMAP_FILE_TYPE; + type <= LAST_KEYMAP_FILE_TYPE; + type++) { + if (files[type] == NULL) { + log_err(ctx, "Required section %s missing from keymap\n", + xkb_file_type_to_string(type)); + ok = false; + } + } + if (!ok) + return false; + + /* Compile sections. */ + for (type = FIRST_KEYMAP_FILE_TYPE; + type <= LAST_KEYMAP_FILE_TYPE; + type++) { + log_dbg(ctx, "Compiling %s \"%s\"\n", + xkb_file_type_to_string(type), files[type]->name); + + ok = compile_file_fns[type](files[type], keymap, merge); + if (!ok) { + log_err(ctx, "Failed to compile %s\n", + xkb_file_type_to_string(type)); + return false; + } + } + + return UpdateDerivedKeymapFields(keymap); +} diff --git a/src/xkbcomp/keywords.c b/src/xkbcomp/keywords.c new file mode 100644 index 0000000..c8a48f1 --- /dev/null +++ b/src/xkbcomp/keywords.c @@ -0,0 +1,389 @@ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf src/xkbcomp/keywords.gperf */ +/* Computed positions: -k'1-2,5' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." +#endif + +#line 1 "src/xkbcomp/keywords.gperf" + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "parser-priv.h" + +static const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register size_t len); +#line 11 "src/xkbcomp/keywords.gperf" +struct keyword_tok { int name; int tok; }; +#include <string.h> +/* maximum key range = 70, duplicates = 0 */ + +#ifndef GPERF_DOWNCASE +#define GPERF_DOWNCASE 1 +static unsigned char gperf_downcase[256] = + { + 0, 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, 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, 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, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 + }; +#endif + +#ifndef GPERF_CASE_STRCMP +#define GPERF_CASE_STRCMP 1 +static int +gperf_case_strcmp (register const char *s1, register const char *s2) +{ + for (;;) + { + unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; + unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; + if (c1 != 0 && c1 == c2) + continue; + return (int)c1 - (int)c2; + } +} +#endif + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +keyword_gperf_hash (register const char *str, register size_t len) +{ + static const unsigned char asso_values[] = + { + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 0, 73, 5, 36, 0, + 10, 1, 15, 15, 73, 0, 10, 20, 35, 20, + 50, 73, 10, 10, 5, 0, 15, 73, 0, 15, + 73, 73, 73, 73, 73, 73, 73, 0, 73, 5, + 36, 0, 10, 1, 15, 15, 73, 0, 10, 20, + 35, 20, 50, 73, 10, 10, 5, 0, 15, 73, + 0, 15, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73 + }; + register unsigned int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + case 3: + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +struct stringpool_t + { + char stringpool_str3[sizeof("key")]; + char stringpool_str4[sizeof("keys")]; + char stringpool_str7[sizeof("augment")]; + char stringpool_str9[sizeof("text")]; + char stringpool_str10[sizeof("xkb_keymap")]; + char stringpool_str11[sizeof("keypad_keys")]; + char stringpool_str12[sizeof("xkb_keycodes")]; + char stringpool_str13[sizeof("xkb_geometry")]; + char stringpool_str14[sizeof("xkb_types")]; + char stringpool_str15[sizeof("xkb_compat")]; + char stringpool_str17[sizeof("replace")]; + char stringpool_str19[sizeof("xkb_compat_map")]; + char stringpool_str20[sizeof("xkb_layout")]; + char stringpool_str21[sizeof("xkb_symbols")]; + char stringpool_str22[sizeof("xkb_compatibility")]; + char stringpool_str23[sizeof("xkb_semantics")]; + char stringpool_str24[sizeof("type")]; + char stringpool_str25[sizeof("alias")]; + char stringpool_str26[sizeof("xkb_compatibility_map")]; + char stringpool_str27[sizeof("alphanumeric_keys")]; + char stringpool_str28[sizeof("function_keys")]; + char stringpool_str29[sizeof("alternate")]; + char stringpool_str30[sizeof("shape")]; + char stringpool_str31[sizeof("action")]; + char stringpool_str32[sizeof("section")]; + char stringpool_str33[sizeof("row")]; + char stringpool_str34[sizeof("logo")]; + char stringpool_str35[sizeof("alternate_group")]; + char stringpool_str36[sizeof("hidden")]; + char stringpool_str37[sizeof("virtual")]; + char stringpool_str42[sizeof("outline")]; + char stringpool_str43[sizeof("default")]; + char stringpool_str46[sizeof("modmap")]; + char stringpool_str47[sizeof("virtual_modifiers")]; + char stringpool_str52[sizeof("overlay")]; + char stringpool_str53[sizeof("override")]; + char stringpool_str57[sizeof("include")]; + char stringpool_str62[sizeof("modifier_map")]; + char stringpool_str63[sizeof("modifier_keys")]; + char stringpool_str64[sizeof("indicator")]; + char stringpool_str66[sizeof("group")]; + char stringpool_str67[sizeof("mod_map")]; + char stringpool_str69[sizeof("interpret")]; + char stringpool_str71[sizeof("solid")]; + char stringpool_str72[sizeof("partial")]; + }; +static const struct stringpool_t stringpool_contents = + { + "key", + "keys", + "augment", + "text", + "xkb_keymap", + "keypad_keys", + "xkb_keycodes", + "xkb_geometry", + "xkb_types", + "xkb_compat", + "replace", + "xkb_compat_map", + "xkb_layout", + "xkb_symbols", + "xkb_compatibility", + "xkb_semantics", + "type", + "alias", + "xkb_compatibility_map", + "alphanumeric_keys", + "function_keys", + "alternate", + "shape", + "action", + "section", + "row", + "logo", + "alternate_group", + "hidden", + "virtual", + "outline", + "default", + "modmap", + "virtual_modifiers", + "overlay", + "override", + "include", + "modifier_map", + "modifier_keys", + "indicator", + "group", + "mod_map", + "interpret", + "solid", + "partial" + }; +#define stringpool ((const char *) &stringpool_contents) +const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register size_t len) +{ + enum + { + TOTAL_KEYWORDS = 45, + MIN_WORD_LENGTH = 3, + MAX_WORD_LENGTH = 21, + MIN_HASH_VALUE = 3, + MAX_HASH_VALUE = 72 + }; + + static const struct keyword_tok wordlist[] = + { + {-1}, {-1}, {-1}, +#line 37 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str3, KEY}, +#line 38 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str4, KEYS}, + {-1}, {-1}, +#line 28 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str7, AUGMENT}, + {-1}, +#line 53 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str9, TEXT}, +#line 63 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str10, XKB_KEYMAP}, +#line 36 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str11, KEYPAD_KEYS}, +#line 62 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, XKB_KEYCODES}, +#line 61 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, XKB_GEOMETRY}, +#line 67 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, XKB_TYPES}, +#line 60 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, XKB_COMPATMAP}, + {-1}, +#line 48 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, REPLACE}, + {-1}, +#line 59 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, XKB_COMPATMAP}, +#line 64 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, XKB_LAYOUT}, +#line 66 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, XKB_SYMBOLS}, +#line 58 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, XKB_COMPATMAP}, +#line 65 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, XKB_SEMANTICS}, +#line 54 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, TYPE}, +#line 24 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, ALIAS}, +#line 57 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, XKB_COMPATMAP}, +#line 25 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str27, ALPHANUMERIC_KEYS}, +#line 30 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, FUNCTION_KEYS}, +#line 27 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, ALTERNATE}, +#line 51 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, SHAPE}, +#line 23 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, ACTION_TOK}, +#line 50 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, SECTION}, +#line 49 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, ROW}, +#line 39 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, LOGO}, +#line 26 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, ALTERNATE_GROUP}, +#line 32 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, HIDDEN}, +#line 56 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VIRTUAL}, + {-1}, {-1}, {-1}, {-1}, +#line 44 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, OUTLINE}, +#line 29 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, DEFAULT}, + {-1}, {-1}, +#line 43 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, MODIFIER_MAP}, +#line 55 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VIRTUAL_MODS}, + {-1}, {-1}, {-1}, {-1}, +#line 45 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, OVERLAY}, +#line 46 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, OVERRIDE}, + {-1}, {-1}, {-1}, +#line 33 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, INCLUDE}, + {-1}, {-1}, {-1}, {-1}, +#line 41 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, MODIFIER_MAP}, +#line 40 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, MODIFIER_KEYS}, +#line 34 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, INDICATOR}, + {-1}, +#line 31 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, GROUP}, +#line 42 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, MODIFIER_MAP}, + {-1}, +#line 35 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str69, INTERPRET}, + {-1}, +#line 52 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, SOLID}, +#line 47 "src/xkbcomp/keywords.gperf" + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, PARTIAL} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register unsigned int key = keyword_gperf_hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + register int o = wordlist[key].name; + if (o >= 0) + { + register const char *s = o + stringpool; + + if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strcmp (str, s)) + return &wordlist[key]; + } + } + } + return 0; +} +#line 68 "src/xkbcomp/keywords.gperf" + + +int +keyword_to_token(const char *string, size_t len) +{ + const struct keyword_tok *kt = keyword_gperf_lookup(string, len); + if (!kt) + return -1; + return kt->tok; +} diff --git a/src/xkbcomp/keywords.gperf b/src/xkbcomp/keywords.gperf new file mode 100644 index 0000000..dc6a13c --- /dev/null +++ b/src/xkbcomp/keywords.gperf @@ -0,0 +1,77 @@ +%{ +#include "config.h" + +#include "xkbcomp-priv.h" +#include "parser-priv.h" + +static const struct keyword_tok * +keyword_gperf_lookup (register const char *str, register size_t len); +%} + +struct keyword_tok { int name; int tok; }; +%language=ANSI-C +%define hash-function-name keyword_gperf_hash +%define lookup-function-name keyword_gperf_lookup +%readonly-tables +%enum +%includes +%struct-type +%pic +%ignore-case + +%% +action, ACTION_TOK +alias, ALIAS +alphanumeric_keys, ALPHANUMERIC_KEYS +alternate_group, ALTERNATE_GROUP +alternate, ALTERNATE +augment, AUGMENT +default, DEFAULT +function_keys, FUNCTION_KEYS +group, GROUP +hidden, HIDDEN +include, INCLUDE +indicator, INDICATOR +interpret, INTERPRET +keypad_keys, KEYPAD_KEYS +key, KEY +keys, KEYS +logo, LOGO +modifier_keys, MODIFIER_KEYS +modifier_map, MODIFIER_MAP +mod_map, MODIFIER_MAP +modmap, MODIFIER_MAP +outline, OUTLINE +overlay, OVERLAY +override, OVERRIDE +partial, PARTIAL +replace, REPLACE +row, ROW +section, SECTION +shape, SHAPE +solid, SOLID +text, TEXT +type, TYPE +virtual_modifiers, VIRTUAL_MODS +virtual, VIRTUAL +xkb_compatibility_map, XKB_COMPATMAP +xkb_compatibility, XKB_COMPATMAP +xkb_compat_map, XKB_COMPATMAP +xkb_compat, XKB_COMPATMAP +xkb_geometry, XKB_GEOMETRY +xkb_keycodes, XKB_KEYCODES +xkb_keymap, XKB_KEYMAP +xkb_layout, XKB_LAYOUT +xkb_semantics, XKB_SEMANTICS +xkb_symbols, XKB_SYMBOLS +xkb_types, XKB_TYPES +%% + +int +keyword_to_token(const char *string, size_t len) +{ + const struct keyword_tok *kt = keyword_gperf_lookup(string, len); + if (!kt) + return -1; + return kt->tok; +} diff --git a/src/xkbcomp/parser-priv.h b/src/xkbcomp/parser-priv.h new file mode 100644 index 0000000..f10351a --- /dev/null +++ b/src/xkbcomp/parser-priv.h @@ -0,0 +1,44 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_PARSER_PRIV_H +#define XKBCOMP_PARSER_PRIV_H + +struct parser_param; +struct scanner; + +#include "parser.h" + +int +_xkbcommon_lex(YYSTYPE *yylval, struct scanner *scanner); + +XkbFile * +parse(struct xkb_context *ctx, struct scanner *scanner, const char *map); + +int +keyword_to_token(const char *string, size_t len); + +#endif diff --git a/src/xkbcomp/parser.c b/src/xkbcomp/parser.c new file mode 100644 index 0000000..26d7a57 --- /dev/null +++ b/src/xkbcomp/parser.c @@ -0,0 +1,3403 @@ +/* A Bison parser, made by GNU Bison 3.6.3. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.6.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse _xkbcommon_parse +#define yylex _xkbcommon_lex +#define yyerror _xkbcommon_error +#define yydebug _xkbcommon_debug +#define yynerrs _xkbcommon_nerrs + +/* First part of user prologue. */ +#line 33 "../src/xkbcomp/parser.y" + +#include "config.h" + +#include "xkbcomp/xkbcomp-priv.h" +#include "xkbcomp/ast-build.h" +#include "xkbcomp/parser-priv.h" +#include "scanner-utils.h" + +struct parser_param { + struct xkb_context *ctx; + struct scanner *scanner; + XkbFile *rtrn; + bool more_maps; +}; + +#define parser_err(param, fmt, ...) \ + scanner_err((param)->scanner, fmt, ##__VA_ARGS__) + +#define parser_warn(param, fmt, ...) \ + scanner_warn((param)->scanner, fmt, ##__VA_ARGS__) + +static void +_xkbcommon_error(struct parser_param *param, const char *msg) +{ + parser_err(param, "%s", msg); +} + +static bool +resolve_keysym(const char *name, xkb_keysym_t *sym_rtrn) +{ + xkb_keysym_t sym; + + if (!name || istreq(name, "any") || istreq(name, "nosymbol")) { + *sym_rtrn = XKB_KEY_NoSymbol; + return true; + } + + if (istreq(name, "none") || istreq(name, "voidsymbol")) { + *sym_rtrn = XKB_KEY_VoidSymbol; + return true; + } + + sym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); + if (sym != XKB_KEY_NoSymbol) { + *sym_rtrn = sym; + return true; + } + + return false; +} + +#define param_scanner param->scanner + +#line 130 "xkbcommon@sha/parser.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +# define YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int _xkbcommon_debug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + END_OF_FILE = 0, /* END_OF_FILE */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + ERROR_TOK = 255, /* ERROR_TOK */ + XKB_KEYMAP = 1, /* XKB_KEYMAP */ + XKB_KEYCODES = 2, /* XKB_KEYCODES */ + XKB_TYPES = 3, /* XKB_TYPES */ + XKB_SYMBOLS = 4, /* XKB_SYMBOLS */ + XKB_COMPATMAP = 5, /* XKB_COMPATMAP */ + XKB_GEOMETRY = 6, /* XKB_GEOMETRY */ + XKB_SEMANTICS = 7, /* XKB_SEMANTICS */ + XKB_LAYOUT = 8, /* XKB_LAYOUT */ + INCLUDE = 10, /* INCLUDE */ + OVERRIDE = 11, /* OVERRIDE */ + AUGMENT = 12, /* AUGMENT */ + REPLACE = 13, /* REPLACE */ + ALTERNATE = 14, /* ALTERNATE */ + VIRTUAL_MODS = 20, /* VIRTUAL_MODS */ + TYPE = 21, /* TYPE */ + INTERPRET = 22, /* INTERPRET */ + ACTION_TOK = 23, /* ACTION_TOK */ + KEY = 24, /* KEY */ + ALIAS = 25, /* ALIAS */ + GROUP = 26, /* GROUP */ + MODIFIER_MAP = 27, /* MODIFIER_MAP */ + INDICATOR = 28, /* INDICATOR */ + SHAPE = 29, /* SHAPE */ + KEYS = 30, /* KEYS */ + ROW = 31, /* ROW */ + SECTION = 32, /* SECTION */ + OVERLAY = 33, /* OVERLAY */ + TEXT = 34, /* TEXT */ + OUTLINE = 35, /* OUTLINE */ + SOLID = 36, /* SOLID */ + LOGO = 37, /* LOGO */ + VIRTUAL = 38, /* VIRTUAL */ + EQUALS = 40, /* EQUALS */ + PLUS = 41, /* PLUS */ + MINUS = 42, /* MINUS */ + DIVIDE = 43, /* DIVIDE */ + TIMES = 44, /* TIMES */ + OBRACE = 45, /* OBRACE */ + CBRACE = 46, /* CBRACE */ + OPAREN = 47, /* OPAREN */ + CPAREN = 48, /* CPAREN */ + OBRACKET = 49, /* OBRACKET */ + CBRACKET = 50, /* CBRACKET */ + DOT = 51, /* DOT */ + COMMA = 52, /* COMMA */ + SEMI = 53, /* SEMI */ + EXCLAM = 54, /* EXCLAM */ + INVERT = 55, /* INVERT */ + STRING = 60, /* STRING */ + INTEGER = 61, /* INTEGER */ + FLOAT = 62, /* FLOAT */ + IDENT = 63, /* IDENT */ + KEYNAME = 64, /* KEYNAME */ + PARTIAL = 70, /* PARTIAL */ + DEFAULT = 71, /* DEFAULT */ + HIDDEN = 72, /* HIDDEN */ + ALPHANUMERIC_KEYS = 73, /* ALPHANUMERIC_KEYS */ + MODIFIER_KEYS = 74, /* MODIFIER_KEYS */ + KEYPAD_KEYS = 75, /* KEYPAD_KEYS */ + FUNCTION_KEYS = 76, /* FUNCTION_KEYS */ + ALTERNATE_GROUP = 77 /* ALTERNATE_GROUP */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 164 "../src/xkbcomp/parser.y" + + int64_t num; + enum xkb_file_type file_type; + char *str; + xkb_atom_t atom; + enum merge_mode merge; + enum xkb_map_flags mapFlags; + xkb_keysym_t keysym; + ParseCommon *any; + struct { ParseCommon *head; ParseCommon *last; } anyList; + ExprDef *expr; + struct { ExprDef *head; ExprDef *last; } exprList; + VarDef *var; + struct { VarDef *head; VarDef *last; } varList; + VModDef *vmod; + struct { VModDef *head; VModDef *last; } vmodList; + InterpDef *interp; + KeyTypeDef *keyType; + SymbolsDef *syms; + ModMapDef *modMask; + GroupCompatDef *groupCompat; + LedMapDef *ledMap; + LedNameDef *ledName; + KeycodeDef *keyCode; + KeyAliasDef *keyAlias; + void *geom; + XkbFile *file; + struct { XkbFile *head; XkbFile *last; } fileList; + +#line 274 "xkbcommon@sha/parser.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int _xkbcommon_parse (struct parser_param *param); + +#endif /* !YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED */ +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* END_OF_FILE */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_ERROR_TOK = 3, /* ERROR_TOK */ + YYSYMBOL_XKB_KEYMAP = 4, /* XKB_KEYMAP */ + YYSYMBOL_XKB_KEYCODES = 5, /* XKB_KEYCODES */ + YYSYMBOL_XKB_TYPES = 6, /* XKB_TYPES */ + YYSYMBOL_XKB_SYMBOLS = 7, /* XKB_SYMBOLS */ + YYSYMBOL_XKB_COMPATMAP = 8, /* XKB_COMPATMAP */ + YYSYMBOL_XKB_GEOMETRY = 9, /* XKB_GEOMETRY */ + YYSYMBOL_XKB_SEMANTICS = 10, /* XKB_SEMANTICS */ + YYSYMBOL_XKB_LAYOUT = 11, /* XKB_LAYOUT */ + YYSYMBOL_INCLUDE = 12, /* INCLUDE */ + YYSYMBOL_OVERRIDE = 13, /* OVERRIDE */ + YYSYMBOL_AUGMENT = 14, /* AUGMENT */ + YYSYMBOL_REPLACE = 15, /* REPLACE */ + YYSYMBOL_ALTERNATE = 16, /* ALTERNATE */ + YYSYMBOL_VIRTUAL_MODS = 17, /* VIRTUAL_MODS */ + YYSYMBOL_TYPE = 18, /* TYPE */ + YYSYMBOL_INTERPRET = 19, /* INTERPRET */ + YYSYMBOL_ACTION_TOK = 20, /* ACTION_TOK */ + YYSYMBOL_KEY = 21, /* KEY */ + YYSYMBOL_ALIAS = 22, /* ALIAS */ + YYSYMBOL_GROUP = 23, /* GROUP */ + YYSYMBOL_MODIFIER_MAP = 24, /* MODIFIER_MAP */ + YYSYMBOL_INDICATOR = 25, /* INDICATOR */ + YYSYMBOL_SHAPE = 26, /* SHAPE */ + YYSYMBOL_KEYS = 27, /* KEYS */ + YYSYMBOL_ROW = 28, /* ROW */ + YYSYMBOL_SECTION = 29, /* SECTION */ + YYSYMBOL_OVERLAY = 30, /* OVERLAY */ + YYSYMBOL_TEXT = 31, /* TEXT */ + YYSYMBOL_OUTLINE = 32, /* OUTLINE */ + YYSYMBOL_SOLID = 33, /* SOLID */ + YYSYMBOL_LOGO = 34, /* LOGO */ + YYSYMBOL_VIRTUAL = 35, /* VIRTUAL */ + YYSYMBOL_EQUALS = 36, /* EQUALS */ + YYSYMBOL_PLUS = 37, /* PLUS */ + YYSYMBOL_MINUS = 38, /* MINUS */ + YYSYMBOL_DIVIDE = 39, /* DIVIDE */ + YYSYMBOL_TIMES = 40, /* TIMES */ + YYSYMBOL_OBRACE = 41, /* OBRACE */ + YYSYMBOL_CBRACE = 42, /* CBRACE */ + YYSYMBOL_OPAREN = 43, /* OPAREN */ + YYSYMBOL_CPAREN = 44, /* CPAREN */ + YYSYMBOL_OBRACKET = 45, /* OBRACKET */ + YYSYMBOL_CBRACKET = 46, /* CBRACKET */ + YYSYMBOL_DOT = 47, /* DOT */ + YYSYMBOL_COMMA = 48, /* COMMA */ + YYSYMBOL_SEMI = 49, /* SEMI */ + YYSYMBOL_EXCLAM = 50, /* EXCLAM */ + YYSYMBOL_INVERT = 51, /* INVERT */ + YYSYMBOL_STRING = 52, /* STRING */ + YYSYMBOL_INTEGER = 53, /* INTEGER */ + YYSYMBOL_FLOAT = 54, /* FLOAT */ + YYSYMBOL_IDENT = 55, /* IDENT */ + YYSYMBOL_KEYNAME = 56, /* KEYNAME */ + YYSYMBOL_PARTIAL = 57, /* PARTIAL */ + YYSYMBOL_DEFAULT = 58, /* DEFAULT */ + YYSYMBOL_HIDDEN = 59, /* HIDDEN */ + YYSYMBOL_ALPHANUMERIC_KEYS = 60, /* ALPHANUMERIC_KEYS */ + YYSYMBOL_MODIFIER_KEYS = 61, /* MODIFIER_KEYS */ + YYSYMBOL_KEYPAD_KEYS = 62, /* KEYPAD_KEYS */ + YYSYMBOL_FUNCTION_KEYS = 63, /* FUNCTION_KEYS */ + YYSYMBOL_ALTERNATE_GROUP = 64, /* ALTERNATE_GROUP */ + YYSYMBOL_YYACCEPT = 65, /* $accept */ + YYSYMBOL_XkbFile = 66, /* XkbFile */ + YYSYMBOL_XkbCompositeMap = 67, /* XkbCompositeMap */ + YYSYMBOL_XkbCompositeType = 68, /* XkbCompositeType */ + YYSYMBOL_XkbMapConfigList = 69, /* XkbMapConfigList */ + YYSYMBOL_XkbMapConfig = 70, /* XkbMapConfig */ + YYSYMBOL_FileType = 71, /* FileType */ + YYSYMBOL_OptFlags = 72, /* OptFlags */ + YYSYMBOL_Flags = 73, /* Flags */ + YYSYMBOL_Flag = 74, /* Flag */ + YYSYMBOL_DeclList = 75, /* DeclList */ + YYSYMBOL_Decl = 76, /* Decl */ + YYSYMBOL_VarDecl = 77, /* VarDecl */ + YYSYMBOL_KeyNameDecl = 78, /* KeyNameDecl */ + YYSYMBOL_KeyAliasDecl = 79, /* KeyAliasDecl */ + YYSYMBOL_VModDecl = 80, /* VModDecl */ + YYSYMBOL_VModDefList = 81, /* VModDefList */ + YYSYMBOL_VModDef = 82, /* VModDef */ + YYSYMBOL_InterpretDecl = 83, /* InterpretDecl */ + YYSYMBOL_InterpretMatch = 84, /* InterpretMatch */ + YYSYMBOL_VarDeclList = 85, /* VarDeclList */ + YYSYMBOL_KeyTypeDecl = 86, /* KeyTypeDecl */ + YYSYMBOL_SymbolsDecl = 87, /* SymbolsDecl */ + YYSYMBOL_SymbolsBody = 88, /* SymbolsBody */ + YYSYMBOL_SymbolsVarDecl = 89, /* SymbolsVarDecl */ + YYSYMBOL_ArrayInit = 90, /* ArrayInit */ + YYSYMBOL_GroupCompatDecl = 91, /* GroupCompatDecl */ + YYSYMBOL_ModMapDecl = 92, /* ModMapDecl */ + YYSYMBOL_LedMapDecl = 93, /* LedMapDecl */ + YYSYMBOL_LedNameDecl = 94, /* LedNameDecl */ + YYSYMBOL_ShapeDecl = 95, /* ShapeDecl */ + YYSYMBOL_SectionDecl = 96, /* SectionDecl */ + YYSYMBOL_SectionBody = 97, /* SectionBody */ + YYSYMBOL_SectionBodyItem = 98, /* SectionBodyItem */ + YYSYMBOL_RowBody = 99, /* RowBody */ + YYSYMBOL_RowBodyItem = 100, /* RowBodyItem */ + YYSYMBOL_Keys = 101, /* Keys */ + YYSYMBOL_Key = 102, /* Key */ + YYSYMBOL_OverlayDecl = 103, /* OverlayDecl */ + YYSYMBOL_OverlayKeyList = 104, /* OverlayKeyList */ + YYSYMBOL_OverlayKey = 105, /* OverlayKey */ + YYSYMBOL_OutlineList = 106, /* OutlineList */ + YYSYMBOL_OutlineInList = 107, /* OutlineInList */ + YYSYMBOL_CoordList = 108, /* CoordList */ + YYSYMBOL_Coord = 109, /* Coord */ + YYSYMBOL_DoodadDecl = 110, /* DoodadDecl */ + YYSYMBOL_DoodadType = 111, /* DoodadType */ + YYSYMBOL_FieldSpec = 112, /* FieldSpec */ + YYSYMBOL_Element = 113, /* Element */ + YYSYMBOL_OptMergeMode = 114, /* OptMergeMode */ + YYSYMBOL_MergeMode = 115, /* MergeMode */ + YYSYMBOL_OptExprList = 116, /* OptExprList */ + YYSYMBOL_ExprList = 117, /* ExprList */ + YYSYMBOL_Expr = 118, /* Expr */ + YYSYMBOL_Term = 119, /* Term */ + YYSYMBOL_ActionList = 120, /* ActionList */ + YYSYMBOL_Action = 121, /* Action */ + YYSYMBOL_Lhs = 122, /* Lhs */ + YYSYMBOL_Terminal = 123, /* Terminal */ + YYSYMBOL_OptKeySymList = 124, /* OptKeySymList */ + YYSYMBOL_KeySymList = 125, /* KeySymList */ + YYSYMBOL_KeySyms = 126, /* KeySyms */ + YYSYMBOL_KeySym = 127, /* KeySym */ + YYSYMBOL_SignedNumber = 128, /* SignedNumber */ + YYSYMBOL_Number = 129, /* Number */ + YYSYMBOL_Float = 130, /* Float */ + YYSYMBOL_Integer = 131, /* Integer */ + YYSYMBOL_KeyCode = 132, /* KeyCode */ + YYSYMBOL_Ident = 133, /* Ident */ + YYSYMBOL_String = 134, /* String */ + YYSYMBOL_OptMapName = 135, /* OptMapName */ + YYSYMBOL_MapName = 136 /* MapName */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 16 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 735 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 65 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 72 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 184 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 334 + +#define YYMAXUTOK 257 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 4, 5, 6, 7, 8, 9, 10, 11, 2, + 12, 13, 14, 15, 16, 2, 2, 2, 2, 2, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 2, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 2, 2, 2, 2, + 52, 53, 54, 55, 56, 2, 2, 2, 2, 2, + 57, 58, 59, 60, 61, 62, 63, 64, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 1, 2 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 254, 254, 256, 258, 262, 268, 269, 270, 273, + 275, 279, 287, 288, 289, 290, 291, 294, 295, 298, + 299, 302, 303, 304, 305, 306, 307, 308, 309, 312, + 327, 337, 340, 346, 351, 356, 361, 366, 371, 376, + 381, 386, 391, 392, 393, 394, 401, 403, 405, 409, + 413, 417, 421, 423, 427, 429, 433, 439, 441, 445, + 447, 451, 457, 463, 465, 467, 470, 471, 472, 473, + 474, 477, 479, 483, 487, 491, 495, 497, 501, 503, + 507, 511, 512, 515, 517, 519, 521, 523, 527, 528, + 531, 532, 536, 537, 540, 542, 546, 550, 551, 554, + 557, 559, 563, 565, 567, 571, 573, 577, 581, 585, + 586, 587, 588, 591, 592, 595, 597, 599, 601, 603, + 605, 607, 609, 611, 613, 615, 619, 620, 623, 624, + 625, 626, 627, 637, 638, 641, 643, 647, 649, 651, + 653, 655, 657, 661, 663, 665, 667, 669, 671, 673, + 675, 679, 681, 685, 689, 691, 693, 695, 699, 701, + 703, 705, 709, 710, 713, 715, 717, 719, 723, 727, + 733, 734, 754, 755, 758, 759, 762, 765, 768, 771, + 772, 775, 778, 779, 782 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "END_OF_FILE", "error", "\"invalid token\"", "ERROR_TOK", "XKB_KEYMAP", + "XKB_KEYCODES", "XKB_TYPES", "XKB_SYMBOLS", "XKB_COMPATMAP", + "XKB_GEOMETRY", "XKB_SEMANTICS", "XKB_LAYOUT", "INCLUDE", "OVERRIDE", + "AUGMENT", "REPLACE", "ALTERNATE", "VIRTUAL_MODS", "TYPE", "INTERPRET", + "ACTION_TOK", "KEY", "ALIAS", "GROUP", "MODIFIER_MAP", "INDICATOR", + "SHAPE", "KEYS", "ROW", "SECTION", "OVERLAY", "TEXT", "OUTLINE", "SOLID", + "LOGO", "VIRTUAL", "EQUALS", "PLUS", "MINUS", "DIVIDE", "TIMES", + "OBRACE", "CBRACE", "OPAREN", "CPAREN", "OBRACKET", "CBRACKET", "DOT", + "COMMA", "SEMI", "EXCLAM", "INVERT", "STRING", "INTEGER", "FLOAT", + "IDENT", "KEYNAME", "PARTIAL", "DEFAULT", "HIDDEN", "ALPHANUMERIC_KEYS", + "MODIFIER_KEYS", "KEYPAD_KEYS", "FUNCTION_KEYS", "ALTERNATE_GROUP", + "$accept", "XkbFile", "XkbCompositeMap", "XkbCompositeType", + "XkbMapConfigList", "XkbMapConfig", "FileType", "OptFlags", "Flags", + "Flag", "DeclList", "Decl", "VarDecl", "KeyNameDecl", "KeyAliasDecl", + "VModDecl", "VModDefList", "VModDef", "InterpretDecl", "InterpretMatch", + "VarDeclList", "KeyTypeDecl", "SymbolsDecl", "SymbolsBody", + "SymbolsVarDecl", "ArrayInit", "GroupCompatDecl", "ModMapDecl", + "LedMapDecl", "LedNameDecl", "ShapeDecl", "SectionDecl", "SectionBody", + "SectionBodyItem", "RowBody", "RowBodyItem", "Keys", "Key", + "OverlayDecl", "OverlayKeyList", "OverlayKey", "OutlineList", + "OutlineInList", "CoordList", "Coord", "DoodadDecl", "DoodadType", + "FieldSpec", "Element", "OptMergeMode", "MergeMode", "OptExprList", + "ExprList", "Expr", "Term", "ActionList", "Action", "Lhs", "Terminal", + "OptKeySymList", "KeySymList", "KeySyms", "KeySym", "SignedNumber", + "Number", "Float", "Integer", "KeyCode", "Ident", "String", "OptMapName", + "MapName", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 255, 1, 2, 3, 4, 5, 6, + 7, 8, 10, 11, 12, 13, 14, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 60, 61, 62, 63, 64, 70, 71, 72, + 73, 74, 75, 76, 77 +}; +#endif + +#define YYPACT_NINF (-182) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-180) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 176, -182, -182, -182, -182, -182, -182, -182, -182, -182, + 6, -182, -182, 271, 227, -182, -182, -182, -182, -182, + -182, -182, -182, -182, -182, -38, -38, -182, -182, -24, + -182, 33, 227, -182, 210, -182, 353, 44, 5, -182, + -182, -182, -182, -182, -182, 32, -182, 13, 41, -182, + -182, -48, 55, 11, -182, 79, 87, 58, -48, -2, + 55, -182, 55, 72, -182, -182, -182, 107, -48, -182, + 110, -182, -182, -182, -182, -182, -182, -182, -182, -182, + -182, -182, -182, -182, -182, -182, 55, -18, -182, 127, + 121, -182, 66, -182, 138, -182, 136, -182, -182, -182, + 144, 147, -182, 152, 180, 182, 178, 184, 187, 188, + 190, 58, 198, 201, 214, 367, 677, 367, -182, -48, + -182, 367, 663, 663, 367, 494, 200, 367, 367, 367, + 663, 68, 449, 223, -182, -182, 212, 663, -182, -182, + -182, -182, -182, -182, -182, -182, -182, 367, 367, 367, + 367, 367, -182, -182, 57, 157, -182, 224, -182, -182, + -182, -182, -182, 218, 91, -182, 333, -182, 509, 537, + 333, 552, -48, 1, -182, -182, 228, 40, 216, 143, + 70, 333, 150, 593, 247, -30, 97, -182, 105, -182, + 261, 55, 259, 55, -182, -182, 408, -182, -182, -182, + 367, -182, 608, -182, -182, -182, 287, -182, -182, 367, + 367, 367, 367, 367, -182, 367, 367, -182, 252, -182, + 253, 264, 24, 269, 272, 163, -182, 273, 270, -182, + -182, -182, 280, 494, 285, -182, -182, 283, 367, -182, + 284, 112, 8, -182, -182, 294, -182, 299, -36, 304, + 247, 326, 649, 279, 307, -182, 204, 316, -182, 322, + 320, 111, 111, -182, -182, 333, 211, -182, -182, 116, + 367, -182, 677, -182, 24, -182, -182, -182, 333, -182, + 333, -182, -182, -182, -30, -182, -182, -182, -182, 247, + 333, 334, -182, 466, -182, 318, -182, -182, -182, -182, + -182, -182, 339, -182, -182, -182, 343, 120, 14, 345, + -182, 361, 124, -182, -182, -182, -182, 367, -182, 131, + -182, -182, 344, 350, 318, 166, 352, 14, -182, -182, + -182, -182, -182, -182 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 18, 4, 21, 22, 23, 24, 25, 26, 27, 28, + 0, 2, 3, 0, 17, 20, 1, 6, 12, 13, + 15, 14, 16, 7, 8, 183, 183, 19, 184, 0, + 182, 0, 18, 31, 18, 10, 0, 127, 0, 9, + 128, 130, 129, 131, 132, 0, 29, 0, 126, 5, + 11, 0, 117, 116, 115, 118, 0, 119, 120, 121, + 122, 123, 124, 125, 110, 111, 112, 0, 0, 179, + 0, 180, 32, 34, 35, 30, 33, 36, 37, 39, + 38, 40, 41, 42, 43, 44, 0, 154, 114, 0, + 113, 45, 0, 53, 54, 181, 0, 170, 177, 169, + 0, 58, 171, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, + 51, 0, 0, 0, 0, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 48, 178, 0, 0, 117, 116, + 118, 119, 120, 121, 122, 124, 125, 0, 0, 0, + 0, 0, 176, 161, 154, 0, 142, 147, 149, 160, + 159, 113, 158, 155, 0, 52, 55, 60, 0, 0, + 57, 163, 0, 0, 64, 70, 0, 113, 0, 0, + 0, 136, 0, 0, 0, 0, 0, 101, 0, 106, + 0, 121, 123, 0, 84, 86, 0, 82, 87, 85, + 0, 49, 0, 144, 147, 143, 0, 145, 146, 134, + 0, 0, 0, 0, 156, 0, 0, 46, 0, 59, + 0, 170, 0, 169, 0, 0, 152, 0, 162, 167, + 166, 69, 0, 0, 0, 50, 73, 0, 0, 76, + 0, 0, 0, 175, 174, 0, 173, 0, 0, 0, + 0, 0, 0, 0, 0, 81, 0, 0, 150, 0, + 133, 138, 139, 137, 140, 141, 0, 61, 56, 0, + 134, 72, 0, 71, 0, 62, 63, 67, 66, 74, + 135, 75, 102, 172, 0, 78, 100, 79, 105, 0, + 104, 0, 91, 0, 89, 0, 80, 77, 108, 148, + 157, 168, 0, 151, 165, 164, 0, 0, 0, 0, + 88, 0, 0, 98, 153, 107, 103, 0, 94, 0, + 93, 83, 0, 0, 0, 0, 0, 0, 99, 96, + 97, 95, 90, 92 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -182, -182, -182, -182, -182, 181, -182, 402, -182, 389, + -182, -182, -35, -182, -182, -182, -182, 288, -182, -182, + -50, -182, -182, -182, 173, 174, -182, -182, 362, -182, + -182, -182, -182, 215, -182, 119, -182, 86, -182, -182, + 90, -182, 167, -181, 185, 369, -182, -27, -182, -182, + -182, 154, -126, 83, 76, -182, 158, -31, -182, -182, + 221, 170, -52, 161, 205, -182, -44, -182, -47, -34, + 420, -182 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 10, 11, 25, 34, 12, 26, 36, 14, 15, + 37, 46, 167, 73, 74, 75, 92, 93, 76, 100, + 168, 77, 78, 173, 174, 175, 79, 80, 195, 82, + 83, 84, 196, 197, 293, 294, 319, 320, 198, 312, + 313, 186, 187, 188, 189, 199, 86, 154, 88, 47, + 48, 259, 260, 181, 156, 225, 226, 157, 158, 227, + 228, 229, 230, 245, 246, 159, 160, 136, 161, 162, + 29, 30 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 90, 101, 180, 241, 94, 184, 16, 69, 242, 102, + 71, 106, 72, 105, 28, 107, 89, 32, 96, 69, + 87, 112, 71, 243, 244, 108, 109, 115, 110, 116, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 97, 61, 62, 232, 63, 64, 65, 66, 67, 233, + 95, 98, 114, 97, 49, 317, 40, 41, 42, 43, + 44, 243, 244, 68, 98, 222, 99, 133, 69, 70, + 318, 71, 94, 169, 33, 90, 90, 98, 177, 99, + 183, 50, -68, 90, 190, 90, 45, 202, -68, 163, + 90, 89, 89, 91, 176, 87, 87, 194, 87, 89, + 209, 89, 115, 87, 116, 87, 89, 95, 307, 184, + 87, 98, 237, 185, 119, 120, 204, 204, 238, 204, + 204, 90, 90, 69, -109, 231, 71, 102, 210, 211, + 212, 213, 111, 219, 219, 103, 90, 89, 89, 247, + 217, 87, 87, 104, 224, 248, 113, 249, 219, 90, + 212, 213, 89, 250, 282, 90, 87, 108, 301, 253, + 250, 194, 316, 117, 274, 89, 323, 219, 250, 87, + 118, 89, 324, 326, 121, 87, 1, 122, 102, 327, + 210, 211, 212, 213, 124, 123, 177, 210, 211, 212, + 213, 325, 236, 125, 210, 211, 212, 213, 155, 239, + 164, 190, 176, 214, 166, 90, 87, 170, 331, 271, + 179, 272, 182, 35, 238, 39, 126, 292, 127, 128, + 129, 89, 305, 203, 205, 87, 207, 208, 130, 131, + 102, 132, 206, 2, 3, 4, 5, 6, 7, 8, + 9, 210, 211, 212, 213, 224, 90, 134, 210, 211, + 212, 213, 38, 297, 135, 137, 178, 300, 292, 200, + 215, 201, 89, 216, 234, 235, 87, 2, 3, 4, + 5, 6, 7, 8, 9, 17, 18, 19, 20, 21, + 22, 23, 24, 256, 2, 3, 4, 5, 6, 7, + 8, 9, 185, 261, 262, 263, 264, 251, 265, 266, + 252, 267, 268, 138, 139, 54, 140, -124, 141, 142, + 143, 144, -179, 61, 145, 270, 146, 278, 274, 273, + 295, 280, 147, 148, 210, 211, 212, 213, 149, 275, + 171, 258, 279, 281, 290, 150, 151, 95, 98, 152, + 69, 153, 284, 71, 138, 139, 54, 140, 285, 141, + 142, 143, 144, 287, 61, 145, 296, 146, 18, 19, + 20, 21, 22, 147, 148, 298, 299, 289, 238, 149, + 210, 211, 212, 213, 311, 308, 150, 151, 95, 98, + 152, 69, 153, 314, 71, 138, 139, 54, 140, 315, + 141, 142, 143, 144, 321, 61, 145, 322, 146, 329, + 328, 332, 13, 27, 147, 148, 276, 165, 277, 81, + 149, 255, 310, 333, 330, 286, 85, 150, 151, 95, + 98, 152, 69, 153, 302, 71, 138, 139, 54, 140, + 303, 141, 142, 191, 144, 288, 192, 145, 193, 63, + 64, 65, 66, 269, 304, 306, 31, 283, 0, 0, + 254, 0, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 0, 0, 69, 0, 0, 71, 138, 139, 54, + 140, 0, 141, 142, 191, 144, 0, 192, 145, 193, + 63, 64, 65, 66, 138, 139, 54, 140, 0, 141, + 142, 143, 144, 291, 61, 145, 0, 146, 0, 68, + 0, 0, 0, 0, 69, 0, 0, 71, 309, 0, + 0, 0, 138, 139, 54, 140, 68, 141, 142, 143, + 144, 69, 61, 145, 71, 146, 0, 138, 139, 54, + 140, 0, 141, 142, 143, 144, 0, 61, 145, 171, + 146, 0, 0, 0, 172, 0, 0, 0, 0, 69, + 0, 218, 71, 0, 0, 138, 139, 54, 140, 68, + 141, 142, 143, 144, 69, 61, 145, 71, 146, 0, + 138, 139, 54, 140, 0, 141, 142, 143, 144, 220, + 61, 221, 0, 146, 0, 0, 0, 68, 0, 0, + 0, 0, 69, 222, 0, 71, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 98, 0, 223, 0, 0, + 71, 138, 139, 54, 140, 0, 141, 142, 143, 144, + 0, 61, 145, 0, 146, 0, 138, 139, 54, 140, + 0, 141, 142, 143, 144, 240, 61, 145, 0, 146, + 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, + 257, 71, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 0, 0, 69, 0, 0, 71, 138, 139, 54, + 140, 0, 141, 142, 143, 144, 291, 61, 145, 0, + 146, 138, 139, 54, 140, 0, 141, 142, 143, 144, + 0, 61, 145, 0, 146, 138, 139, 54, 140, 68, + 141, 142, 143, 144, 69, 61, 145, 71, 146, 0, + 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, + 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 69, 0, 0, 71 +}; + +static const yytype_int16 yycheck[] = +{ + 47, 53, 128, 184, 51, 41, 0, 55, 38, 53, + 58, 58, 47, 57, 52, 59, 47, 41, 52, 55, + 47, 68, 58, 53, 54, 59, 60, 45, 62, 47, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 29, 28, 29, 42, 31, 32, 33, 34, 35, 48, + 52, 53, 86, 29, 49, 41, 12, 13, 14, 15, + 16, 53, 54, 50, 53, 41, 55, 111, 55, 56, + 56, 58, 119, 123, 41, 122, 123, 53, 125, 55, + 130, 49, 42, 130, 131, 132, 42, 137, 48, 116, + 137, 122, 123, 52, 125, 122, 123, 132, 125, 130, + 43, 132, 45, 130, 47, 132, 137, 52, 289, 41, + 137, 53, 42, 45, 48, 49, 147, 148, 48, 150, + 151, 168, 169, 55, 52, 172, 58, 171, 37, 38, + 39, 40, 25, 168, 169, 56, 183, 168, 169, 42, + 49, 168, 169, 56, 171, 48, 36, 42, 183, 196, + 39, 40, 183, 48, 42, 202, 183, 191, 42, 193, + 48, 196, 42, 36, 48, 196, 42, 202, 48, 196, + 49, 202, 48, 42, 36, 202, 0, 41, 222, 48, + 37, 38, 39, 40, 37, 41, 233, 37, 38, 39, + 40, 317, 49, 41, 37, 38, 39, 40, 115, 49, + 117, 248, 233, 46, 121, 252, 233, 124, 42, 46, + 127, 48, 129, 32, 48, 34, 36, 252, 36, 41, + 36, 252, 274, 147, 148, 252, 150, 151, 41, 41, + 274, 41, 149, 57, 58, 59, 60, 61, 62, 63, + 64, 37, 38, 39, 40, 272, 293, 49, 37, 38, + 39, 40, 42, 49, 53, 41, 56, 46, 293, 36, + 36, 49, 293, 45, 36, 49, 293, 57, 58, 59, + 60, 61, 62, 63, 64, 4, 5, 6, 7, 8, + 9, 10, 11, 200, 57, 58, 59, 60, 61, 62, + 63, 64, 45, 210, 211, 212, 213, 36, 215, 216, + 41, 49, 49, 18, 19, 20, 21, 43, 23, 24, + 25, 26, 43, 28, 29, 43, 31, 234, 48, 46, + 41, 238, 37, 38, 37, 38, 39, 40, 43, 49, + 45, 44, 49, 49, 251, 50, 51, 52, 53, 54, + 55, 56, 48, 58, 18, 19, 20, 21, 49, 23, + 24, 25, 26, 49, 28, 29, 49, 31, 5, 6, + 7, 8, 9, 37, 38, 49, 44, 41, 48, 43, + 37, 38, 39, 40, 56, 41, 50, 51, 52, 53, + 54, 55, 56, 44, 58, 18, 19, 20, 21, 46, + 23, 24, 25, 26, 49, 28, 29, 36, 31, 49, + 56, 49, 0, 14, 37, 38, 233, 119, 234, 47, + 43, 196, 293, 327, 324, 248, 47, 50, 51, 52, + 53, 54, 55, 56, 270, 58, 18, 19, 20, 21, + 272, 23, 24, 25, 26, 250, 28, 29, 30, 31, + 32, 33, 34, 222, 274, 284, 26, 242, -1, -1, + 42, -1, -1, -1, -1, -1, -1, -1, 50, -1, + -1, -1, -1, 55, -1, -1, 58, 18, 19, 20, + 21, -1, 23, 24, 25, 26, -1, 28, 29, 30, + 31, 32, 33, 34, 18, 19, 20, 21, -1, 23, + 24, 25, 26, 27, 28, 29, -1, 31, -1, 50, + -1, -1, -1, -1, 55, -1, -1, 58, 42, -1, + -1, -1, 18, 19, 20, 21, 50, 23, 24, 25, + 26, 55, 28, 29, 58, 31, -1, 18, 19, 20, + 21, -1, 23, 24, 25, 26, -1, 28, 29, 45, + 31, -1, -1, -1, 50, -1, -1, -1, -1, 55, + -1, 42, 58, -1, -1, 18, 19, 20, 21, 50, + 23, 24, 25, 26, 55, 28, 29, 58, 31, -1, + 18, 19, 20, 21, -1, 23, 24, 25, 26, 42, + 28, 29, -1, 31, -1, -1, -1, 50, -1, -1, + -1, -1, 55, 41, -1, 58, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 53, -1, 55, -1, -1, + 58, 18, 19, 20, 21, -1, 23, 24, 25, 26, + -1, 28, 29, -1, 31, -1, 18, 19, 20, 21, + -1, 23, 24, 25, 26, 42, 28, 29, -1, 31, + -1, -1, -1, 50, -1, -1, -1, -1, 55, -1, + 42, 58, -1, -1, -1, -1, -1, -1, 50, -1, + -1, -1, -1, 55, -1, -1, 58, 18, 19, 20, + 21, -1, 23, 24, 25, 26, 27, 28, 29, -1, + 31, 18, 19, 20, 21, -1, 23, 24, 25, 26, + -1, 28, 29, -1, 31, 18, 19, 20, 21, 50, + 23, 24, 25, 26, 55, 28, 29, 58, 31, -1, + -1, -1, -1, 50, -1, -1, -1, -1, 55, -1, + -1, 58, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 55, -1, -1, 58 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 0, 57, 58, 59, 60, 61, 62, 63, 64, + 66, 67, 70, 72, 73, 74, 0, 4, 5, 6, + 7, 8, 9, 10, 11, 68, 71, 74, 52, 135, + 136, 135, 41, 41, 69, 70, 72, 75, 42, 70, + 12, 13, 14, 15, 16, 42, 76, 114, 115, 49, + 49, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 28, 29, 31, 32, 33, 34, 35, 50, 55, + 56, 58, 77, 78, 79, 80, 83, 86, 87, 91, + 92, 93, 94, 95, 96, 110, 111, 112, 113, 122, + 133, 52, 81, 82, 133, 52, 134, 29, 53, 55, + 84, 127, 131, 56, 56, 131, 133, 131, 134, 134, + 134, 25, 133, 36, 134, 45, 47, 36, 49, 48, + 49, 36, 41, 41, 37, 41, 36, 36, 41, 36, + 41, 41, 41, 131, 49, 53, 132, 41, 18, 19, + 21, 23, 24, 25, 26, 29, 31, 37, 38, 43, + 50, 51, 54, 56, 112, 118, 119, 122, 123, 130, + 131, 133, 134, 112, 118, 82, 118, 77, 85, 85, + 118, 45, 50, 88, 89, 90, 122, 133, 56, 118, + 117, 118, 118, 85, 41, 45, 106, 107, 108, 109, + 133, 25, 28, 30, 77, 93, 97, 98, 103, 110, + 36, 49, 85, 119, 122, 119, 118, 119, 119, 43, + 37, 38, 39, 40, 46, 36, 45, 49, 42, 77, + 42, 29, 41, 55, 112, 120, 121, 124, 125, 126, + 127, 133, 42, 48, 36, 49, 49, 42, 48, 49, + 42, 108, 38, 53, 54, 128, 129, 42, 48, 42, + 48, 36, 41, 134, 42, 98, 118, 42, 44, 116, + 117, 118, 118, 118, 118, 118, 118, 49, 49, 125, + 43, 46, 48, 46, 48, 49, 89, 90, 118, 49, + 118, 49, 42, 129, 48, 49, 107, 49, 109, 41, + 118, 27, 77, 99, 100, 41, 49, 49, 49, 44, + 46, 42, 116, 121, 126, 127, 128, 108, 41, 42, + 100, 56, 104, 105, 44, 46, 42, 41, 56, 101, + 102, 49, 36, 42, 48, 117, 42, 48, 56, 49, + 105, 42, 49, 102 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 65, 66, 66, 66, 67, 68, 68, 68, 69, + 69, 70, 71, 71, 71, 71, 71, 72, 72, 73, + 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, + 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 77, 77, 77, 78, + 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, + 85, 86, 87, 88, 88, 88, 89, 89, 89, 89, + 89, 90, 90, 91, 92, 93, 94, 94, 95, 95, + 96, 97, 97, 98, 98, 98, 98, 98, 99, 99, + 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, + 106, 106, 107, 107, 107, 108, 108, 109, 110, 111, + 111, 111, 111, 112, 112, 113, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 114, 114, 115, 115, + 115, 115, 115, 116, 116, 117, 117, 118, 118, 118, + 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, + 119, 120, 120, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 125, 125, 125, 125, 126, 127, + 127, 127, 128, 128, 129, 129, 130, 131, 132, 133, + 133, 134, 135, 135, 136 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 1, 7, 1, 1, 1, 2, + 1, 7, 1, 1, 1, 1, 1, 1, 0, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 3, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 4, 2, 3, 4, + 5, 3, 3, 1, 1, 3, 6, 3, 1, 2, + 1, 6, 6, 3, 1, 0, 3, 3, 1, 2, + 1, 3, 3, 5, 6, 6, 5, 6, 6, 6, + 6, 2, 1, 5, 1, 1, 1, 1, 2, 1, + 5, 1, 3, 1, 1, 3, 6, 3, 1, 3, + 3, 1, 3, 5, 3, 3, 1, 5, 6, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 0, 3, 1, 3, 3, 3, + 3, 3, 1, 2, 2, 2, 2, 1, 4, 1, + 3, 3, 1, 4, 1, 3, 4, 6, 1, 1, + 1, 1, 1, 0, 3, 3, 1, 1, 3, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (param, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, param); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct parser_param *param) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (param); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct parser_param *param) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, param); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, struct parser_param *param) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], param); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, param); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, struct parser_param *param) +{ + YYUSE (yyvaluep); + YYUSE (param); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yykind) + { + case 52: /* STRING */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1453 "xkbcommon@sha/parser.c" + break; + + case 55: /* IDENT */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1459 "xkbcommon@sha/parser.c" + break; + + case 66: /* XkbFile */ +#line 236 "../src/xkbcomp/parser.y" + { if (!param->rtrn) FreeXkbFile(((*yyvaluep).file)); } +#line 1465 "xkbcommon@sha/parser.c" + break; + + case 67: /* XkbCompositeMap */ +#line 236 "../src/xkbcomp/parser.y" + { if (!param->rtrn) FreeXkbFile(((*yyvaluep).file)); } +#line 1471 "xkbcommon@sha/parser.c" + break; + + case 69: /* XkbMapConfigList */ +#line 237 "../src/xkbcomp/parser.y" + { FreeXkbFile(((*yyvaluep).fileList).head); } +#line 1477 "xkbcommon@sha/parser.c" + break; + + case 70: /* XkbMapConfig */ +#line 236 "../src/xkbcomp/parser.y" + { if (!param->rtrn) FreeXkbFile(((*yyvaluep).file)); } +#line 1483 "xkbcommon@sha/parser.c" + break; + + case 75: /* DeclList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).anyList).head); } +#line 1489 "xkbcommon@sha/parser.c" + break; + + case 76: /* Decl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).any)); } +#line 1495 "xkbcommon@sha/parser.c" + break; + + case 77: /* VarDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).var)); } +#line 1501 "xkbcommon@sha/parser.c" + break; + + case 78: /* KeyNameDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).keyCode)); } +#line 1507 "xkbcommon@sha/parser.c" + break; + + case 79: /* KeyAliasDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).keyAlias)); } +#line 1513 "xkbcommon@sha/parser.c" + break; + + case 80: /* VModDecl */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).vmodList).head); } +#line 1519 "xkbcommon@sha/parser.c" + break; + + case 81: /* VModDefList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).vmodList).head); } +#line 1525 "xkbcommon@sha/parser.c" + break; + + case 82: /* VModDef */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).vmod)); } +#line 1531 "xkbcommon@sha/parser.c" + break; + + case 83: /* InterpretDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).interp)); } +#line 1537 "xkbcommon@sha/parser.c" + break; + + case 84: /* InterpretMatch */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).interp)); } +#line 1543 "xkbcommon@sha/parser.c" + break; + + case 85: /* VarDeclList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).varList).head); } +#line 1549 "xkbcommon@sha/parser.c" + break; + + case 86: /* KeyTypeDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).keyType)); } +#line 1555 "xkbcommon@sha/parser.c" + break; + + case 87: /* SymbolsDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).syms)); } +#line 1561 "xkbcommon@sha/parser.c" + break; + + case 88: /* SymbolsBody */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).varList).head); } +#line 1567 "xkbcommon@sha/parser.c" + break; + + case 89: /* SymbolsVarDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).var)); } +#line 1573 "xkbcommon@sha/parser.c" + break; + + case 90: /* ArrayInit */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1579 "xkbcommon@sha/parser.c" + break; + + case 91: /* GroupCompatDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).groupCompat)); } +#line 1585 "xkbcommon@sha/parser.c" + break; + + case 92: /* ModMapDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).modMask)); } +#line 1591 "xkbcommon@sha/parser.c" + break; + + case 93: /* LedMapDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).ledMap)); } +#line 1597 "xkbcommon@sha/parser.c" + break; + + case 94: /* LedNameDecl */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).ledName)); } +#line 1603 "xkbcommon@sha/parser.c" + break; + + case 108: /* CoordList */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1609 "xkbcommon@sha/parser.c" + break; + + case 109: /* Coord */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1615 "xkbcommon@sha/parser.c" + break; + + case 116: /* OptExprList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).exprList).head); } +#line 1621 "xkbcommon@sha/parser.c" + break; + + case 117: /* ExprList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).exprList).head); } +#line 1627 "xkbcommon@sha/parser.c" + break; + + case 118: /* Expr */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1633 "xkbcommon@sha/parser.c" + break; + + case 119: /* Term */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1639 "xkbcommon@sha/parser.c" + break; + + case 120: /* ActionList */ +#line 232 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).exprList).head); } +#line 1645 "xkbcommon@sha/parser.c" + break; + + case 121: /* Action */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1651 "xkbcommon@sha/parser.c" + break; + + case 122: /* Lhs */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1657 "xkbcommon@sha/parser.c" + break; + + case 123: /* Terminal */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1663 "xkbcommon@sha/parser.c" + break; + + case 124: /* OptKeySymList */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1669 "xkbcommon@sha/parser.c" + break; + + case 125: /* KeySymList */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1675 "xkbcommon@sha/parser.c" + break; + + case 126: /* KeySyms */ +#line 229 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) ((*yyvaluep).expr)); } +#line 1681 "xkbcommon@sha/parser.c" + break; + + case 135: /* OptMapName */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1687 "xkbcommon@sha/parser.c" + break; + + case 136: /* MapName */ +#line 238 "../src/xkbcomp/parser.y" + { free(((*yyvaluep).str)); } +#line 1693 "xkbcommon@sha/parser.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (struct parser_param *param) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize; + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yynerrs = 0; + yystate = 0; + yyerrstatus = 0; + + yystacksize = YYINITDEPTH; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, param_scanner); + } + + if (yychar <= END_OF_FILE) + { + yychar = END_OF_FILE; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 255 "../src/xkbcomp/parser.y" + { (yyval.file) = param->rtrn = (yyvsp[0].file); param->more_maps = !!param->rtrn; } +#line 1980 "xkbcommon@sha/parser.c" + break; + + case 3: +#line 257 "../src/xkbcomp/parser.y" + { (yyval.file) = param->rtrn = (yyvsp[0].file); param->more_maps = !!param->rtrn; YYACCEPT; } +#line 1986 "xkbcommon@sha/parser.c" + break; + + case 4: +#line 259 "../src/xkbcomp/parser.y" + { (yyval.file) = param->rtrn = NULL; param->more_maps = false; } +#line 1992 "xkbcommon@sha/parser.c" + break; + + case 5: +#line 265 "../src/xkbcomp/parser.y" + { (yyval.file) = XkbFileCreate((yyvsp[-5].file_type), (yyvsp[-4].str), (ParseCommon *) (yyvsp[-2].fileList).head, (yyvsp[-6].mapFlags)); } +#line 1998 "xkbcommon@sha/parser.c" + break; + + case 6: +#line 268 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYMAP; } +#line 2004 "xkbcommon@sha/parser.c" + break; + + case 7: +#line 269 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYMAP; } +#line 2010 "xkbcommon@sha/parser.c" + break; + + case 8: +#line 270 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYMAP; } +#line 2016 "xkbcommon@sha/parser.c" + break; + + case 9: +#line 274 "../src/xkbcomp/parser.y" + { (yyval.fileList).head = (yyvsp[-1].fileList).head; (yyval.fileList).last->common.next = &(yyvsp[0].file)->common; (yyval.fileList).last = (yyvsp[0].file); } +#line 2022 "xkbcommon@sha/parser.c" + break; + + case 10: +#line 276 "../src/xkbcomp/parser.y" + { (yyval.fileList).head = (yyval.fileList).last = (yyvsp[0].file); } +#line 2028 "xkbcommon@sha/parser.c" + break; + + case 11: +#line 282 "../src/xkbcomp/parser.y" + { + (yyval.file) = XkbFileCreate((yyvsp[-5].file_type), (yyvsp[-4].str), (yyvsp[-2].anyList).head, (yyvsp[-6].mapFlags)); + } +#line 2036 "xkbcommon@sha/parser.c" + break; + + case 12: +#line 287 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_KEYCODES; } +#line 2042 "xkbcommon@sha/parser.c" + break; + + case 13: +#line 288 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_TYPES; } +#line 2048 "xkbcommon@sha/parser.c" + break; + + case 14: +#line 289 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_COMPAT; } +#line 2054 "xkbcommon@sha/parser.c" + break; + + case 15: +#line 290 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_SYMBOLS; } +#line 2060 "xkbcommon@sha/parser.c" + break; + + case 16: +#line 291 "../src/xkbcomp/parser.y" + { (yyval.file_type) = FILE_TYPE_GEOMETRY; } +#line 2066 "xkbcommon@sha/parser.c" + break; + + case 17: +#line 294 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = (yyvsp[0].mapFlags); } +#line 2072 "xkbcommon@sha/parser.c" + break; + + case 18: +#line 295 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = 0; } +#line 2078 "xkbcommon@sha/parser.c" + break; + + case 19: +#line 298 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = ((yyvsp[-1].mapFlags) | (yyvsp[0].mapFlags)); } +#line 2084 "xkbcommon@sha/parser.c" + break; + + case 20: +#line 299 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = (yyvsp[0].mapFlags); } +#line 2090 "xkbcommon@sha/parser.c" + break; + + case 21: +#line 302 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_PARTIAL; } +#line 2096 "xkbcommon@sha/parser.c" + break; + + case 22: +#line 303 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_DEFAULT; } +#line 2102 "xkbcommon@sha/parser.c" + break; + + case 23: +#line 304 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_HIDDEN; } +#line 2108 "xkbcommon@sha/parser.c" + break; + + case 24: +#line 305 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_ALPHANUMERIC; } +#line 2114 "xkbcommon@sha/parser.c" + break; + + case 25: +#line 306 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_MODIFIER; } +#line 2120 "xkbcommon@sha/parser.c" + break; + + case 26: +#line 307 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_KEYPAD; } +#line 2126 "xkbcommon@sha/parser.c" + break; + + case 27: +#line 308 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_HAS_FN; } +#line 2132 "xkbcommon@sha/parser.c" + break; + + case 28: +#line 309 "../src/xkbcomp/parser.y" + { (yyval.mapFlags) = MAP_IS_ALTGR; } +#line 2138 "xkbcommon@sha/parser.c" + break; + + case 29: +#line 313 "../src/xkbcomp/parser.y" + { + if ((yyvsp[0].any)) { + if ((yyvsp[-1].anyList).head) { + (yyval.anyList).head = (yyvsp[-1].anyList).head; (yyvsp[-1].anyList).last->next = (yyvsp[0].any); (yyval.anyList).last = (yyvsp[0].any); + } else { + (yyval.anyList).head = (yyval.anyList).last = (yyvsp[0].any); + } + } + } +#line 2152 "xkbcommon@sha/parser.c" + break; + + case 30: +#line 328 "../src/xkbcomp/parser.y" + { + for (VModDef *vmod = (yyvsp[0].vmodList).head; vmod; vmod = (VModDef *) vmod->common.next) + vmod->merge = (yyvsp[-1].merge); + if ((yyvsp[-2].anyList).head) { + (yyval.anyList).head = (yyvsp[-2].anyList).head; (yyvsp[-2].anyList).last->next = &(yyvsp[0].vmodList).head->common; (yyval.anyList).last = &(yyvsp[0].vmodList).last->common; + } else { + (yyval.anyList).head = &(yyvsp[0].vmodList).head->common; (yyval.anyList).last = &(yyvsp[0].vmodList).last->common; + } + } +#line 2166 "xkbcommon@sha/parser.c" + break; + + case 31: +#line 337 "../src/xkbcomp/parser.y" + { (yyval.anyList).head = (yyval.anyList).last = NULL; } +#line 2172 "xkbcommon@sha/parser.c" + break; + + case 32: +#line 341 "../src/xkbcomp/parser.y" + { + (yyvsp[0].var)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].var); + } +#line 2181 "xkbcommon@sha/parser.c" + break; + + case 33: +#line 347 "../src/xkbcomp/parser.y" + { + (yyvsp[0].interp)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].interp); + } +#line 2190 "xkbcommon@sha/parser.c" + break; + + case 34: +#line 352 "../src/xkbcomp/parser.y" + { + (yyvsp[0].keyCode)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].keyCode); + } +#line 2199 "xkbcommon@sha/parser.c" + break; + + case 35: +#line 357 "../src/xkbcomp/parser.y" + { + (yyvsp[0].keyAlias)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].keyAlias); + } +#line 2208 "xkbcommon@sha/parser.c" + break; + + case 36: +#line 362 "../src/xkbcomp/parser.y" + { + (yyvsp[0].keyType)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].keyType); + } +#line 2217 "xkbcommon@sha/parser.c" + break; + + case 37: +#line 367 "../src/xkbcomp/parser.y" + { + (yyvsp[0].syms)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].syms); + } +#line 2226 "xkbcommon@sha/parser.c" + break; + + case 38: +#line 372 "../src/xkbcomp/parser.y" + { + (yyvsp[0].modMask)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].modMask); + } +#line 2235 "xkbcommon@sha/parser.c" + break; + + case 39: +#line 377 "../src/xkbcomp/parser.y" + { + (yyvsp[0].groupCompat)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].groupCompat); + } +#line 2244 "xkbcommon@sha/parser.c" + break; + + case 40: +#line 382 "../src/xkbcomp/parser.y" + { + (yyvsp[0].ledMap)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].ledMap); + } +#line 2253 "xkbcommon@sha/parser.c" + break; + + case 41: +#line 387 "../src/xkbcomp/parser.y" + { + (yyvsp[0].ledName)->merge = (yyvsp[-1].merge); + (yyval.any) = (ParseCommon *) (yyvsp[0].ledName); + } +#line 2262 "xkbcommon@sha/parser.c" + break; + + case 42: +#line 391 "../src/xkbcomp/parser.y" + { (yyval.any) = NULL; } +#line 2268 "xkbcommon@sha/parser.c" + break; + + case 43: +#line 392 "../src/xkbcomp/parser.y" + { (yyval.any) = NULL; } +#line 2274 "xkbcommon@sha/parser.c" + break; + + case 44: +#line 393 "../src/xkbcomp/parser.y" + { (yyval.any) = NULL; } +#line 2280 "xkbcommon@sha/parser.c" + break; + + case 45: +#line 395 "../src/xkbcomp/parser.y" + { + (yyval.any) = (ParseCommon *) IncludeCreate(param->ctx, (yyvsp[0].str), (yyvsp[-1].merge)); + free((yyvsp[0].str)); + } +#line 2289 "xkbcommon@sha/parser.c" + break; + + case 46: +#line 402 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate((yyvsp[-3].expr), (yyvsp[-1].expr)); } +#line 2295 "xkbcommon@sha/parser.c" + break; + + case 47: +#line 404 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[-1].atom), true); } +#line 2301 "xkbcommon@sha/parser.c" + break; + + case 48: +#line 406 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[-1].atom), false); } +#line 2307 "xkbcommon@sha/parser.c" + break; + + case 49: +#line 410 "../src/xkbcomp/parser.y" + { (yyval.keyCode) = KeycodeCreate((yyvsp[-3].atom), (yyvsp[-1].num)); } +#line 2313 "xkbcommon@sha/parser.c" + break; + + case 50: +#line 414 "../src/xkbcomp/parser.y" + { (yyval.keyAlias) = KeyAliasCreate((yyvsp[-3].atom), (yyvsp[-1].atom)); } +#line 2319 "xkbcommon@sha/parser.c" + break; + + case 51: +#line 418 "../src/xkbcomp/parser.y" + { (yyval.vmodList) = (yyvsp[-1].vmodList); } +#line 2325 "xkbcommon@sha/parser.c" + break; + + case 52: +#line 422 "../src/xkbcomp/parser.y" + { (yyval.vmodList).head = (yyvsp[-2].vmodList).head; (yyval.vmodList).last->common.next = &(yyvsp[0].vmod)->common; (yyval.vmodList).last = (yyvsp[0].vmod); } +#line 2331 "xkbcommon@sha/parser.c" + break; + + case 53: +#line 424 "../src/xkbcomp/parser.y" + { (yyval.vmodList).head = (yyval.vmodList).last = (yyvsp[0].vmod); } +#line 2337 "xkbcommon@sha/parser.c" + break; + + case 54: +#line 428 "../src/xkbcomp/parser.y" + { (yyval.vmod) = VModCreate((yyvsp[0].atom), NULL); } +#line 2343 "xkbcommon@sha/parser.c" + break; + + case 55: +#line 430 "../src/xkbcomp/parser.y" + { (yyval.vmod) = VModCreate((yyvsp[-2].atom), (yyvsp[0].expr)); } +#line 2349 "xkbcommon@sha/parser.c" + break; + + case 56: +#line 436 "../src/xkbcomp/parser.y" + { (yyvsp[-4].interp)->def = (yyvsp[-2].varList).head; (yyval.interp) = (yyvsp[-4].interp); } +#line 2355 "xkbcommon@sha/parser.c" + break; + + case 57: +#line 440 "../src/xkbcomp/parser.y" + { (yyval.interp) = InterpCreate((yyvsp[-2].keysym), (yyvsp[0].expr)); } +#line 2361 "xkbcommon@sha/parser.c" + break; + + case 58: +#line 442 "../src/xkbcomp/parser.y" + { (yyval.interp) = InterpCreate((yyvsp[0].keysym), NULL); } +#line 2367 "xkbcommon@sha/parser.c" + break; + + case 59: +#line 446 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyvsp[-1].varList).head; (yyval.varList).last->common.next = &(yyvsp[0].var)->common; (yyval.varList).last = (yyvsp[0].var); } +#line 2373 "xkbcommon@sha/parser.c" + break; + + case 60: +#line 448 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyval.varList).last = (yyvsp[0].var); } +#line 2379 "xkbcommon@sha/parser.c" + break; + + case 61: +#line 454 "../src/xkbcomp/parser.y" + { (yyval.keyType) = KeyTypeCreate((yyvsp[-4].atom), (yyvsp[-2].varList).head); } +#line 2385 "xkbcommon@sha/parser.c" + break; + + case 62: +#line 460 "../src/xkbcomp/parser.y" + { (yyval.syms) = SymbolsCreate((yyvsp[-4].atom), (yyvsp[-2].varList).head); } +#line 2391 "xkbcommon@sha/parser.c" + break; + + case 63: +#line 464 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyvsp[-2].varList).head; (yyval.varList).last->common.next = &(yyvsp[0].var)->common; (yyval.varList).last = (yyvsp[0].var); } +#line 2397 "xkbcommon@sha/parser.c" + break; + + case 64: +#line 466 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyval.varList).last = (yyvsp[0].var); } +#line 2403 "xkbcommon@sha/parser.c" + break; + + case 65: +#line 467 "../src/xkbcomp/parser.y" + { (yyval.varList).head = (yyval.varList).last = NULL; } +#line 2409 "xkbcommon@sha/parser.c" + break; + + case 66: +#line 470 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate((yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2415 "xkbcommon@sha/parser.c" + break; + + case 67: +#line 471 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate((yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2421 "xkbcommon@sha/parser.c" + break; + + case 68: +#line 472 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[0].atom), true); } +#line 2427 "xkbcommon@sha/parser.c" + break; + + case 69: +#line 473 "../src/xkbcomp/parser.y" + { (yyval.var) = BoolVarCreate((yyvsp[0].atom), false); } +#line 2433 "xkbcommon@sha/parser.c" + break; + + case 70: +#line 474 "../src/xkbcomp/parser.y" + { (yyval.var) = VarCreate(NULL, (yyvsp[0].expr)); } +#line 2439 "xkbcommon@sha/parser.c" + break; + + case 71: +#line 478 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2445 "xkbcommon@sha/parser.c" + break; + + case 72: +#line 480 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateActionList((yyvsp[-1].exprList).head); } +#line 2451 "xkbcommon@sha/parser.c" + break; + + case 73: +#line 484 "../src/xkbcomp/parser.y" + { (yyval.groupCompat) = GroupCompatCreate((yyvsp[-3].num), (yyvsp[-1].expr)); } +#line 2457 "xkbcommon@sha/parser.c" + break; + + case 74: +#line 488 "../src/xkbcomp/parser.y" + { (yyval.modMask) = ModMapCreate((yyvsp[-4].atom), (yyvsp[-2].exprList).head); } +#line 2463 "xkbcommon@sha/parser.c" + break; + + case 75: +#line 492 "../src/xkbcomp/parser.y" + { (yyval.ledMap) = LedMapCreate((yyvsp[-4].atom), (yyvsp[-2].varList).head); } +#line 2469 "xkbcommon@sha/parser.c" + break; + + case 76: +#line 496 "../src/xkbcomp/parser.y" + { (yyval.ledName) = LedNameCreate((yyvsp[-3].num), (yyvsp[-1].expr), false); } +#line 2475 "xkbcommon@sha/parser.c" + break; + + case 77: +#line 498 "../src/xkbcomp/parser.y" + { (yyval.ledName) = LedNameCreate((yyvsp[-3].num), (yyvsp[-1].expr), true); } +#line 2481 "xkbcommon@sha/parser.c" + break; + + case 78: +#line 502 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2487 "xkbcommon@sha/parser.c" + break; + + case 79: +#line 504 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-2].expr); (yyval.geom) = NULL; } +#line 2493 "xkbcommon@sha/parser.c" + break; + + case 80: +#line 508 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2499 "xkbcommon@sha/parser.c" + break; + + case 81: +#line 511 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL;} +#line 2505 "xkbcommon@sha/parser.c" + break; + + case 82: +#line 512 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2511 "xkbcommon@sha/parser.c" + break; + + case 83: +#line 516 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2517 "xkbcommon@sha/parser.c" + break; + + case 84: +#line 518 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].var)); (yyval.geom) = NULL; } +#line 2523 "xkbcommon@sha/parser.c" + break; + + case 85: +#line 520 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2529 "xkbcommon@sha/parser.c" + break; + + case 86: +#line 522 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].ledMap)); (yyval.geom) = NULL; } +#line 2535 "xkbcommon@sha/parser.c" + break; + + case 87: +#line 524 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2541 "xkbcommon@sha/parser.c" + break; + + case 88: +#line 527 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL;} +#line 2547 "xkbcommon@sha/parser.c" + break; + + case 89: +#line 528 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2553 "xkbcommon@sha/parser.c" + break; + + case 90: +#line 531 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2559 "xkbcommon@sha/parser.c" + break; + + case 91: +#line 533 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].var)); (yyval.geom) = NULL; } +#line 2565 "xkbcommon@sha/parser.c" + break; + + case 92: +#line 536 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2571 "xkbcommon@sha/parser.c" + break; + + case 93: +#line 537 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2577 "xkbcommon@sha/parser.c" + break; + + case 94: +#line 541 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2583 "xkbcommon@sha/parser.c" + break; + + case 95: +#line 543 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[-1].exprList).head); (yyval.geom) = NULL; } +#line 2589 "xkbcommon@sha/parser.c" + break; + + case 96: +#line 547 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2595 "xkbcommon@sha/parser.c" + break; + + case 97: +#line 550 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2601 "xkbcommon@sha/parser.c" + break; + + case 98: +#line 551 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2607 "xkbcommon@sha/parser.c" + break; + + case 99: +#line 554 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2613 "xkbcommon@sha/parser.c" + break; + + case 100: +#line 558 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL;} +#line 2619 "xkbcommon@sha/parser.c" + break; + + case 101: +#line 560 "../src/xkbcomp/parser.y" + { (yyval.geom) = NULL; } +#line 2625 "xkbcommon@sha/parser.c" + break; + + case 102: +#line 564 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-1].expr); (yyval.geom) = NULL; } +#line 2631 "xkbcommon@sha/parser.c" + break; + + case 103: +#line 566 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-1].expr); (yyval.geom) = NULL; } +#line 2637 "xkbcommon@sha/parser.c" + break; + + case 104: +#line 568 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[0].expr)); (yyval.geom) = NULL; } +#line 2643 "xkbcommon@sha/parser.c" + break; + + case 105: +#line 572 "../src/xkbcomp/parser.y" + { (void) (yyvsp[-2].expr); (void) (yyvsp[0].expr); (yyval.expr) = NULL; } +#line 2649 "xkbcommon@sha/parser.c" + break; + + case 106: +#line 574 "../src/xkbcomp/parser.y" + { (void) (yyvsp[0].expr); (yyval.expr) = NULL; } +#line 2655 "xkbcommon@sha/parser.c" + break; + + case 107: +#line 578 "../src/xkbcomp/parser.y" + { (yyval.expr) = NULL; } +#line 2661 "xkbcommon@sha/parser.c" + break; + + case 108: +#line 582 "../src/xkbcomp/parser.y" + { FreeStmt((ParseCommon *) (yyvsp[-2].varList).head); (yyval.geom) = NULL; } +#line 2667 "xkbcommon@sha/parser.c" + break; + + case 109: +#line 585 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2673 "xkbcommon@sha/parser.c" + break; + + case 110: +#line 586 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2679 "xkbcommon@sha/parser.c" + break; + + case 111: +#line 587 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2685 "xkbcommon@sha/parser.c" + break; + + case 112: +#line 588 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 2691 "xkbcommon@sha/parser.c" + break; + + case 113: +#line 591 "../src/xkbcomp/parser.y" + { (yyval.atom) = (yyvsp[0].atom); } +#line 2697 "xkbcommon@sha/parser.c" + break; + + case 114: +#line 592 "../src/xkbcomp/parser.y" + { (yyval.atom) = (yyvsp[0].atom); } +#line 2703 "xkbcommon@sha/parser.c" + break; + + case 115: +#line 596 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "action"); } +#line 2709 "xkbcommon@sha/parser.c" + break; + + case 116: +#line 598 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "interpret"); } +#line 2715 "xkbcommon@sha/parser.c" + break; + + case 117: +#line 600 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "type"); } +#line 2721 "xkbcommon@sha/parser.c" + break; + + case 118: +#line 602 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "key"); } +#line 2727 "xkbcommon@sha/parser.c" + break; + + case 119: +#line 604 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "group"); } +#line 2733 "xkbcommon@sha/parser.c" + break; + + case 120: +#line 606 "../src/xkbcomp/parser.y" + {(yyval.atom) = xkb_atom_intern_literal(param->ctx, "modifier_map");} +#line 2739 "xkbcommon@sha/parser.c" + break; + + case 121: +#line 608 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "indicator"); } +#line 2745 "xkbcommon@sha/parser.c" + break; + + case 122: +#line 610 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "shape"); } +#line 2751 "xkbcommon@sha/parser.c" + break; + + case 123: +#line 612 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "row"); } +#line 2757 "xkbcommon@sha/parser.c" + break; + + case 124: +#line 614 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "section"); } +#line 2763 "xkbcommon@sha/parser.c" + break; + + case 125: +#line 616 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "text"); } +#line 2769 "xkbcommon@sha/parser.c" + break; + + case 126: +#line 619 "../src/xkbcomp/parser.y" + { (yyval.merge) = (yyvsp[0].merge); } +#line 2775 "xkbcommon@sha/parser.c" + break; + + case 127: +#line 620 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_DEFAULT; } +#line 2781 "xkbcommon@sha/parser.c" + break; + + case 128: +#line 623 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_DEFAULT; } +#line 2787 "xkbcommon@sha/parser.c" + break; + + case 129: +#line 624 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_AUGMENT; } +#line 2793 "xkbcommon@sha/parser.c" + break; + + case 130: +#line 625 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_OVERRIDE; } +#line 2799 "xkbcommon@sha/parser.c" + break; + + case 131: +#line 626 "../src/xkbcomp/parser.y" + { (yyval.merge) = MERGE_REPLACE; } +#line 2805 "xkbcommon@sha/parser.c" + break; + + case 132: +#line 628 "../src/xkbcomp/parser.y" + { + /* + * This used to be MERGE_ALT_FORM. This functionality was + * unused and has been removed. + */ + (yyval.merge) = MERGE_DEFAULT; + } +#line 2817 "xkbcommon@sha/parser.c" + break; + + case 133: +#line 637 "../src/xkbcomp/parser.y" + { (yyval.exprList) = (yyvsp[0].exprList); } +#line 2823 "xkbcommon@sha/parser.c" + break; + + case 134: +#line 638 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyval.exprList).last = NULL; } +#line 2829 "xkbcommon@sha/parser.c" + break; + + case 135: +#line 642 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyvsp[-2].exprList).head; (yyval.exprList).last->common.next = &(yyvsp[0].expr)->common; (yyval.exprList).last = (yyvsp[0].expr); } +#line 2835 "xkbcommon@sha/parser.c" + break; + + case 136: +#line 644 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyval.exprList).last = (yyvsp[0].expr); } +#line 2841 "xkbcommon@sha/parser.c" + break; + + case 137: +#line 648 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_DIVIDE, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2847 "xkbcommon@sha/parser.c" + break; + + case 138: +#line 650 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_ADD, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2853 "xkbcommon@sha/parser.c" + break; + + case 139: +#line 652 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_SUBTRACT, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2859 "xkbcommon@sha/parser.c" + break; + + case 140: +#line 654 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_MULTIPLY, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2865 "xkbcommon@sha/parser.c" + break; + + case 141: +#line 656 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateBinary(EXPR_ASSIGN, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2871 "xkbcommon@sha/parser.c" + break; + + case 142: +#line 658 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2877 "xkbcommon@sha/parser.c" + break; + + case 143: +#line 662 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_NEGATE, (yyvsp[0].expr)->expr.value_type, (yyvsp[0].expr)); } +#line 2883 "xkbcommon@sha/parser.c" + break; + + case 144: +#line 664 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_UNARY_PLUS, (yyvsp[0].expr)->expr.value_type, (yyvsp[0].expr)); } +#line 2889 "xkbcommon@sha/parser.c" + break; + + case 145: +#line 666 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_NOT, EXPR_TYPE_BOOLEAN, (yyvsp[0].expr)); } +#line 2895 "xkbcommon@sha/parser.c" + break; + + case 146: +#line 668 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateUnary(EXPR_INVERT, (yyvsp[0].expr)->expr.value_type, (yyvsp[0].expr)); } +#line 2901 "xkbcommon@sha/parser.c" + break; + + case 147: +#line 670 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2907 "xkbcommon@sha/parser.c" + break; + + case 148: +#line 672 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateAction((yyvsp[-3].atom), (yyvsp[-1].exprList).head); } +#line 2913 "xkbcommon@sha/parser.c" + break; + + case 149: +#line 674 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2919 "xkbcommon@sha/parser.c" + break; + + case 150: +#line 676 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2925 "xkbcommon@sha/parser.c" + break; + + case 151: +#line 680 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyvsp[-2].exprList).head; (yyval.exprList).last->common.next = &(yyvsp[0].expr)->common; (yyval.exprList).last = (yyvsp[0].expr); } +#line 2931 "xkbcommon@sha/parser.c" + break; + + case 152: +#line 682 "../src/xkbcomp/parser.y" + { (yyval.exprList).head = (yyval.exprList).last = (yyvsp[0].expr); } +#line 2937 "xkbcommon@sha/parser.c" + break; + + case 153: +#line 686 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateAction((yyvsp[-3].atom), (yyvsp[-1].exprList).head); } +#line 2943 "xkbcommon@sha/parser.c" + break; + + case 154: +#line 690 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateIdent((yyvsp[0].atom)); } +#line 2949 "xkbcommon@sha/parser.c" + break; + + case 155: +#line 692 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateFieldRef((yyvsp[-2].atom), (yyvsp[0].atom)); } +#line 2955 "xkbcommon@sha/parser.c" + break; + + case 156: +#line 694 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateArrayRef(XKB_ATOM_NONE, (yyvsp[-3].atom), (yyvsp[-1].expr)); } +#line 2961 "xkbcommon@sha/parser.c" + break; + + case 157: +#line 696 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateArrayRef((yyvsp[-5].atom), (yyvsp[-3].atom), (yyvsp[-1].expr)); } +#line 2967 "xkbcommon@sha/parser.c" + break; + + case 158: +#line 700 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateString((yyvsp[0].atom)); } +#line 2973 "xkbcommon@sha/parser.c" + break; + + case 159: +#line 702 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateInteger((yyvsp[0].num)); } +#line 2979 "xkbcommon@sha/parser.c" + break; + + case 160: +#line 704 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateFloat(/* Discard $1 */); } +#line 2985 "xkbcommon@sha/parser.c" + break; + + case 161: +#line 706 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateKeyName((yyvsp[0].atom)); } +#line 2991 "xkbcommon@sha/parser.c" + break; + + case 162: +#line 709 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 2997 "xkbcommon@sha/parser.c" + break; + + case 163: +#line 710 "../src/xkbcomp/parser.y" + { (yyval.expr) = NULL; } +#line 3003 "xkbcommon@sha/parser.c" + break; + + case 164: +#line 714 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprAppendKeysymList((yyvsp[-2].expr), (yyvsp[0].keysym)); } +#line 3009 "xkbcommon@sha/parser.c" + break; + + case 165: +#line 716 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprAppendMultiKeysymList((yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 3015 "xkbcommon@sha/parser.c" + break; + + case 166: +#line 718 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateKeysymList((yyvsp[0].keysym)); } +#line 3021 "xkbcommon@sha/parser.c" + break; + + case 167: +#line 720 "../src/xkbcomp/parser.y" + { (yyval.expr) = ExprCreateMultiKeysymList((yyvsp[0].expr)); } +#line 3027 "xkbcommon@sha/parser.c" + break; + + case 168: +#line 724 "../src/xkbcomp/parser.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 3033 "xkbcommon@sha/parser.c" + break; + + case 169: +#line 728 "../src/xkbcomp/parser.y" + { + if (!resolve_keysym((yyvsp[0].str), &(yyval.keysym))) + parser_warn(param, "unrecognized keysym \"%s\"", (yyvsp[0].str)); + free((yyvsp[0].str)); + } +#line 3043 "xkbcommon@sha/parser.c" + break; + + case 170: +#line 733 "../src/xkbcomp/parser.y" + { (yyval.keysym) = XKB_KEY_section; } +#line 3049 "xkbcommon@sha/parser.c" + break; + + case 171: +#line 735 "../src/xkbcomp/parser.y" + { + if ((yyvsp[0].num) < 0) { + parser_warn(param, "unrecognized keysym \"%" PRId64 "\"", (yyvsp[0].num)); + (yyval.keysym) = XKB_KEY_NoSymbol; + } + else if ((yyvsp[0].num) < 10) { /* XKB_KEY_0 .. XKB_KEY_9 */ + (yyval.keysym) = XKB_KEY_0 + (xkb_keysym_t) (yyvsp[0].num); + } + else { + char buf[32]; + snprintf(buf, sizeof(buf), "0x%"PRIx64, (yyvsp[0].num)); + if (!resolve_keysym(buf, &(yyval.keysym))) { + parser_warn(param, "unrecognized keysym \"%s\"", buf); + (yyval.keysym) = XKB_KEY_NoSymbol; + } + } + } +#line 3071 "xkbcommon@sha/parser.c" + break; + + case 172: +#line 754 "../src/xkbcomp/parser.y" + { (yyval.num) = -(yyvsp[0].num); } +#line 3077 "xkbcommon@sha/parser.c" + break; + + case 173: +#line 755 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3083 "xkbcommon@sha/parser.c" + break; + + case 174: +#line 758 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3089 "xkbcommon@sha/parser.c" + break; + + case 175: +#line 759 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3095 "xkbcommon@sha/parser.c" + break; + + case 176: +#line 762 "../src/xkbcomp/parser.y" + { (yyval.num) = 0; } +#line 3101 "xkbcommon@sha/parser.c" + break; + + case 177: +#line 765 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3107 "xkbcommon@sha/parser.c" + break; + + case 178: +#line 768 "../src/xkbcomp/parser.y" + { (yyval.num) = (yyvsp[0].num); } +#line 3113 "xkbcommon@sha/parser.c" + break; + + case 179: +#line 771 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern(param->ctx, (yyvsp[0].str), strlen((yyvsp[0].str))); free((yyvsp[0].str)); } +#line 3119 "xkbcommon@sha/parser.c" + break; + + case 180: +#line 772 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern_literal(param->ctx, "default"); } +#line 3125 "xkbcommon@sha/parser.c" + break; + + case 181: +#line 775 "../src/xkbcomp/parser.y" + { (yyval.atom) = xkb_atom_intern(param->ctx, (yyvsp[0].str), strlen((yyvsp[0].str))); free((yyvsp[0].str)); } +#line 3131 "xkbcommon@sha/parser.c" + break; + + case 182: +#line 778 "../src/xkbcomp/parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 3137 "xkbcommon@sha/parser.c" + break; + + case 183: +#line 779 "../src/xkbcomp/parser.y" + { (yyval.str) = NULL; } +#line 3143 "xkbcommon@sha/parser.c" + break; + + case 184: +#line 782 "../src/xkbcomp/parser.y" + { (yyval.str) = (yyvsp[0].str); } +#line 3149 "xkbcommon@sha/parser.c" + break; + + +#line 3153 "xkbcommon@sha/parser.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (param, YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= END_OF_FILE) + { + /* Return failure if at end of input. */ + if (yychar == END_OF_FILE) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, param); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, param); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (param, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, param); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, param); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 785 "../src/xkbcomp/parser.y" + + +XkbFile * +parse(struct xkb_context *ctx, struct scanner *scanner, const char *map) +{ + int ret; + XkbFile *first = NULL; + struct parser_param param = { + .scanner = scanner, + .ctx = ctx, + .rtrn = NULL, + .more_maps = false, + }; + + /* + * If we got a specific map, we look for it exclusively and return + * immediately upon finding it. Otherwise, we need to get the + * default map. If we find a map marked as default, we return it + * immediately. If there are no maps marked as default, we return + * the first map in the file. + */ + + while ((ret = yyparse(¶m)) == 0 && param.more_maps) { + if (map) { + if (streq_not_null(map, param.rtrn->name)) + return param.rtrn; + else + FreeXkbFile(param.rtrn); + } + else { + if (param.rtrn->flags & MAP_IS_DEFAULT) { + FreeXkbFile(first); + return param.rtrn; + } + else if (!first) { + first = param.rtrn; + } + else { + FreeXkbFile(param.rtrn); + } + } + param.rtrn = NULL; + } + + if (ret != 0) { + FreeXkbFile(first); + return NULL; + } + + if (first) + log_vrb(ctx, 5, + "No map in include statement, but \"%s\" contains several; " + "Using first defined map, \"%s\"\n", + scanner->file_name, first->name); + + return first; +} diff --git a/src/xkbcomp/parser.h b/src/xkbcomp/parser.h new file mode 100644 index 0000000..e158fa4 --- /dev/null +++ b/src/xkbcomp/parser.h @@ -0,0 +1,169 @@ +/* A Bison parser, made by GNU Bison 3.6.3. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +# define YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int _xkbcommon_debug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + END_OF_FILE = 0, /* END_OF_FILE */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + ERROR_TOK = 255, /* ERROR_TOK */ + XKB_KEYMAP = 1, /* XKB_KEYMAP */ + XKB_KEYCODES = 2, /* XKB_KEYCODES */ + XKB_TYPES = 3, /* XKB_TYPES */ + XKB_SYMBOLS = 4, /* XKB_SYMBOLS */ + XKB_COMPATMAP = 5, /* XKB_COMPATMAP */ + XKB_GEOMETRY = 6, /* XKB_GEOMETRY */ + XKB_SEMANTICS = 7, /* XKB_SEMANTICS */ + XKB_LAYOUT = 8, /* XKB_LAYOUT */ + INCLUDE = 10, /* INCLUDE */ + OVERRIDE = 11, /* OVERRIDE */ + AUGMENT = 12, /* AUGMENT */ + REPLACE = 13, /* REPLACE */ + ALTERNATE = 14, /* ALTERNATE */ + VIRTUAL_MODS = 20, /* VIRTUAL_MODS */ + TYPE = 21, /* TYPE */ + INTERPRET = 22, /* INTERPRET */ + ACTION_TOK = 23, /* ACTION_TOK */ + KEY = 24, /* KEY */ + ALIAS = 25, /* ALIAS */ + GROUP = 26, /* GROUP */ + MODIFIER_MAP = 27, /* MODIFIER_MAP */ + INDICATOR = 28, /* INDICATOR */ + SHAPE = 29, /* SHAPE */ + KEYS = 30, /* KEYS */ + ROW = 31, /* ROW */ + SECTION = 32, /* SECTION */ + OVERLAY = 33, /* OVERLAY */ + TEXT = 34, /* TEXT */ + OUTLINE = 35, /* OUTLINE */ + SOLID = 36, /* SOLID */ + LOGO = 37, /* LOGO */ + VIRTUAL = 38, /* VIRTUAL */ + EQUALS = 40, /* EQUALS */ + PLUS = 41, /* PLUS */ + MINUS = 42, /* MINUS */ + DIVIDE = 43, /* DIVIDE */ + TIMES = 44, /* TIMES */ + OBRACE = 45, /* OBRACE */ + CBRACE = 46, /* CBRACE */ + OPAREN = 47, /* OPAREN */ + CPAREN = 48, /* CPAREN */ + OBRACKET = 49, /* OBRACKET */ + CBRACKET = 50, /* CBRACKET */ + DOT = 51, /* DOT */ + COMMA = 52, /* COMMA */ + SEMI = 53, /* SEMI */ + EXCLAM = 54, /* EXCLAM */ + INVERT = 55, /* INVERT */ + STRING = 60, /* STRING */ + INTEGER = 61, /* INTEGER */ + FLOAT = 62, /* FLOAT */ + IDENT = 63, /* IDENT */ + KEYNAME = 64, /* KEYNAME */ + PARTIAL = 70, /* PARTIAL */ + DEFAULT = 71, /* DEFAULT */ + HIDDEN = 72, /* HIDDEN */ + ALPHANUMERIC_KEYS = 73, /* ALPHANUMERIC_KEYS */ + MODIFIER_KEYS = 74, /* MODIFIER_KEYS */ + KEYPAD_KEYS = 75, /* KEYPAD_KEYS */ + FUNCTION_KEYS = 76, /* FUNCTION_KEYS */ + ALTERNATE_GROUP = 77 /* ALTERNATE_GROUP */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 164 "../src/xkbcomp/parser.y" + + int64_t num; + enum xkb_file_type file_type; + char *str; + xkb_atom_t atom; + enum merge_mode merge; + enum xkb_map_flags mapFlags; + xkb_keysym_t keysym; + ParseCommon *any; + struct { ParseCommon *head; ParseCommon *last; } anyList; + ExprDef *expr; + struct { ExprDef *head; ExprDef *last; } exprList; + VarDef *var; + struct { VarDef *head; VarDef *last; } varList; + VModDef *vmod; + struct { VModDef *head; VModDef *last; } vmodList; + InterpDef *interp; + KeyTypeDef *keyType; + SymbolsDef *syms; + ModMapDef *modMask; + GroupCompatDef *groupCompat; + LedMapDef *ledMap; + LedNameDef *ledName; + KeycodeDef *keyCode; + KeyAliasDef *keyAlias; + void *geom; + XkbFile *file; + struct { XkbFile *head; XkbFile *last; } fileList; + +#line 158 "xkbcommon@sha/parser.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int _xkbcommon_parse (struct parser_param *param); + +#endif /* !YY__XKBCOMMON_XKBCOMMON_SHA_PARSER_H_INCLUDED */ diff --git a/src/xkbcomp/parser.y b/src/xkbcomp/parser.y new file mode 100644 index 0000000..b1e4188 --- /dev/null +++ b/src/xkbcomp/parser.y @@ -0,0 +1,841 @@ +/************************************************************ + Copyright (c) 1994 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. + + ********************************************************/ + +/* + * The parser should work with reasonably recent versions of either + * bison or byacc. So if you make changes, try to make sure it works + * in both! + */ + +%{ +#include "config.h" + +#include "xkbcomp/xkbcomp-priv.h" +#include "xkbcomp/ast-build.h" +#include "xkbcomp/parser-priv.h" +#include "scanner-utils.h" + +struct parser_param { + struct xkb_context *ctx; + struct scanner *scanner; + XkbFile *rtrn; + bool more_maps; +}; + +#define parser_err(param, fmt, ...) \ + scanner_err((param)->scanner, fmt, ##__VA_ARGS__) + +#define parser_warn(param, fmt, ...) \ + scanner_warn((param)->scanner, fmt, ##__VA_ARGS__) + +static void +_xkbcommon_error(struct parser_param *param, const char *msg) +{ + parser_err(param, "%s", msg); +} + +static bool +resolve_keysym(const char *name, xkb_keysym_t *sym_rtrn) +{ + xkb_keysym_t sym; + + if (!name || istreq(name, "any") || istreq(name, "nosymbol")) { + *sym_rtrn = XKB_KEY_NoSymbol; + return true; + } + + if (istreq(name, "none") || istreq(name, "voidsymbol")) { + *sym_rtrn = XKB_KEY_VoidSymbol; + return true; + } + + sym = xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); + if (sym != XKB_KEY_NoSymbol) { + *sym_rtrn = sym; + return true; + } + + return false; +} + +#define param_scanner param->scanner +%} + +%pure-parser +%lex-param { struct scanner *param_scanner } +%parse-param { struct parser_param *param } + +%token + END_OF_FILE 0 + ERROR_TOK 255 + XKB_KEYMAP 1 + XKB_KEYCODES 2 + XKB_TYPES 3 + XKB_SYMBOLS 4 + XKB_COMPATMAP 5 + XKB_GEOMETRY 6 + XKB_SEMANTICS 7 + XKB_LAYOUT 8 + INCLUDE 10 + OVERRIDE 11 + AUGMENT 12 + REPLACE 13 + ALTERNATE 14 + VIRTUAL_MODS 20 + TYPE 21 + INTERPRET 22 + ACTION_TOK 23 + KEY 24 + ALIAS 25 + GROUP 26 + MODIFIER_MAP 27 + INDICATOR 28 + SHAPE 29 + KEYS 30 + ROW 31 + SECTION 32 + OVERLAY 33 + TEXT 34 + OUTLINE 35 + SOLID 36 + LOGO 37 + VIRTUAL 38 + EQUALS 40 + PLUS 41 + MINUS 42 + DIVIDE 43 + TIMES 44 + OBRACE 45 + CBRACE 46 + OPAREN 47 + CPAREN 48 + OBRACKET 49 + CBRACKET 50 + DOT 51 + COMMA 52 + SEMI 53 + EXCLAM 54 + INVERT 55 + STRING 60 + INTEGER 61 + FLOAT 62 + IDENT 63 + KEYNAME 64 + PARTIAL 70 + DEFAULT 71 + HIDDEN 72 + ALPHANUMERIC_KEYS 73 + MODIFIER_KEYS 74 + KEYPAD_KEYS 75 + FUNCTION_KEYS 76 + ALTERNATE_GROUP 77 + +%right EQUALS +%left PLUS MINUS +%left TIMES DIVIDE +%left EXCLAM INVERT +%left OPAREN + +%start XkbFile + +%union { + int64_t num; + enum xkb_file_type file_type; + char *str; + xkb_atom_t atom; + enum merge_mode merge; + enum xkb_map_flags mapFlags; + xkb_keysym_t keysym; + ParseCommon *any; + struct { ParseCommon *head; ParseCommon *last; } anyList; + ExprDef *expr; + struct { ExprDef *head; ExprDef *last; } exprList; + VarDef *var; + struct { VarDef *head; VarDef *last; } varList; + VModDef *vmod; + struct { VModDef *head; VModDef *last; } vmodList; + InterpDef *interp; + KeyTypeDef *keyType; + SymbolsDef *syms; + ModMapDef *modMask; + GroupCompatDef *groupCompat; + LedMapDef *ledMap; + LedNameDef *ledName; + KeycodeDef *keyCode; + KeyAliasDef *keyAlias; + void *geom; + XkbFile *file; + struct { XkbFile *head; XkbFile *last; } fileList; +} + +%type <num> INTEGER FLOAT +%type <str> IDENT STRING +%type <atom> KEYNAME +%type <num> KeyCode Number Integer Float SignedNumber DoodadType +%type <merge> MergeMode OptMergeMode +%type <file_type> XkbCompositeType FileType +%type <mapFlags> Flag Flags OptFlags +%type <str> MapName OptMapName +%type <atom> FieldSpec Ident Element String +%type <keysym> KeySym +%type <any> Decl +%type <anyList> DeclList +%type <expr> Expr Term Lhs Terminal ArrayInit KeySyms +%type <expr> OptKeySymList KeySymList Action Coord CoordList +%type <exprList> OptExprList ExprList ActionList +%type <var> VarDecl SymbolsVarDecl +%type <varList> VarDeclList SymbolsBody +%type <vmod> VModDef +%type <vmodList> VModDefList VModDecl +%type <interp> InterpretDecl InterpretMatch +%type <keyType> KeyTypeDecl +%type <syms> SymbolsDecl +%type <modMask> ModMapDecl +%type <groupCompat> GroupCompatDecl +%type <ledMap> LedMapDecl +%type <ledName> LedNameDecl +%type <keyCode> KeyNameDecl +%type <keyAlias> KeyAliasDecl +%type <geom> ShapeDecl SectionDecl SectionBody SectionBodyItem RowBody RowBodyItem +%type <geom> Keys Key OverlayDecl OverlayKeyList OverlayKey OutlineList OutlineInList +%type <geom> DoodadDecl +%type <file> XkbFile XkbMapConfig +%type <fileList> XkbMapConfigList +%type <file> XkbCompositeMap + +%destructor { FreeStmt((ParseCommon *) $$); } + <any> <expr> <var> <vmod> <interp> <keyType> <syms> <modMask> <groupCompat> + <ledMap> <ledName> <keyCode> <keyAlias> +%destructor { FreeStmt((ParseCommon *) $$.head); } + <anyList> <exprList> <varList> <vmodList> +/* The destructor also runs on the start symbol when the parser *succeeds*. + * The `if` here catches this case. */ +%destructor { if (!param->rtrn) FreeXkbFile($$); } <file> +%destructor { FreeXkbFile($$.head); } <fileList> +%destructor { free($$); } <str> + +%% + +/* + * An actual file may contain more than one map. However, if we do things + * in the normal yacc way, i.e. aggregate all of the maps into a list and + * let the caller find the map it wants, we end up scanning and parsing a + * lot of unneeded maps (in the end we always just need one). + * Instead of doing that, we make yyparse return one map at a time, and + * then call it repeatedly until we find the map we need. Once we find it, + * we don't need to parse everything that follows in the file. + * This does mean that if we e.g. always use the first map, the file may + * contain complete garbage after that. But it's worth it. + */ + +XkbFile : XkbCompositeMap + { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; } + | XkbMapConfig + { $$ = param->rtrn = $1; param->more_maps = !!param->rtrn; YYACCEPT; } + | END_OF_FILE + { $$ = param->rtrn = NULL; param->more_maps = false; } + ; + +XkbCompositeMap : OptFlags XkbCompositeType OptMapName OBRACE + XkbMapConfigList + CBRACE SEMI + { $$ = XkbFileCreate($2, $3, (ParseCommon *) $5.head, $1); } + ; + +XkbCompositeType: XKB_KEYMAP { $$ = FILE_TYPE_KEYMAP; } + | XKB_SEMANTICS { $$ = FILE_TYPE_KEYMAP; } + | XKB_LAYOUT { $$ = FILE_TYPE_KEYMAP; } + ; + +XkbMapConfigList : XkbMapConfigList XkbMapConfig + { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; } + | XkbMapConfig + { $$.head = $$.last = $1; } + ; + +XkbMapConfig : OptFlags FileType OptMapName OBRACE + DeclList + CBRACE SEMI + { + $$ = XkbFileCreate($2, $3, $5.head, $1); + } + ; + +FileType : XKB_KEYCODES { $$ = FILE_TYPE_KEYCODES; } + | XKB_TYPES { $$ = FILE_TYPE_TYPES; } + | XKB_COMPATMAP { $$ = FILE_TYPE_COMPAT; } + | XKB_SYMBOLS { $$ = FILE_TYPE_SYMBOLS; } + | XKB_GEOMETRY { $$ = FILE_TYPE_GEOMETRY; } + ; + +OptFlags : Flags { $$ = $1; } + | { $$ = 0; } + ; + +Flags : Flags Flag { $$ = ($1 | $2); } + | Flag { $$ = $1; } + ; + +Flag : PARTIAL { $$ = MAP_IS_PARTIAL; } + | DEFAULT { $$ = MAP_IS_DEFAULT; } + | HIDDEN { $$ = MAP_IS_HIDDEN; } + | ALPHANUMERIC_KEYS { $$ = MAP_HAS_ALPHANUMERIC; } + | MODIFIER_KEYS { $$ = MAP_HAS_MODIFIER; } + | KEYPAD_KEYS { $$ = MAP_HAS_KEYPAD; } + | FUNCTION_KEYS { $$ = MAP_HAS_FN; } + | ALTERNATE_GROUP { $$ = MAP_IS_ALTGR; } + ; + +DeclList : DeclList Decl + { + if ($2) { + if ($1.head) { + $$.head = $1.head; $1.last->next = $2; $$.last = $2; + } else { + $$.head = $$.last = $2; + } + } + } + /* + * VModDecl is "inlined" directly into DeclList, i.e. + * each VModDef in the VModDecl is a separate Decl in + * the File. + */ + | DeclList OptMergeMode VModDecl + { + for (VModDef *vmod = $3.head; vmod; vmod = (VModDef *) vmod->common.next) + vmod->merge = $2; + if ($1.head) { + $$.head = $1.head; $1.last->next = &$3.head->common; $$.last = &$3.last->common; + } else { + $$.head = &$3.head->common; $$.last = &$3.last->common; + } + } + | { $$.head = $$.last = NULL; } + ; + +Decl : OptMergeMode VarDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + /* OptMergeMode VModDecl - see above. */ + | OptMergeMode InterpretDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode KeyNameDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode KeyAliasDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode KeyTypeDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode SymbolsDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode ModMapDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode GroupCompatDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode LedMapDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode LedNameDecl + { + $2->merge = $1; + $$ = (ParseCommon *) $2; + } + | OptMergeMode ShapeDecl { $$ = NULL; } + | OptMergeMode SectionDecl { $$ = NULL; } + | OptMergeMode DoodadDecl { $$ = NULL; } + | MergeMode STRING + { + $$ = (ParseCommon *) IncludeCreate(param->ctx, $2, $1); + free($2); + } + ; + +VarDecl : Lhs EQUALS Expr SEMI + { $$ = VarCreate($1, $3); } + | Ident SEMI + { $$ = BoolVarCreate($1, true); } + | EXCLAM Ident SEMI + { $$ = BoolVarCreate($2, false); } + ; + +KeyNameDecl : KEYNAME EQUALS KeyCode SEMI + { $$ = KeycodeCreate($1, $3); } + ; + +KeyAliasDecl : ALIAS KEYNAME EQUALS KEYNAME SEMI + { $$ = KeyAliasCreate($2, $4); } + ; + +VModDecl : VIRTUAL_MODS VModDefList SEMI + { $$ = $2; } + ; + +VModDefList : VModDefList COMMA VModDef + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | VModDef + { $$.head = $$.last = $1; } + ; + +VModDef : Ident + { $$ = VModCreate($1, NULL); } + | Ident EQUALS Expr + { $$ = VModCreate($1, $3); } + ; + +InterpretDecl : INTERPRET InterpretMatch OBRACE + VarDeclList + CBRACE SEMI + { $2->def = $4.head; $$ = $2; } + ; + +InterpretMatch : KeySym PLUS Expr + { $$ = InterpCreate($1, $3); } + | KeySym + { $$ = InterpCreate($1, NULL); } + ; + +VarDeclList : VarDeclList VarDecl + { $$.head = $1.head; $$.last->common.next = &$2->common; $$.last = $2; } + | VarDecl + { $$.head = $$.last = $1; } + ; + +KeyTypeDecl : TYPE String OBRACE + VarDeclList + CBRACE SEMI + { $$ = KeyTypeCreate($2, $4.head); } + ; + +SymbolsDecl : KEY KEYNAME OBRACE + SymbolsBody + CBRACE SEMI + { $$ = SymbolsCreate($2, $4.head); } + ; + +SymbolsBody : SymbolsBody COMMA SymbolsVarDecl + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | SymbolsVarDecl + { $$.head = $$.last = $1; } + | { $$.head = $$.last = NULL; } + ; + +SymbolsVarDecl : Lhs EQUALS Expr { $$ = VarCreate($1, $3); } + | Lhs EQUALS ArrayInit { $$ = VarCreate($1, $3); } + | Ident { $$ = BoolVarCreate($1, true); } + | EXCLAM Ident { $$ = BoolVarCreate($2, false); } + | ArrayInit { $$ = VarCreate(NULL, $1); } + ; + +ArrayInit : OBRACKET OptKeySymList CBRACKET + { $$ = $2; } + | OBRACKET ActionList CBRACKET + { $$ = ExprCreateActionList($2.head); } + ; + +GroupCompatDecl : GROUP Integer EQUALS Expr SEMI + { $$ = GroupCompatCreate($2, $4); } + ; + +ModMapDecl : MODIFIER_MAP Ident OBRACE ExprList CBRACE SEMI + { $$ = ModMapCreate($2, $4.head); } + ; + +LedMapDecl: INDICATOR String OBRACE VarDeclList CBRACE SEMI + { $$ = LedMapCreate($2, $4.head); } + ; + +LedNameDecl: INDICATOR Integer EQUALS Expr SEMI + { $$ = LedNameCreate($2, $4, false); } + | VIRTUAL INDICATOR Integer EQUALS Expr SEMI + { $$ = LedNameCreate($3, $5, true); } + ; + +ShapeDecl : SHAPE String OBRACE OutlineList CBRACE SEMI + { $$ = NULL; } + | SHAPE String OBRACE CoordList CBRACE SEMI + { (void) $4; $$ = NULL; } + ; + +SectionDecl : SECTION String OBRACE SectionBody CBRACE SEMI + { $$ = NULL; } + ; + +SectionBody : SectionBody SectionBodyItem { $$ = NULL;} + | SectionBodyItem { $$ = NULL; } + ; + +SectionBodyItem : ROW OBRACE RowBody CBRACE SEMI + { $$ = NULL; } + | VarDecl + { FreeStmt((ParseCommon *) $1); $$ = NULL; } + | DoodadDecl + { $$ = NULL; } + | LedMapDecl + { FreeStmt((ParseCommon *) $1); $$ = NULL; } + | OverlayDecl + { $$ = NULL; } + ; + +RowBody : RowBody RowBodyItem { $$ = NULL;} + | RowBodyItem { $$ = NULL; } + ; + +RowBodyItem : KEYS OBRACE Keys CBRACE SEMI { $$ = NULL; } + | VarDecl + { FreeStmt((ParseCommon *) $1); $$ = NULL; } + ; + +Keys : Keys COMMA Key { $$ = NULL; } + | Key { $$ = NULL; } + ; + +Key : KEYNAME + { $$ = NULL; } + | OBRACE ExprList CBRACE + { FreeStmt((ParseCommon *) $2.head); $$ = NULL; } + ; + +OverlayDecl : OVERLAY String OBRACE OverlayKeyList CBRACE SEMI + { $$ = NULL; } + ; + +OverlayKeyList : OverlayKeyList COMMA OverlayKey { $$ = NULL; } + | OverlayKey { $$ = NULL; } + ; + +OverlayKey : KEYNAME EQUALS KEYNAME { $$ = NULL; } + ; + +OutlineList : OutlineList COMMA OutlineInList + { $$ = NULL;} + | OutlineInList + { $$ = NULL; } + ; + +OutlineInList : OBRACE CoordList CBRACE + { (void) $2; $$ = NULL; } + | Ident EQUALS OBRACE CoordList CBRACE + { (void) $4; $$ = NULL; } + | Ident EQUALS Expr + { FreeStmt((ParseCommon *) $3); $$ = NULL; } + ; + +CoordList : CoordList COMMA Coord + { (void) $1; (void) $3; $$ = NULL; } + | Coord + { (void) $1; $$ = NULL; } + ; + +Coord : OBRACKET SignedNumber COMMA SignedNumber CBRACKET + { $$ = NULL; } + ; + +DoodadDecl : DoodadType String OBRACE VarDeclList CBRACE SEMI + { FreeStmt((ParseCommon *) $4.head); $$ = NULL; } + ; + +DoodadType : TEXT { $$ = 0; } + | OUTLINE { $$ = 0; } + | SOLID { $$ = 0; } + | LOGO { $$ = 0; } + ; + +FieldSpec : Ident { $$ = $1; } + | Element { $$ = $1; } + ; + +Element : ACTION_TOK + { $$ = xkb_atom_intern_literal(param->ctx, "action"); } + | INTERPRET + { $$ = xkb_atom_intern_literal(param->ctx, "interpret"); } + | TYPE + { $$ = xkb_atom_intern_literal(param->ctx, "type"); } + | KEY + { $$ = xkb_atom_intern_literal(param->ctx, "key"); } + | GROUP + { $$ = xkb_atom_intern_literal(param->ctx, "group"); } + | MODIFIER_MAP + {$$ = xkb_atom_intern_literal(param->ctx, "modifier_map");} + | INDICATOR + { $$ = xkb_atom_intern_literal(param->ctx, "indicator"); } + | SHAPE + { $$ = xkb_atom_intern_literal(param->ctx, "shape"); } + | ROW + { $$ = xkb_atom_intern_literal(param->ctx, "row"); } + | SECTION + { $$ = xkb_atom_intern_literal(param->ctx, "section"); } + | TEXT + { $$ = xkb_atom_intern_literal(param->ctx, "text"); } + ; + +OptMergeMode : MergeMode { $$ = $1; } + | { $$ = MERGE_DEFAULT; } + ; + +MergeMode : INCLUDE { $$ = MERGE_DEFAULT; } + | AUGMENT { $$ = MERGE_AUGMENT; } + | OVERRIDE { $$ = MERGE_OVERRIDE; } + | REPLACE { $$ = MERGE_REPLACE; } + | ALTERNATE + { + /* + * This used to be MERGE_ALT_FORM. This functionality was + * unused and has been removed. + */ + $$ = MERGE_DEFAULT; + } + ; + +OptExprList : ExprList { $$ = $1; } + | { $$.head = $$.last = NULL; } + ; + +ExprList : ExprList COMMA Expr + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | Expr + { $$.head = $$.last = $1; } + ; + +Expr : Expr DIVIDE Expr + { $$ = ExprCreateBinary(EXPR_DIVIDE, $1, $3); } + | Expr PLUS Expr + { $$ = ExprCreateBinary(EXPR_ADD, $1, $3); } + | Expr MINUS Expr + { $$ = ExprCreateBinary(EXPR_SUBTRACT, $1, $3); } + | Expr TIMES Expr + { $$ = ExprCreateBinary(EXPR_MULTIPLY, $1, $3); } + | Lhs EQUALS Expr + { $$ = ExprCreateBinary(EXPR_ASSIGN, $1, $3); } + | Term + { $$ = $1; } + ; + +Term : MINUS Term + { $$ = ExprCreateUnary(EXPR_NEGATE, $2->expr.value_type, $2); } + | PLUS Term + { $$ = ExprCreateUnary(EXPR_UNARY_PLUS, $2->expr.value_type, $2); } + | EXCLAM Term + { $$ = ExprCreateUnary(EXPR_NOT, EXPR_TYPE_BOOLEAN, $2); } + | INVERT Term + { $$ = ExprCreateUnary(EXPR_INVERT, $2->expr.value_type, $2); } + | Lhs + { $$ = $1; } + | FieldSpec OPAREN OptExprList CPAREN %prec OPAREN + { $$ = ExprCreateAction($1, $3.head); } + | Terminal + { $$ = $1; } + | OPAREN Expr CPAREN + { $$ = $2; } + ; + +ActionList : ActionList COMMA Action + { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } + | Action + { $$.head = $$.last = $1; } + ; + +Action : FieldSpec OPAREN OptExprList CPAREN + { $$ = ExprCreateAction($1, $3.head); } + ; + +Lhs : FieldSpec + { $$ = ExprCreateIdent($1); } + | FieldSpec DOT FieldSpec + { $$ = ExprCreateFieldRef($1, $3); } + | FieldSpec OBRACKET Expr CBRACKET + { $$ = ExprCreateArrayRef(XKB_ATOM_NONE, $1, $3); } + | FieldSpec DOT FieldSpec OBRACKET Expr CBRACKET + { $$ = ExprCreateArrayRef($1, $3, $5); } + ; + +Terminal : String + { $$ = ExprCreateString($1); } + | Integer + { $$ = ExprCreateInteger($1); } + | Float + { $$ = ExprCreateFloat(/* Discard $1 */); } + | KEYNAME + { $$ = ExprCreateKeyName($1); } + ; + +OptKeySymList : KeySymList { $$ = $1; } + | { $$ = NULL; } + ; + +KeySymList : KeySymList COMMA KeySym + { $$ = ExprAppendKeysymList($1, $3); } + | KeySymList COMMA KeySyms + { $$ = ExprAppendMultiKeysymList($1, $3); } + | KeySym + { $$ = ExprCreateKeysymList($1); } + | KeySyms + { $$ = ExprCreateMultiKeysymList($1); } + ; + +KeySyms : OBRACE KeySymList CBRACE + { $$ = $2; } + ; + +KeySym : IDENT + { + if (!resolve_keysym($1, &$$)) + parser_warn(param, "unrecognized keysym \"%s\"", $1); + free($1); + } + | SECTION { $$ = XKB_KEY_section; } + | Integer + { + if ($1 < 0) { + parser_warn(param, "unrecognized keysym \"%" PRId64 "\"", $1); + $$ = XKB_KEY_NoSymbol; + } + else if ($1 < 10) { /* XKB_KEY_0 .. XKB_KEY_9 */ + $$ = XKB_KEY_0 + (xkb_keysym_t) $1; + } + else { + char buf[32]; + snprintf(buf, sizeof(buf), "0x%"PRIx64, $1); + if (!resolve_keysym(buf, &$$)) { + parser_warn(param, "unrecognized keysym \"%s\"", buf); + $$ = XKB_KEY_NoSymbol; + } + } + } + ; + +SignedNumber : MINUS Number { $$ = -$2; } + | Number { $$ = $1; } + ; + +Number : FLOAT { $$ = $1; } + | INTEGER { $$ = $1; } + ; + +Float : FLOAT { $$ = 0; } + ; + +Integer : INTEGER { $$ = $1; } + ; + +KeyCode : INTEGER { $$ = $1; } + ; + +Ident : IDENT { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); } + | DEFAULT { $$ = xkb_atom_intern_literal(param->ctx, "default"); } + ; + +String : STRING { $$ = xkb_atom_intern(param->ctx, $1, strlen($1)); free($1); } + ; + +OptMapName : MapName { $$ = $1; } + | { $$ = NULL; } + ; + +MapName : STRING { $$ = $1; } + ; + +%% + +XkbFile * +parse(struct xkb_context *ctx, struct scanner *scanner, const char *map) +{ + int ret; + XkbFile *first = NULL; + struct parser_param param = { + .scanner = scanner, + .ctx = ctx, + .rtrn = NULL, + .more_maps = false, + }; + + /* + * If we got a specific map, we look for it exclusively and return + * immediately upon finding it. Otherwise, we need to get the + * default map. If we find a map marked as default, we return it + * immediately. If there are no maps marked as default, we return + * the first map in the file. + */ + + while ((ret = yyparse(¶m)) == 0 && param.more_maps) { + if (map) { + if (streq_not_null(map, param.rtrn->name)) + return param.rtrn; + else + FreeXkbFile(param.rtrn); + } + else { + if (param.rtrn->flags & MAP_IS_DEFAULT) { + FreeXkbFile(first); + return param.rtrn; + } + else if (!first) { + first = param.rtrn; + } + else { + FreeXkbFile(param.rtrn); + } + } + param.rtrn = NULL; + } + + if (ret != 0) { + FreeXkbFile(first); + return NULL; + } + + if (first) + log_vrb(ctx, 5, + "No map in include statement, but \"%s\" contains several; " + "Using first defined map, \"%s\"\n", + scanner->file_name, first->name); + + return first; +} diff --git a/src/xkbcomp/rules.c b/src/xkbcomp/rules.c new file mode 100644 index 0000000..099500a --- /dev/null +++ b/src/xkbcomp/rules.c @@ -0,0 +1,1163 @@ +/************************************************************ + * 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 © 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 "include.h" +#include "scanner-utils.h" + +#define MAX_INCLUDE_DEPTH 5 + +/* Scanner / Lexer */ + +/* Values returned with some tokens, like yylval. */ +union lvalue { + struct sval string; +}; + +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 inline bool +is_ident(char ch) +{ + return is_graph(ch) && ch != '\\'; +} + +static enum rules_token +lex(struct scanner *s, union lvalue *val) +{ +skip_more_whitespace_and_comments: + /* Skip spaces. */ + while (chr(s, ' ') || chr(s, '\t') || chr(s, '\r')); + + /* Skip comments. */ + if (lit(s, "//")) { + skip_to_eol(s); + } + + /* New line. */ + if (eol(s)) { + while (eol(s)) next(s); + return TOK_END_OF_LINE; + } + + /* Escaped line continuation. */ + if (chr(s, '\\')) { + /* Optional \r. */ + chr(s, '\r'); + if (!eol(s)) { + scanner_err(s, "illegal new line escape; must appear at end of line"); + return TOK_ERROR; + } + next(s); + goto skip_more_whitespace_and_comments; + } + + /* See if we're done. */ + if (eof(s)) return TOK_END_OF_FILE; + + /* New token. */ + s->token_line = s->line; + s->token_column = s->column; + + /* Operators and punctuation. */ + if (chr(s, '!')) return TOK_BANG; + if (chr(s, '=')) return TOK_EQUALS; + if (chr(s, '*')) return TOK_STAR; + + /* Group name. */ + if (chr(s, '$')) { + val->string.start = s->s + s->pos; + val->string.len = 0; + while (is_ident(peek(s))) { + 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; + } + + /* Include statement. */ + if (lit(s, "include")) + return TOK_INCLUDE; + + /* Identifier. */ + if (is_ident(peek(s))) { + val->string.start = s->s + s->pos; + val->string.len = 0; + while (is_ident(peek(s))) { + next(s); + val->string.len++; + } + return TOK_IDENTIFIER; + } + + scanner_err(s, "unrecognized token"); + return TOK_ERROR; +} + +/***====================================================================***/ + +enum rules_mlvo { + MLVO_MODEL, + MLVO_LAYOUT, + MLVO_VARIANT, + MLVO_OPTION, + _MLVO_NUM_ENTRIES +}; + +#define SVAL_LIT(literal) { literal, sizeof(literal) - 1 } + +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"), +}; + +enum rules_kccgst { + KCCGST_KEYCODES, + KCCGST_TYPES, + KCCGST_COMPAT, + KCCGST_SYMBOLS, + KCCGST_GEOMETRY, + _KCCGST_NUM_ENTRIES +}; + +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"), +}; + +/* 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; + +/* + * 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 { + struct sval name; + darray_sval elements; +}; + +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; +}; + +enum mlvo_match_type { + MLVO_MATCH_NORMAL = 0, + MLVO_MATCH_WILDCARD, + MLVO_MATCH_GROUP, +}; + +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; +}; + +/* + * 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()). + */ +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]; +}; + +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; +} + +static darray_matched_sval +split_comma_separated_mlvo(const char *s) +{ + darray_matched_sval arr = darray_new(); + + /* + * Make sure the array returned by this function always includes at + * least one value, e.g. "" -> { "" } and "," -> { "", "" }. + */ + + if (!s) { + struct matched_sval val = { .sval = { NULL, 0 } }; + darray_append(arr, val); + return arr; + } + + 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++; + } + + return arr; +} + +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; + + 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); + + return m; +} + +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); +} + +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); +} + +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; + } + + while (!eof(&s) && !eol(&s)) { + if (chr(&s, '%')) { + if (chr(&s, '%')) { + buf_append(&s, '%'); + } + else if (chr(&s, 'H')) { + const char *home = secure_getenv("HOME"); + if (!home) { + scanner_err(&s, "%%H was used in an include statement, but the HOME environment variable is not set"); + return; + } + if (!buf_appends(&s, home)) { + scanner_err(&s, "include path after expanding %%H is too long"); + return; + } + } + else if (chr(&s, 'S')) { + const char *default_root = xkb_context_include_path_get_system_path(m->ctx); + if (!buf_appends(&s, default_root) || !buf_appends(&s, "/rules")) { + scanner_err(&s, "include path after expanding %%S is too long"); + return; + } + } + else if (chr(&s, 'E')) { + const char *default_root = xkb_context_include_path_get_extra_path(m->ctx); + if (!buf_appends(&s, default_root) || !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", peek(&s)); + return; + } + } + else { + buf_append(&s, next(&s)); + } + } + if (!buf_append(&s, '\0')) { + scanner_err(&s, "include path is too long"); + 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, "No components returned from included XKB rules \"%s\"\n", s.buf); + fclose(file); + } else { + log_err(m->ctx, "Failed to open included XKB rules \"%s\"\n", s.buf); + } +} + +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; +} + +static int +extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out) +{ + /* 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; +} + +static void +matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s, + struct sval ident) +{ + enum rules_mlvo mlvo; + struct sval mlvo_sval; + + for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) { + mlvo_sval = rules_mlvo_svals[mlvo]; + + if (svaleq_prefix(mlvo_sval, ident)) + break; + } + + /* 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; + } + + 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; + } + + /* 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; + } + + 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++; +} + +static void +matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident) +{ + 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; + } + + /* 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; + } + + 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; + } + + m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst; + m->mapping.defined_kccgst_mask |= 1u << kccgst; + m->mapping.num_kccgst++; +} + +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; + } + + 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; + } + + /* + * 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; + } + } + + 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; + +skip: + m->mapping.skip = true; +} + +static void +matcher_rule_start_new(struct matcher *m) +{ + memset(&m->rule, 0, sizeof(m->rule)); + m->rule.skip = m->mapping.skip; +} + +static void +matcher_rule_set_mlvo_common(struct matcher *m, struct scanner *s, + struct sval ident, + enum mlvo_match_type match_type) +{ + 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 void +matcher_rule_set_mlvo_wildcard(struct matcher *m, struct scanner *s) +{ + struct sval dummy = { NULL, 0 }; + matcher_rule_set_mlvo_common(m, s, dummy, MLVO_MATCH_WILDCARD); +} + +static void +matcher_rule_set_mlvo_group(struct matcher *m, struct scanner *s, + struct sval ident) +{ + matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_GROUP); +} + +static void +matcher_rule_set_mlvo(struct matcher *m, struct scanner *s, + struct sval ident) +{ + matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_NORMAL); +} + +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++; +} + +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; + } + } + + 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 bool +match_value(struct matcher *m, struct sval val, struct sval to, + enum mlvo_match_type match_type) +{ + 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 +match_value_and_mark(struct matcher *m, struct sval val, + struct matched_sval *to, enum mlvo_match_type match_type) +{ + 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 +append_expanded_kccgst_value(struct matcher *m, struct scanner *s, + darray_char *to, struct sval value) +{ + 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; + } + 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; + } + + /* 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; + } + + /* Check for index. */ + idx = XKB_LAYOUT_INVALID; + if (i < value.len && str[i] == '[') { + int consumed; + + if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) { + scanner_err(s, "invalid index in %%-expansion; may only index layout or variant"); + goto error; + } + + consumed = extract_layout_index(str + i, value.len - i, &idx); + if (consumed == -1) goto error; + i += consumed; + } + + /* Check for suffix, if there supposed to be one. */ + if (sfx != 0) { + if (i >= value.len) goto error; + if (str[i++] != sfx) goto error; + } + + /* 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; + } + + /* If we didn't get one, skip silently. */ + if (!expanded_value || expanded_value->sval.len == 0) + continue; + + 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 +matcher_rule_verify(struct matcher *m, struct scanner *s) +{ + 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 void +matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) +{ + 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); + } + 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 (!matched) + return; + } + + 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); + } + + /* + * 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 enum rules_token +gettok(struct matcher *m, struct scanner *s) +{ + return lex(s, &m->val); +} + +static bool +matcher_match(struct matcher *m, struct scanner *s, + unsigned include_depth, + const char *string, size_t len, + const char *file_name) +{ + 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; + } + +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; + } + +group_name: + switch (tok = gettok(m, s)) { + case TOK_EQUALS: + goto group_element; + default: + goto unexpected; + } + +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; + } + +include_statement: + switch (tok = gettok(m, s)) { + case TOK_IDENTIFIER: + matcher_include(m, s, include_depth, m->val.string); + goto initial; + default: + goto unexpected; + } + +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; + } + +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; + } + +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; + } + +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; + } + +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; + } + +unexpected: + switch (tok) { + case TOK_ERROR: + goto error; + default: + goto state_error; + } + +finish: + return true; + +state_error: + scanner_err(s, "unexpected token"); +error: + return false; +} + +static bool +read_rules_file(struct xkb_context *ctx, + struct matcher *matcher, + unsigned include_depth, + FILE *file, + const char *path) +{ + bool ret = false; + char *string; + size_t size; + struct scanner scanner; + + ret = map_file(file, &string, &size); + if (!ret) { + log_err(ctx, "Couldn't read rules file \"%s\": %s\n", + path, strerror(errno)); + goto out; + } + + scanner_init(&scanner, matcher->ctx, string, size, path, NULL); + + ret = matcher_match(matcher, &scanner, include_depth, string, size, path); + + 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, "No components returned from XKB rules \"%s\"\n", path); + ret = false; + goto err_out; + } + + 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, "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, "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, "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, "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; +} diff --git a/src/xkbcomp/rules.h b/src/xkbcomp/rules.h new file mode 100644 index 0000000..5381b15 --- /dev/null +++ b/src/xkbcomp/rules.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2009 Dan Nicholson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef XKBCOMP_RULES_H +#define XKBCOMP_RULES_H + +bool +xkb_components_from_rules(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo, + struct xkb_component_names *out); + +#endif diff --git a/src/xkbcomp/scanner.c b/src/xkbcomp/scanner.c new file mode 100644 index 0000000..b349499 --- /dev/null +++ b/src/xkbcomp/scanner.c @@ -0,0 +1,212 @@ +/* + * 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 "parser-priv.h" +#include "scanner-utils.h" + +static bool +number(struct scanner *s, int64_t *out, int *out_tok) +{ + bool is_float = false, is_hex = false; + const char *start = s->s + s->pos; + char *end; + + if (lit(s, "0x")) { + while (is_xdigit(peek(s))) next(s); + is_hex = true; + } + else { + while (is_digit(peek(s))) next(s); + is_float = chr(s, '.'); + while (is_digit(peek(s))) next(s); + } + if (s->s + s->pos == start) + return false; + + errno = 0; + if (is_hex) + *out = strtoul(start, &end, 16); + else if (is_float) + /* The parser currently just ignores floats, so the cast is + * fine - the value doesn't matter. */ + *out = strtod(start, &end); + else + *out = strtoul(start, &end, 10); + if (errno != 0 || s->s + s->pos != end) + *out_tok = ERROR_TOK; + else + *out_tok = (is_float ? FLOAT : INTEGER); + return true; +} + +int +_xkbcommon_lex(YYSTYPE *yylval, struct scanner *s) +{ + int tok; + +skip_more_whitespace_and_comments: + /* Skip spaces. */ + while (is_space(peek(s))) next(s); + + /* Skip comments. */ + if (lit(s, "//") || chr(s, '#')) { + skip_to_eol(s); + goto skip_more_whitespace_and_comments; + } + + /* See if we're done. */ + if (eof(s)) return END_OF_FILE; + + /* New token. */ + s->token_line = s->line; + s->token_column = s->column; + s->buf_pos = 0; + + /* String literal. */ + if (chr(s, '\"')) { + while (!eof(s) && !eol(s) && peek(s) != '\"') { + if (chr(s, '\\')) { + uint8_t o; + if (chr(s, '\\')) buf_append(s, '\\'); + else if (chr(s, 'n')) buf_append(s, '\n'); + else if (chr(s, 't')) buf_append(s, '\t'); + else if (chr(s, 'r')) buf_append(s, '\r'); + else if (chr(s, 'b')) buf_append(s, '\b'); + else if (chr(s, 'f')) buf_append(s, '\f'); + else if (chr(s, 'v')) buf_append(s, '\v'); + else if (chr(s, 'e')) buf_append(s, '\033'); + else if (oct(s, &o)) buf_append(s, (char) o); + else { + scanner_warn(s, "unknown escape sequence in string literal"); + /* Ignore. */ + } + } else { + buf_append(s, next(s)); + } + } + if (!buf_append(s, '\0') || !chr(s, '\"')) { + scanner_err(s, "unterminated string literal"); + return ERROR_TOK; + } + yylval->str = strdup(s->buf); + if (!yylval->str) + return ERROR_TOK; + return STRING; + } + + /* Key name literal. */ + if (chr(s, '<')) { + while (is_graph(peek(s)) && peek(s) != '>') + buf_append(s, next(s)); + if (!buf_append(s, '\0') || !chr(s, '>')) { + scanner_err(s, "unterminated key name literal"); + return ERROR_TOK; + } + /* Empty key name literals are allowed. */ + yylval->atom = xkb_atom_intern(s->ctx, s->buf, s->buf_pos - 1); + return KEYNAME; + } + + /* Operators and punctuation. */ + if (chr(s, ';')) return SEMI; + if (chr(s, '{')) return OBRACE; + if (chr(s, '}')) return CBRACE; + if (chr(s, '=')) return EQUALS; + if (chr(s, '[')) return OBRACKET; + if (chr(s, ']')) return CBRACKET; + if (chr(s, '(')) return OPAREN; + if (chr(s, ')')) return CPAREN; + if (chr(s, '.')) return DOT; + if (chr(s, ',')) return COMMA; + if (chr(s, '+')) return PLUS; + if (chr(s, '-')) return MINUS; + if (chr(s, '*')) return TIMES; + if (chr(s, '/')) return DIVIDE; + if (chr(s, '!')) return EXCLAM; + if (chr(s, '~')) return INVERT; + + /* Identifier. */ + if (is_alpha(peek(s)) || peek(s) == '_') { + s->buf_pos = 0; + while (is_alnum(peek(s)) || peek(s) == '_') + buf_append(s, next(s)); + if (!buf_append(s, '\0')) { + scanner_err(s, "identifier too long"); + return ERROR_TOK; + } + + /* Keyword. */ + tok = keyword_to_token(s->buf, s->buf_pos - 1); + if (tok != -1) return tok; + + yylval->str = strdup(s->buf); + if (!yylval->str) + return ERROR_TOK; + return IDENT; + } + + /* Number literal (hexadecimal / decimal / float). */ + if (number(s, &yylval->num, &tok)) { + if (tok == ERROR_TOK) { + scanner_err(s, "malformed number literal"); + return ERROR_TOK; + } + return tok; + } + + scanner_err(s, "unrecognized token"); + return ERROR_TOK; +} + +XkbFile * +XkbParseString(struct xkb_context *ctx, const char *string, size_t len, + const char *file_name, const char *map) +{ + struct scanner scanner; + scanner_init(&scanner, ctx, string, len, file_name, NULL); + return parse(ctx, &scanner, map); +} + +XkbFile * +XkbParseFile(struct xkb_context *ctx, FILE *file, + const char *file_name, const char *map) +{ + bool ok; + XkbFile *xkb_file; + char *string; + size_t size; + + ok = map_file(file, &string, &size); + if (!ok) { + log_err(ctx, "Couldn't read XKB file %s: %s\n", + file_name, strerror(errno)); + return NULL; + } + + xkb_file = XkbParseString(ctx, string, size, file_name, map); + unmap_file(string, size); + return xkb_file; +} diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c new file mode 100644 index 0000000..eb78412 --- /dev/null +++ b/src/xkbcomp/symbols.c @@ -0,0 +1,1600 @@ +/************************************************************ + * Copyright (c) 1994 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 © 2012 Intel Corporation + * 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. + * + * Author: Daniel Stone <daniel@fooishbar.org> + * Ran Benita <ran234@gmail.com> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "action.h" +#include "vmod.h" +#include "include.h" +#include "keysym.h" + +enum key_repeat { + KEY_REPEAT_UNDEFINED = 0, + KEY_REPEAT_YES = 1, + KEY_REPEAT_NO = 2, +}; + +enum group_field { + GROUP_FIELD_SYMS = (1 << 0), + GROUP_FIELD_ACTS = (1 << 1), + GROUP_FIELD_TYPE = (1 << 2), +}; + +enum key_field { + KEY_FIELD_REPEAT = (1 << 0), + KEY_FIELD_DEFAULT_TYPE = (1 << 1), + KEY_FIELD_GROUPINFO = (1 << 2), + KEY_FIELD_VMODMAP = (1 << 3), +}; + +typedef struct { + enum group_field defined; + darray(struct xkb_level) levels; + xkb_atom_t type; +} GroupInfo; + +typedef struct { + enum key_field defined; + enum merge_mode merge; + + xkb_atom_t name; + + darray(GroupInfo) groups; + + enum key_repeat repeat; + xkb_mod_mask_t vmodmap; + xkb_atom_t default_type; + + enum xkb_range_exceed_type out_of_range_group_action; + xkb_layout_index_t out_of_range_group_number; +} KeyInfo; + +static void +ClearLevelInfo(struct xkb_level *leveli) +{ + if (leveli->num_syms > 1) + free(leveli->u.syms); +} + +static void +InitGroupInfo(GroupInfo *groupi) +{ + memset(groupi, 0, sizeof(*groupi)); +} + +static void +ClearGroupInfo(GroupInfo *groupi) +{ + struct xkb_level *leveli; + darray_foreach(leveli, groupi->levels) + ClearLevelInfo(leveli); + darray_free(groupi->levels); +} + +static void +CopyGroupInfo(GroupInfo *to, const GroupInfo *from) +{ + to->defined = from->defined; + to->type = from->type; + darray_init(to->levels); + darray_copy(to->levels, from->levels); + for (xkb_level_index_t j = 0; j < darray_size(to->levels); j++) + if (darray_item(from->levels, j).num_syms > 1) + darray_item(to->levels, j).u.syms = + memdup(darray_item(from->levels, j).u.syms, + darray_item(from->levels, j).num_syms, + sizeof(xkb_keysym_t)); +} + +static void +InitKeyInfo(struct xkb_context *ctx, KeyInfo *keyi) +{ + memset(keyi, 0, sizeof(*keyi)); + keyi->merge = MERGE_OVERRIDE; + keyi->name = xkb_atom_intern_literal(ctx, "*"); + keyi->out_of_range_group_action = RANGE_WRAP; +} + +static void +ClearKeyInfo(KeyInfo *keyi) +{ + GroupInfo *groupi; + darray_foreach(groupi, keyi->groups) + ClearGroupInfo(groupi); + darray_free(keyi->groups); +} + +/***====================================================================***/ + +typedef struct { + enum merge_mode merge; + bool haveSymbol; + xkb_mod_index_t modifier; + union { + xkb_atom_t keyName; + xkb_keysym_t keySym; + } u; +} ModMapEntry; + +typedef struct { + char *name; /* e.g. pc+us+inet(evdev) */ + int errorCount; + enum merge_mode merge; + xkb_layout_index_t explicit_group; + darray(KeyInfo) keys; + KeyInfo default_key; + ActionsInfo *actions; + darray(xkb_atom_t) group_names; + darray(ModMapEntry) modmaps; + struct xkb_mod_set mods; + + struct xkb_context *ctx; + /* Needed for AddKeySymbols. */ + const struct xkb_keymap *keymap; +} SymbolsInfo; + +static void +InitSymbolsInfo(SymbolsInfo *info, const struct xkb_keymap *keymap, + ActionsInfo *actions, const struct xkb_mod_set *mods) +{ + memset(info, 0, sizeof(*info)); + info->ctx = keymap->ctx; + info->keymap = keymap; + info->merge = MERGE_OVERRIDE; + InitKeyInfo(keymap->ctx, &info->default_key); + info->actions = actions; + info->mods = *mods; + info->explicit_group = XKB_LAYOUT_INVALID; +} + +static void +ClearSymbolsInfo(SymbolsInfo *info) +{ + KeyInfo *keyi; + free(info->name); + darray_foreach(keyi, info->keys) + ClearKeyInfo(keyi); + darray_free(info->keys); + darray_free(info->group_names); + darray_free(info->modmaps); + ClearKeyInfo(&info->default_key); +} + +static const char * +KeyInfoText(SymbolsInfo *info, KeyInfo *keyi) +{ + return KeyNameText(info->ctx, keyi->name); +} + +static bool +MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber, + bool report, xkb_layout_index_t group, xkb_atom_t key_name) +{ + xkb_level_index_t i, levels_in_both; + struct xkb_level *level; + + /* First find the type of the merged group. */ + if (into->type != from->type) { + if (from->type == XKB_ATOM_NONE) { + /* it's empty for consistency with other comparisons */ + } + else if (into->type == XKB_ATOM_NONE) { + into->type = from->type; + } + else { + xkb_atom_t use = (clobber ? from->type : into->type); + xkb_atom_t ignore = (clobber ? into->type : from->type); + + if (report) + log_warn(info->ctx, + "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; + } + } + into->defined |= (from->defined & GROUP_FIELD_TYPE); + + /* Now look at the levels. */ + + if (darray_empty(from->levels)) { + InitGroupInfo(from); + return true; + } + + if (darray_empty(into->levels)) { + from->type = into->type; + *into = *from; + InitGroupInfo(from); + return true; + } + + /* Merge the actions and syms. */ + levels_in_both = MIN(darray_size(into->levels), darray_size(from->levels)); + for (i = 0; i < levels_in_both; i++) { + struct xkb_level *intoLevel = &darray_item(into->levels, i); + struct xkb_level *fromLevel = &darray_item(from->levels, i); + + if (fromLevel->action.type == ACTION_TYPE_NONE) { + /* it's empty for consistency with other comparisons */ + } + else if (intoLevel->action.type == ACTION_TYPE_NONE) { + intoLevel->action = fromLevel->action; + } + else { + union xkb_action *use, *ignore; + use = (clobber ? &fromLevel->action : &intoLevel->action); + ignore = (clobber ? &intoLevel->action : &fromLevel->action); + + if (report) + log_warn(info->ctx, + "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; + } + + if (fromLevel->num_syms == 0) { + /* it's empty for consistency with other comparisons */ + } + else if (intoLevel->num_syms == 0) { + intoLevel->num_syms = fromLevel->num_syms; + if (fromLevel->num_syms > 1) + intoLevel->u.syms = fromLevel->u.syms; + else + intoLevel->u.sym = fromLevel->u.sym; + fromLevel->num_syms = 0; + } + else if (!XkbLevelsSameSyms(fromLevel, intoLevel)) { + if (report) + log_warn(info->ctx, + "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); + intoLevel->num_syms = fromLevel->num_syms; + if (fromLevel->num_syms > 1) + intoLevel->u.syms = fromLevel->u.syms; + else + intoLevel->u.sym = fromLevel->u.sym; + fromLevel->num_syms = 0; + } + } + } + /* If @from has extra levels, get them as well. */ + darray_foreach_from(level, from->levels, levels_in_both) { + darray_append(into->levels, *level); + level->num_syms = 0; + } + into->defined |= (from->defined & GROUP_FIELD_ACTS); + into->defined |= (from->defined & GROUP_FIELD_SYMS); + + return true; +} + +static bool +UseNewKeyField(enum key_field field, enum key_field old, enum key_field new, + bool clobber, bool report, enum key_field *collide) +{ + if (!(old & field)) + return (new & field); + + if (new & field) { + if (report) + *collide |= field; + + if (clobber) + return true; + } + + return false; +} + +static bool +MergeKeys(SymbolsInfo *info, KeyInfo *into, KeyInfo *from, bool same_file) +{ + xkb_layout_index_t i; + xkb_layout_index_t groups_in_both; + enum key_field collide = 0; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + const bool clobber = (from->merge != MERGE_AUGMENT); + const bool report = (same_file && verbosity > 0) || verbosity > 9; + + if (from->merge == MERGE_REPLACE) { + ClearKeyInfo(into); + *into = *from; + InitKeyInfo(info->ctx, from); + return true; + } + + groups_in_both = MIN(darray_size(into->groups), darray_size(from->groups)); + for (i = 0; i < groups_in_both; i++) + MergeGroups(info, + &darray_item(into->groups, i), + &darray_item(from->groups, i), + clobber, report, i, into->name); + /* If @from has extra groups, just move them to @into. */ + for (i = groups_in_both; i < darray_size(from->groups); i++) { + darray_append(into->groups, darray_item(from->groups, i)); + InitGroupInfo(&darray_item(from->groups, i)); + } + + if (UseNewKeyField(KEY_FIELD_VMODMAP, into->defined, from->defined, + clobber, report, &collide)) { + into->vmodmap = from->vmodmap; + into->defined |= KEY_FIELD_VMODMAP; + } + if (UseNewKeyField(KEY_FIELD_REPEAT, into->defined, from->defined, + clobber, report, &collide)) { + into->repeat = from->repeat; + into->defined |= KEY_FIELD_REPEAT; + } + if (UseNewKeyField(KEY_FIELD_DEFAULT_TYPE, into->defined, from->defined, + clobber, report, &collide)) { + into->default_type = from->default_type; + into->defined |= KEY_FIELD_DEFAULT_TYPE; + } + if (UseNewKeyField(KEY_FIELD_GROUPINFO, into->defined, from->defined, + clobber, report, &collide)) { + into->out_of_range_group_action = from->out_of_range_group_action; + into->out_of_range_group_number = from->out_of_range_group_number; + into->defined |= KEY_FIELD_GROUPINFO; + } + + if (collide) + log_warn(info->ctx, + "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); + return true; +} + +/* TODO: Make it so this function doesn't need the entire keymap. */ +static bool +AddKeySymbols(SymbolsInfo *info, KeyInfo *keyi, bool same_file) +{ + xkb_atom_t real_name; + KeyInfo *iter; + + /* + * Don't keep aliases in the keys array; this guarantees that + * searching for keys to merge with by straight comparison (see the + * following loop) is enough, and we won't get multiple KeyInfo's + * for the same key because of aliases. + */ + real_name = XkbResolveKeyAlias(info->keymap, keyi->name); + if (real_name != XKB_ATOM_NONE) + keyi->name = real_name; + + darray_foreach(iter, info->keys) + if (iter->name == keyi->name) + return MergeKeys(info, iter, keyi, same_file); + + darray_append(info->keys, *keyi); + InitKeyInfo(info->ctx, keyi); + return true; +} + +static bool +AddModMapEntry(SymbolsInfo *info, ModMapEntry *new) +{ + ModMapEntry *old; + bool clobber = (new->merge != MERGE_AUGMENT); + + darray_foreach(old, info->modmaps) { + xkb_mod_index_t use, ignore; + + if ((new->haveSymbol != old->haveSymbol) || + (new->haveSymbol && new->u.keySym != old->u.keySym) || + (!new->haveSymbol && new->u.keyName != old->u.keyName)) + continue; + + if (new->modifier == old->modifier) + return true; + + use = (clobber ? new->modifier : old->modifier); + ignore = (clobber ? old->modifier : new->modifier); + + if (new->haveSymbol) + log_warn(info->ctx, + "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, + "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; + } + + darray_append(info->modmaps, *new); + return true; +} + +/***====================================================================***/ + +static void +MergeIncludedSymbols(SymbolsInfo *into, SymbolsInfo *from, + enum merge_mode merge) +{ + xkb_atom_t *group_name; + xkb_layout_index_t group_names_in_both; + + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + into->mods = from->mods; + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + group_names_in_both = MIN(darray_size(into->group_names), + darray_size(from->group_names)); + for (xkb_layout_index_t i = 0; i < group_names_in_both; i++) { + if (!darray_item(from->group_names, i)) + continue; + + if (merge == MERGE_AUGMENT && darray_item(into->group_names, i)) + continue; + + darray_item(into->group_names, i) = darray_item(from->group_names, i); + } + /* If @from has more, get them as well. */ + darray_foreach_from(group_name, from->group_names, group_names_in_both) + darray_append(into->group_names, *group_name); + + if (darray_empty(into->keys)) { + into->keys = from->keys; + darray_init(from->keys); + } + else { + KeyInfo *keyi; + darray_foreach(keyi, from->keys) { + keyi->merge = (merge == MERGE_DEFAULT ? keyi->merge : merge); + if (!AddKeySymbols(into, keyi, false)) + into->errorCount++; + } + } + + if (darray_empty(into->modmaps)) { + into->modmaps = from->modmaps; + darray_init(from->modmaps); + } + else { + ModMapEntry *mm; + darray_foreach(mm, from->modmaps) { + mm->merge = (merge == MERGE_DEFAULT ? mm->merge : merge); + if (!AddModMapEntry(into, mm)) + into->errorCount++; + } + } +} + +static void +HandleSymbolsFile(SymbolsInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeSymbols(SymbolsInfo *info, IncludeStmt *include) +{ + SymbolsInfo included; + + InitSymbolsInfo(&included, info->keymap, info->actions, &info->mods); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + SymbolsInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_SYMBOLS); + if (!file) { + info->errorCount += 10; + ClearSymbolsInfo(&included); + return false; + } + + InitSymbolsInfo(&next_incl, info->keymap, info->actions, + &included.mods); + if (stmt->modifier) { + next_incl.explicit_group = atoi(stmt->modifier) - 1; + if (next_incl.explicit_group >= XKB_MAX_GROUPS) { + log_err(info->ctx, + "Cannot set explicit group to %d - must be between 1..%d; " + "Ignoring group number\n", + next_incl.explicit_group + 1, XKB_MAX_GROUPS); + next_incl.explicit_group = info->explicit_group; + } + } + else { + next_incl.explicit_group = info->explicit_group; + } + + HandleSymbolsFile(&next_incl, file, MERGE_OVERRIDE); + + MergeIncludedSymbols(&included, &next_incl, stmt->merge); + + ClearSymbolsInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedSymbols(info, &included, include->merge); + ClearSymbolsInfo(&included); + + return (info->errorCount == 0); +} + +#define SYMBOLS 1 +#define ACTIONS 2 + +static bool +GetGroupIndex(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, + unsigned what, xkb_layout_index_t *ndx_rtrn) +{ + const char *name = (what == SYMBOLS ? "symbols" : "actions"); + + if (arrayNdx == NULL) { + xkb_layout_index_t i; + GroupInfo *groupi; + enum group_field field = (what == SYMBOLS ? + GROUP_FIELD_SYMS : GROUP_FIELD_ACTS); + + darray_enumerate(i, groupi, keyi->groups) { + if (!(groupi->defined & field)) { + *ndx_rtrn = i; + return true; + } + } + + if (i >= XKB_MAX_GROUPS) { + log_err(info->ctx, + "Too many groups of %s for key %s (max %u); " + "Ignoring %s defined for extra groups\n", + name, KeyInfoText(info, keyi), XKB_MAX_GROUPS, name); + return false; + } + + darray_resize0(keyi->groups, darray_size(keyi->groups) + 1); + *ndx_rtrn = darray_size(keyi->groups) - 1; + return true; + } + + if (!ExprResolveGroup(info->ctx, arrayNdx, ndx_rtrn)) { + log_err(info->ctx, + "Illegal group index for %s of key %s\n" + "Definition with non-integer array index ignored\n", + name, KeyInfoText(info, keyi)); + return false; + } + + (*ndx_rtrn)--; + if (*ndx_rtrn >= darray_size(keyi->groups)) + darray_resize0(keyi->groups, *ndx_rtrn + 1); + + return true; +} + +static bool +AddSymbolsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_layout_index_t ndx; + GroupInfo *groupi; + xkb_level_index_t nLevels; + + if (!GetGroupIndex(info, keyi, arrayNdx, SYMBOLS, &ndx)) + return false; + + groupi = &darray_item(keyi->groups, ndx); + + if (value == NULL) { + groupi->defined |= GROUP_FIELD_SYMS; + return true; + } + + if (value->expr.op != EXPR_KEYSYM_LIST) { + log_err(info->ctx, + "Expected a list of symbols, found %s; " + "Ignoring symbols for group %u of %s\n", + expr_op_type_to_string(value->expr.op), ndx + 1, + KeyInfoText(info, keyi)); + return false; + } + + if (groupi->defined & GROUP_FIELD_SYMS) { + log_err(info->ctx, + "Symbols for key %s, group %u already defined; " + "Ignoring duplicate definition\n", + KeyInfoText(info, keyi), ndx + 1); + return false; + } + + nLevels = darray_size(value->keysym_list.symsMapIndex); + if (darray_size(groupi->levels) < nLevels) + darray_resize0(groupi->levels, nLevels); + + groupi->defined |= GROUP_FIELD_SYMS; + + for (xkb_level_index_t i = 0; i < nLevels; i++) { + unsigned int sym_index; + struct xkb_level *leveli = &darray_item(groupi->levels, i); + + sym_index = darray_item(value->keysym_list.symsMapIndex, i); + leveli->num_syms = darray_item(value->keysym_list.symsNumEntries, i); + if (leveli->num_syms > 1) + leveli->u.syms = calloc(leveli->num_syms, sizeof(*leveli->u.syms)); + + for (unsigned j = 0; j < leveli->num_syms; j++) { + xkb_keysym_t keysym = darray_item(value->keysym_list.syms, + sym_index + j); + + if (leveli->num_syms == 1) { + if (keysym == XKB_KEY_NoSymbol) + leveli->num_syms = 0; + else + leveli->u.sym = keysym; + } + else if (leveli->num_syms > 1) { + leveli->u.syms[j] = keysym; + } + } + } + + return true; +} + +static bool +AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_layout_index_t ndx; + GroupInfo *groupi; + unsigned int nActs; + ExprDef *act; + + if (!GetGroupIndex(info, keyi, arrayNdx, ACTIONS, &ndx)) + return false; + + groupi = &darray_item(keyi->groups, ndx); + + if (value == NULL) { + groupi->defined |= GROUP_FIELD_ACTS; + return true; + } + + if (value->expr.op != EXPR_ACTION_LIST) { + log_wsgo(info->ctx, + "Bad expression type (%d) for action list value; " + "Ignoring actions for group %u of %s\n", + value->expr.op, ndx, KeyInfoText(info, keyi)); + return false; + } + + if (groupi->defined & GROUP_FIELD_ACTS) { + log_wsgo(info->ctx, + "Actions for key %s, group %u already defined\n", + KeyInfoText(info, keyi), ndx); + return false; + } + + nActs = 0; + for (act = value->actions.actions; act; act = (ExprDef *) act->common.next) + nActs++; + + if (darray_size(groupi->levels) < nActs) + darray_resize0(groupi->levels, nActs); + + groupi->defined |= GROUP_FIELD_ACTS; + + act = value->actions.actions; + for (unsigned i = 0; i < nActs; i++) { + union xkb_action *toAct = &darray_item(groupi->levels, i).action; + + if (!HandleActionDef(info->ctx, info->actions, &info->mods, act, toAct)) + log_err(info->ctx, + "Illegal action definition for %s; " + "Action for group %u/level %u ignored\n", + KeyInfoText(info, keyi), ndx + 1, i + 1); + + act = (ExprDef *) act->common.next; + } + + return true; +} + +static const LookupEntry repeatEntries[] = { + { "true", KEY_REPEAT_YES }, + { "yes", KEY_REPEAT_YES }, + { "on", KEY_REPEAT_YES }, + { "false", KEY_REPEAT_NO }, + { "no", KEY_REPEAT_NO }, + { "off", KEY_REPEAT_NO }, + { "default", KEY_REPEAT_UNDEFINED }, + { NULL, 0 } +}; + +static bool +SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field, + ExprDef *arrayNdx, ExprDef *value) +{ + if (istreq(field, "type")) { + xkb_layout_index_t ndx; + xkb_atom_t val; + + if (!ExprResolveString(info->ctx, value, &val)) { + log_err(info->ctx, + "The type field of a key symbol map must be a string; " + "Ignoring illegal type definition\n"); + return false; + } + + if (!arrayNdx) { + keyi->default_type = val; + keyi->defined |= KEY_FIELD_DEFAULT_TYPE; + } + else if (!ExprResolveGroup(info->ctx, arrayNdx, &ndx)) { + log_err(info->ctx, + "Illegal group index for type of key %s; " + "Definition with non-integer array index ignored\n", + KeyInfoText(info, keyi)); + return false; + } + else { + ndx--; + if (ndx >= darray_size(keyi->groups)) + darray_resize0(keyi->groups, ndx + 1); + darray_item(keyi->groups, ndx).type = val; + darray_item(keyi->groups, ndx).defined |= GROUP_FIELD_TYPE; + } + } + else if (istreq(field, "symbols")) { + return AddSymbolsToKey(info, keyi, arrayNdx, value); + } + else if (istreq(field, "actions")) { + return AddActionsToKey(info, keyi, arrayNdx, value); + } + else if (istreq(field, "vmods") || + istreq(field, "virtualmods") || + istreq(field, "virtualmodifiers")) { + xkb_mod_mask_t mask; + + if (!ExprResolveModMask(info->ctx, value, MOD_VIRT, &info->mods, + &mask)) { + log_err(info->ctx, + "Expected a virtual modifier mask, found %s; " + "Ignoring virtual modifiers definition for key %s\n", + expr_op_type_to_string(value->expr.op), + KeyInfoText(info, keyi)); + return false; + } + + keyi->vmodmap = mask; + keyi->defined |= KEY_FIELD_VMODMAP; + } + else if (istreq(field, "locking") || + istreq(field, "lock") || + istreq(field, "locks")) { + log_vrb(info->ctx, 1, + "Key behaviors not supported; " + "Ignoring locking specification for key %s\n", + KeyInfoText(info, keyi)); + } + else if (istreq(field, "radiogroup") || + istreq(field, "permanentradiogroup") || + istreq(field, "allownone")) { + log_vrb(info->ctx, 1, + "Radio groups not supported; " + "Ignoring radio group specification for key %s\n", + KeyInfoText(info, keyi)); + } + else if (istreq_prefix("overlay", field) || + istreq_prefix("permanentoverlay", field)) { + log_vrb(info->ctx, 1, + "Overlays not supported; " + "Ignoring overlay specification for key %s\n", + KeyInfoText(info, keyi)); + } + else if (istreq(field, "repeating") || + istreq(field, "repeats") || + istreq(field, "repeat")) { + unsigned int val; + + if (!ExprResolveEnum(info->ctx, value, &val, repeatEntries)) { + log_err(info->ctx, + "Illegal repeat setting for %s; " + "Non-boolean repeat setting ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->repeat = val; + keyi->defined |= KEY_FIELD_REPEAT; + } + else if (istreq(field, "groupswrap") || + istreq(field, "wrapgroups")) { + bool set; + + if (!ExprResolveBoolean(info->ctx, value, &set)) { + log_err(info->ctx, + "Illegal groupsWrap setting for %s; " + "Non-boolean value ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->out_of_range_group_action = (set ? RANGE_WRAP : RANGE_SATURATE); + keyi->defined |= KEY_FIELD_GROUPINFO; + } + else if (istreq(field, "groupsclamp") || + istreq(field, "clampgroups")) { + bool set; + + if (!ExprResolveBoolean(info->ctx, value, &set)) { + log_err(info->ctx, + "Illegal groupsClamp setting for %s; " + "Non-boolean value ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->out_of_range_group_action = (set ? RANGE_SATURATE : RANGE_WRAP); + keyi->defined |= KEY_FIELD_GROUPINFO; + } + else if (istreq(field, "groupsredirect") || + istreq(field, "redirectgroups")) { + xkb_layout_index_t grp; + + if (!ExprResolveGroup(info->ctx, value, &grp)) { + log_err(info->ctx, + "Illegal group index for redirect of key %s; " + "Definition with non-integer group ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + keyi->out_of_range_group_action = RANGE_REDIRECT; + keyi->out_of_range_group_number = grp - 1; + keyi->defined |= KEY_FIELD_GROUPINFO; + } + else { + log_err(info->ctx, + "Unknown field %s in a symbol interpretation; " + "Definition ignored\n", + field); + return false; + } + + return true; +} + +static bool +SetGroupName(SymbolsInfo *info, ExprDef *arrayNdx, ExprDef *value) +{ + xkb_layout_index_t group, group_to_use; + xkb_atom_t name; + + if (!arrayNdx) { + log_vrb(info->ctx, 1, + "You must specify an index when specifying a group name; " + "Group name definition without array subscript ignored\n"); + return false; + } + + if (!ExprResolveGroup(info->ctx, arrayNdx, &group)) { + log_err(info->ctx, + "Illegal index in group name definition; " + "Definition with non-integer array index ignored\n"); + return false; + } + + if (!ExprResolveString(info->ctx, value, &name)) { + log_err(info->ctx, + "Group name must be a string; " + "Illegal name for group %d ignored\n", group); + return false; + } + + if (info->explicit_group == XKB_LAYOUT_INVALID) { + group_to_use = group - 1; + } + else if (group - 1 == 0) { + group_to_use = info->explicit_group; + } + else { + log_warn(info->ctx, + "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", + info->name, group, + xkb_atom_text(info->ctx, name)); + return false; + } + + if (group_to_use >= darray_size(info->group_names)) + darray_resize0(info->group_names, group_to_use + 1); + darray_item(info->group_names, group_to_use) = name; + + return true; +} + +static bool +HandleGlobalVar(SymbolsInfo *info, VarDef *stmt) +{ + const char *elem, *field; + ExprDef *arrayNdx; + bool ret; + + if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &arrayNdx)) + return false; + + if (elem && istreq(elem, "key")) { + ret = SetSymbolsField(info, &info->default_key, field, arrayNdx, + stmt->value); + } + else if (!elem && (istreq(field, "name") || + istreq(field, "groupname"))) { + ret = SetGroupName(info, arrayNdx, stmt->value); + } + else if (!elem && (istreq(field, "groupswrap") || + istreq(field, "wrapgroups"))) { + log_err(info->ctx, + "Global \"groupswrap\" not supported; Ignored\n"); + ret = true; + } + else if (!elem && (istreq(field, "groupsclamp") || + istreq(field, "clampgroups"))) { + log_err(info->ctx, + "Global \"groupsclamp\" not supported; Ignored\n"); + ret = true; + } + else if (!elem && (istreq(field, "groupsredirect") || + istreq(field, "redirectgroups"))) { + log_err(info->ctx, + "Global \"groupsredirect\" not supported; Ignored\n"); + ret = true; + } + else if (!elem && istreq(field, "allownone")) { + log_err(info->ctx, + "Radio groups not supported; " + "Ignoring \"allownone\" specification\n"); + ret = true; + } + else { + ret = SetActionField(info->ctx, info->actions, &info->mods, + elem, field, arrayNdx, stmt->value); + } + + return ret; +} + +static bool +HandleSymbolsBody(SymbolsInfo *info, VarDef *def, KeyInfo *keyi) +{ + bool ok = true; + const char *elem, *field; + ExprDef *arrayNdx; + + for (; def; def = (VarDef *) def->common.next) { + if (def->name && def->name->expr.op == EXPR_FIELD_REF) { + log_err(info->ctx, + "Cannot set a global default value from within a key statement; " + "Move statements to the global file scope\n"); + continue; + } + + if (!def->name) { + if (!def->value || def->value->expr.op == EXPR_KEYSYM_LIST) + field = "symbols"; + else + field = "actions"; + arrayNdx = NULL; + } + else { + ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, + &arrayNdx); + } + + if (ok) + ok = SetSymbolsField(info, keyi, field, arrayNdx, def->value); + } + + return ok; +} + +static bool +SetExplicitGroup(SymbolsInfo *info, KeyInfo *keyi) +{ + xkb_layout_index_t i; + GroupInfo *groupi; + bool warn = false; + + if (info->explicit_group == XKB_LAYOUT_INVALID) + return true; + + darray_enumerate_from(i, groupi, keyi->groups, 1) { + if (groupi->defined) { + warn = true; + ClearGroupInfo(groupi); + InitGroupInfo(groupi); + } + } + + if (warn) + log_warn(info->ctx, + "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) { + darray_item(keyi->groups, info->explicit_group) = + darray_item(keyi->groups, 0); + InitGroupInfo(&darray_item(keyi->groups, 0)); + } + + return true; +} + +static bool +HandleSymbolsDef(SymbolsInfo *info, SymbolsDef *stmt) +{ + KeyInfo keyi; + + keyi = info->default_key; + darray_init(keyi.groups); + darray_copy(keyi.groups, info->default_key.groups); + for (xkb_layout_index_t i = 0; i < darray_size(keyi.groups); i++) + CopyGroupInfo(&darray_item(keyi.groups, i), + &darray_item(info->default_key.groups, i)); + keyi.merge = stmt->merge; + keyi.name = stmt->keyName; + + if (!HandleSymbolsBody(info, stmt->symbols, &keyi)) { + info->errorCount++; + return false; + } + + if (!SetExplicitGroup(info, &keyi)) { + info->errorCount++; + return false; + } + + if (!AddKeySymbols(info, &keyi, true)) { + info->errorCount++; + return false; + } + + return true; +} + +static bool +HandleModMapDef(SymbolsInfo *info, ModMapDef *def) +{ + ModMapEntry tmp; + xkb_mod_index_t ndx; + bool ok; + struct xkb_context *ctx = info->ctx; + + ndx = XkbModNameToIndex(&info->mods, def->modifier, MOD_REAL); + if (ndx == XKB_MOD_INVALID) { + log_err(info->ctx, + "Illegal modifier map definition; " + "Ignoring map for non-modifier \"%s\"\n", + xkb_atom_text(ctx, def->modifier)); + return false; + } + + ok = true; + tmp.modifier = ndx; + tmp.merge = def->merge; + + for (ExprDef *key = def->keys; key; key = (ExprDef *) key->common.next) { + xkb_keysym_t sym; + + if (key->expr.op == EXPR_VALUE && + key->expr.value_type == EXPR_TYPE_KEYNAME) { + tmp.haveSymbol = false; + tmp.u.keyName = key->key_name.key_name; + } + else if (ExprResolveKeySym(ctx, key, &sym)) { + tmp.haveSymbol = true; + tmp.u.keySym = sym; + } + else { + log_err(info->ctx, + "Modmap entries may contain only key names or keysyms; " + "Illegal definition for %s modifier ignored\n", + ModIndexText(info->ctx, &info->mods, tmp.modifier)); + continue; + } + + ok = AddModMapEntry(info, &tmp) && ok; + } + return ok; +} + +static void +HandleSymbolsFile(SymbolsInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeSymbols(info, (IncludeStmt *) stmt); + break; + case STMT_SYMBOLS: + ok = HandleSymbolsDef(info, (SymbolsDef *) stmt); + break; + case STMT_VAR: + ok = HandleGlobalVar(info, (VarDef *) stmt); + break; + case STMT_VMOD: + ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge); + break; + case STMT_MODMAP: + ok = HandleModMapDef(info, (ModMapDef *) stmt); + break; + default: + log_err(info->ctx, + "Symbols files may not include other types; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, "Abandoning symbols file \"%s\"\n", + file->name); + break; + } + } +} + +/** + * Given a keysym @sym, return a key which generates it, or NULL. + * This is used for example in a modifier map definition, such as: + * modifier_map Lock { Caps_Lock }; + * where we want to add the Lock modifier to the modmap of the key + * which matches the keysym Caps_Lock. + * Since there can be many keys which generates the keysym, the key + * is chosen first by lowest group in which the keysym appears, than + * by lowest level and than by lowest key code. + */ +static struct xkb_key * +FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym) +{ + struct xkb_key *key; + xkb_layout_index_t group; + bool got_one_group, got_one_level; + + group = 0; + do { + xkb_level_index_t level = 0; + got_one_group = false; + do { + got_one_level = false; + xkb_keys_foreach(key, keymap) { + if (group < key->num_groups && + level < XkbKeyNumLevels(key, group)) { + got_one_group = got_one_level = true; + if (key->groups[group].levels[level].num_syms == 1 && + key->groups[group].levels[level].u.sym == sym) + return key; + } + } + level++; + } while (got_one_level); + group++; + } while (got_one_group); + + return NULL; +} + +/* + * Find an appropriate type for a group and return its name. + * + * Simple recipe: + * - ONE_LEVEL for width 0/1 + * - ALPHABETIC for 2 shift levels, with lower/upercase keysyms + * - KEYPAD for keypad keys. + * - TWO_LEVEL for other 2 shift level keys. + * and the same for four level keys. + * + * FIXME: Decide how to handle multiple-syms-per-level, and do it. + */ +static xkb_atom_t +FindAutomaticType(struct xkb_context *ctx, GroupInfo *groupi) +{ + xkb_keysym_t sym0, sym1; + const xkb_level_index_t width = darray_size(groupi->levels); + +#define GET_SYM(level) \ + (darray_item(groupi->levels, level).num_syms == 0 ? \ + XKB_KEY_NoSymbol : \ + darray_item(groupi->levels, level).num_syms == 1 ? \ + darray_item(groupi->levels, level).u.sym : \ + /* num_syms > 1 */ \ + darray_item(groupi->levels, level).u.syms[0]) + + if (width == 1 || width <= 0) + return xkb_atom_intern_literal(ctx, "ONE_LEVEL"); + + sym0 = GET_SYM(0); + sym1 = GET_SYM(1); + + if (width == 2) { + if (xkb_keysym_is_lower(sym0) && xkb_keysym_is_upper(sym1)) + return xkb_atom_intern_literal(ctx, "ALPHABETIC"); + + if (xkb_keysym_is_keypad(sym0) || xkb_keysym_is_keypad(sym1)) + return xkb_atom_intern_literal(ctx, "KEYPAD"); + + return xkb_atom_intern_literal(ctx, "TWO_LEVEL"); + } + + if (width <= 4) { + if (xkb_keysym_is_lower(sym0) && xkb_keysym_is_upper(sym1)) { + xkb_keysym_t sym2, sym3; + sym2 = GET_SYM(2); + sym3 = (width == 4 ? GET_SYM(3) : XKB_KEY_NoSymbol); + + if (xkb_keysym_is_lower(sym2) && xkb_keysym_is_upper(sym3)) + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL_ALPHABETIC"); + + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL_SEMIALPHABETIC"); + } + + if (xkb_keysym_is_keypad(sym0) || xkb_keysym_is_keypad(sym1)) + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL_KEYPAD"); + + return xkb_atom_intern_literal(ctx, "FOUR_LEVEL"); + } + + return XKB_ATOM_NONE; + +#undef GET_SYM +} + +static const struct xkb_key_type * +FindTypeForGroup(struct xkb_keymap *keymap, KeyInfo *keyi, + xkb_layout_index_t group, bool *explicit_type) +{ + unsigned int i; + GroupInfo *groupi = &darray_item(keyi->groups, group); + xkb_atom_t type_name = groupi->type; + + *explicit_type = true; + + if (type_name == XKB_ATOM_NONE) { + if (keyi->default_type != XKB_ATOM_NONE) { + type_name = keyi->default_type; + } + else { + type_name = FindAutomaticType(keymap->ctx, groupi); + if (type_name != XKB_ATOM_NONE) + *explicit_type = false; + } + } + + if (type_name == XKB_ATOM_NONE) { + log_warn(keymap->ctx, + "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, + (unsigned long) darray_size(groupi->levels)); + goto use_default; + } + + for (i = 0; i < keymap->num_types; i++) + if (keymap->types[i].name == type_name) + break; + + if (i >= keymap->num_types) { + log_warn(keymap->ctx, + "The type \"%s\" for key '%s' group %d was not previously defined; " + "Using the default type\n", + xkb_atom_text(keymap->ctx, type_name), + KeyNameText(keymap->ctx, keyi->name), group + 1); + goto use_default; + } + + return &keymap->types[i]; + +use_default: + /* + * Index 0 is guaranteed to contain something, usually + * ONE_LEVEL or at least some default one-level type. + */ + return &keymap->types[0]; +} + +static bool +CopySymbolsDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info, + KeyInfo *keyi) +{ + struct xkb_key *key; + GroupInfo *groupi; + const GroupInfo *group0; + xkb_layout_index_t i; + + /* + * The name is guaranteed to be real and not an alias (see + * AddKeySymbols), so 'false' is safe here. + */ + key = XkbKeyByName(keymap, keyi->name, false); + if (!key) { + log_vrb(info->ctx, 5, + "Key %s not found in keycodes; Symbols ignored\n", + KeyInfoText(info, keyi)); + return false; + } + + /* Find the range of groups we need. */ + key->num_groups = 0; + darray_enumerate(i, groupi, keyi->groups) + if (groupi->defined) + key->num_groups = i + 1; + + if (key->num_groups <= 0) + return false; /* WSGO */ + + darray_resize(keyi->groups, key->num_groups); + + /* + * If there are empty groups between non-empty ones, fill them with data + * from the first group. + * We can make a wrong assumption here. But leaving gaps is worse. + */ + group0 = &darray_item(keyi->groups, 0); + darray_foreach_from(groupi, keyi->groups, 1) { + if (groupi->defined) + continue; + + CopyGroupInfo(groupi, group0); + } + + key->groups = calloc(key->num_groups, sizeof(*key->groups)); + + /* Find and assign the groups' types in the keymap. */ + darray_enumerate(i, groupi, keyi->groups) { + const struct xkb_key_type *type; + bool explicit_type; + + type = FindTypeForGroup(keymap, keyi, i, &explicit_type); + + /* Always have as many levels as the type specifies. */ + if (type->num_levels < darray_size(groupi->levels)) { + struct xkb_level *leveli; + + log_vrb(info->ctx, 1, + "Type \"%s\" has %d levels, but %s has %d levels; " + "Ignoring extra symbols\n", + xkb_atom_text(keymap->ctx, type->name), type->num_levels, + KeyInfoText(info, keyi), + (int) darray_size(groupi->levels)); + + darray_foreach_from(leveli, groupi->levels, type->num_levels) + ClearLevelInfo(leveli); + } + darray_resize0(groupi->levels, type->num_levels); + + key->groups[i].explicit_type = explicit_type; + key->groups[i].type = type; + } + + /* Copy levels. */ + darray_enumerate(i, groupi, keyi->groups) + darray_steal(groupi->levels, &key->groups[i].levels, NULL); + + key->out_of_range_group_number = keyi->out_of_range_group_number; + key->out_of_range_group_action = keyi->out_of_range_group_action; + + if (keyi->defined & KEY_FIELD_VMODMAP) { + key->vmodmap = keyi->vmodmap; + key->explicit |= EXPLICIT_VMODMAP; + } + + if (keyi->repeat != KEY_REPEAT_UNDEFINED) { + key->repeats = (keyi->repeat == KEY_REPEAT_YES); + key->explicit |= EXPLICIT_REPEAT; + } + + darray_foreach(groupi, keyi->groups) { + if (groupi->defined & GROUP_FIELD_ACTS) { + key->explicit |= EXPLICIT_INTERP; + break; + } + } + + return true; +} + +static bool +CopyModMapDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info, + ModMapEntry *entry) +{ + struct xkb_key *key; + + if (!entry->haveSymbol) { + key = XkbKeyByName(keymap, entry->u.keyName, true); + if (!key) { + log_vrb(info->ctx, 5, + "Key %s not found in keycodes; " + "Modifier map entry for %s not updated\n", + KeyNameText(info->ctx, entry->u.keyName), + ModIndexText(info->ctx, &info->mods, entry->modifier)); + return false; + } + } + else { + key = FindKeyForSymbol(keymap, entry->u.keySym); + if (!key) { + log_vrb(info->ctx, 5, + "Key \"%s\" not found in symbol map; " + "Modifier map entry for %s not updated\n", + KeysymText(info->ctx, entry->u.keySym), + ModIndexText(info->ctx, &info->mods, entry->modifier)); + return false; + } + } + + key->modmap |= (1u << entry->modifier); + return true; +} + +static bool +CopySymbolsToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info) +{ + KeyInfo *keyi; + ModMapEntry *mm; + + keymap->symbols_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->symbols_section_name); + + keymap->mods = info->mods; + + darray_steal(info->group_names, + &keymap->group_names, &keymap->num_group_names); + + darray_foreach(keyi, info->keys) + if (!CopySymbolsDefToKeymap(keymap, info, keyi)) + info->errorCount++; + + if (xkb_context_get_log_verbosity(keymap->ctx) > 3) { + struct xkb_key *key; + + xkb_keys_foreach(key, keymap) { + if (key->name == XKB_ATOM_NONE) + continue; + + if (key->num_groups < 1) + log_info(info->ctx, + "No symbols defined for %s\n", + KeyNameText(info->ctx, key->name)); + } + } + + darray_foreach(mm, info->modmaps) + if (!CopyModMapDefToKeymap(keymap, info, mm)) + info->errorCount++; + + /* XXX: If we don't ignore errorCount, things break. */ + return true; +} + +bool +CompileSymbols(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + SymbolsInfo info; + ActionsInfo *actions; + + actions = NewActionsInfo(); + if (!actions) + return false; + + InitSymbolsInfo(&info, keymap, actions, &keymap->mods); + info.default_key.merge = merge; + + HandleSymbolsFile(&info, file, merge); + + if (info.errorCount != 0) + goto err_info; + + if (!CopySymbolsToKeymap(keymap, &info)) + goto err_info; + + ClearSymbolsInfo(&info); + FreeActionsInfo(actions); + return true; + +err_info: + FreeActionsInfo(actions); + ClearSymbolsInfo(&info); + return false; +} diff --git a/src/xkbcomp/types.c b/src/xkbcomp/types.c new file mode 100644 index 0000000..3feaf41 --- /dev/null +++ b/src/xkbcomp/types.c @@ -0,0 +1,744 @@ +/************************************************************ + * Copyright (c) 1994 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 "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "vmod.h" +#include "expr.h" +#include "include.h" + +enum type_field { + TYPE_FIELD_MASK = (1 << 0), + TYPE_FIELD_MAP = (1 << 1), + TYPE_FIELD_PRESERVE = (1 << 2), + TYPE_FIELD_LEVEL_NAME = (1 << 3), +}; + +typedef struct { + enum type_field defined; + enum merge_mode merge; + + xkb_atom_t name; + xkb_mod_mask_t mods; + xkb_level_index_t num_levels; + darray(struct xkb_key_type_entry) entries; + darray(xkb_atom_t) level_names; +} KeyTypeInfo; + +typedef struct { + char *name; + int errorCount; + + darray(KeyTypeInfo) types; + struct xkb_mod_set mods; + + struct xkb_context *ctx; +} KeyTypesInfo; + +/***====================================================================***/ + +static inline const char * +MapEntryTxt(KeyTypesInfo *info, struct xkb_key_type_entry *entry) +{ + return ModMaskText(info->ctx, &info->mods, entry->mods.mods); +} + +static inline const char * +TypeTxt(KeyTypesInfo *info, KeyTypeInfo *type) +{ + return xkb_atom_text(info->ctx, type->name); +} + +static inline const char * +TypeMaskTxt(KeyTypesInfo *info, KeyTypeInfo *type) +{ + return ModMaskText(info->ctx, &info->mods, type->mods); +} + +static inline bool +ReportTypeShouldBeArray(KeyTypesInfo *info, KeyTypeInfo *type, + const char *field) +{ + return ReportShouldBeArray(info->ctx, "key type", field, + TypeTxt(info, type)); +} + +static inline bool +ReportTypeBadType(KeyTypesInfo *info, KeyTypeInfo *type, + const char *field, const char *wanted) +{ + return ReportBadType(info->ctx, "key type", field, + TypeTxt(info, type), wanted); +} + +/***====================================================================***/ + +static void +InitKeyTypesInfo(KeyTypesInfo *info, struct xkb_context *ctx, + const struct xkb_mod_set *mods) +{ + memset(info, 0, sizeof(*info)); + info->ctx = ctx; + info->mods = *mods; +} + +static void +ClearKeyTypeInfo(KeyTypeInfo *type) +{ + darray_free(type->entries); + darray_free(type->level_names); +} + +static void +ClearKeyTypesInfo(KeyTypesInfo *info) +{ + free(info->name); + darray_free(info->types); +} + +static KeyTypeInfo * +FindMatchingKeyType(KeyTypesInfo *info, xkb_atom_t name) +{ + KeyTypeInfo *old; + + darray_foreach(old, info->types) + if (old->name == name) + return old; + + return NULL; +} + +static bool +AddKeyType(KeyTypesInfo *info, KeyTypeInfo *new, bool same_file) +{ + KeyTypeInfo *old; + const int verbosity = xkb_context_get_log_verbosity(info->ctx); + + old = FindMatchingKeyType(info, new->name); + if (old) { + if (new->merge == MERGE_REPLACE || new->merge == MERGE_OVERRIDE) { + if ((same_file && verbosity > 0) || verbosity > 9) { + log_warn(info->ctx, + "Multiple definitions of the %s key type; " + "Earlier definition ignored\n", + xkb_atom_text(info->ctx, new->name)); + } + + ClearKeyTypeInfo(old); + *old = *new; + darray_init(new->entries); + darray_init(new->level_names); + return true; + } + + if (same_file) + log_vrb(info->ctx, 4, + "Multiple definitions of the %s key type; " + "Later definition ignored\n", + xkb_atom_text(info->ctx, new->name)); + + ClearKeyTypeInfo(new); + return true; + } + + darray_append(info->types, *new); + return true; +} + +/***====================================================================***/ + +static void +MergeIncludedKeyTypes(KeyTypesInfo *into, KeyTypesInfo *from, + enum merge_mode merge) +{ + if (from->errorCount > 0) { + into->errorCount += from->errorCount; + return; + } + + into->mods = from->mods; + + if (into->name == NULL) { + into->name = from->name; + from->name = NULL; + } + + if (darray_empty(into->types)) { + into->types = from->types; + darray_init(from->types); + } + else { + KeyTypeInfo *type; + darray_foreach(type, from->types) { + type->merge = (merge == MERGE_DEFAULT ? type->merge : merge); + if (!AddKeyType(into, type, false)) + into->errorCount++; + } + } +} + +static void +HandleKeyTypesFile(KeyTypesInfo *info, XkbFile *file, enum merge_mode merge); + +static bool +HandleIncludeKeyTypes(KeyTypesInfo *info, IncludeStmt *include) +{ + KeyTypesInfo included; + + InitKeyTypesInfo(&included, info->ctx, &info->mods); + included.name = include->stmt; + include->stmt = NULL; + + for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { + KeyTypesInfo next_incl; + XkbFile *file; + + file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_TYPES); + if (!file) { + info->errorCount += 10; + ClearKeyTypesInfo(&included); + return false; + } + + InitKeyTypesInfo(&next_incl, info->ctx, &included.mods); + + HandleKeyTypesFile(&next_incl, file, stmt->merge); + + MergeIncludedKeyTypes(&included, &next_incl, stmt->merge); + + ClearKeyTypesInfo(&next_incl); + FreeXkbFile(file); + } + + MergeIncludedKeyTypes(info, &included, include->merge); + ClearKeyTypesInfo(&included); + + return (info->errorCount == 0); +} + +/***====================================================================***/ + +static bool +SetModifiers(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_mod_mask_t mods; + + if (arrayNdx) + log_warn(info->ctx, + "The modifiers field of a key type is not an array; " + "Illegal array subscript ignored\n"); + + if (!ExprResolveModMask(info->ctx, value, MOD_BOTH, &info->mods, &mods)) { + log_err(info->ctx, + "Key type mask field must be a modifier mask; " + "Key type definition ignored\n"); + return false; + } + + if (type->defined & TYPE_FIELD_MASK) { + log_warn(info->ctx, + "Multiple modifier mask definitions for key type %s; " + "Using %s, ignoring %s\n", + xkb_atom_text(info->ctx, type->name), + TypeMaskTxt(info, type), + ModMaskText(info->ctx, &info->mods, mods)); + return false; + } + + type->mods = mods; + return true; +} + +/***====================================================================***/ + +static struct xkb_key_type_entry * +FindMatchingMapEntry(KeyTypeInfo *type, xkb_mod_mask_t mods) +{ + struct xkb_key_type_entry *entry; + + darray_foreach(entry, type->entries) + if (entry->mods.mods == mods) + return entry; + + return NULL; +} + +static bool +AddMapEntry(KeyTypesInfo *info, KeyTypeInfo *type, + struct xkb_key_type_entry *new, bool clobber, bool report) +{ + struct xkb_key_type_entry *old; + + old = FindMatchingMapEntry(type, new->mods.mods); + if (old) { + if (report && old->level != new->level) { + log_warn(info->ctx, + "Multiple map entries for %s in %s; " + "Using %d, ignoring %d\n", + MapEntryTxt(info, new), TypeTxt(info, type), + (clobber ? new->level : old->level) + 1, + (clobber ? old->level : new->level) + 1); + } + else { + log_vrb(info->ctx, 10, + "Multiple occurrences of map[%s]= %d in %s; Ignored\n", + MapEntryTxt(info, new), new->level + 1, + TypeTxt(info, type)); + return true; + } + + if (clobber) { + if (new->level >= type->num_levels) + type->num_levels = new->level + 1; + old->level = new->level; + } + + return true; + } + + if (new->level >= type->num_levels) + type->num_levels = new->level + 1; + + darray_append(type->entries, *new); + return true; +} + +static bool +SetMapEntry(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + struct xkb_key_type_entry entry; + + if (arrayNdx == NULL) + return ReportTypeShouldBeArray(info, type, "map entry"); + + if (!ExprResolveModMask(info->ctx, arrayNdx, MOD_BOTH, &info->mods, + &entry.mods.mods)) + return ReportTypeBadType(info, type, "map entry", "modifier mask"); + + if (entry.mods.mods & (~type->mods)) { + log_vrb(info->ctx, 1, + "Map entry for unused modifiers in %s; " + "Using %s instead of %s\n", + TypeTxt(info, type), + ModMaskText(info->ctx, &info->mods, + entry.mods.mods & type->mods), + MapEntryTxt(info, &entry)); + entry.mods.mods &= type->mods; + } + + 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"); + return false; + } + + entry.preserve.mods = 0; + + return AddMapEntry(info, type, &entry, true, true); +} + +/***====================================================================***/ + +static bool +AddPreserve(KeyTypesInfo *info, KeyTypeInfo *type, + xkb_mod_mask_t mods, xkb_mod_mask_t preserve_mods) +{ + struct xkb_key_type_entry *entry; + struct xkb_key_type_entry new; + + darray_foreach(entry, type->entries) { + if (entry->mods.mods != mods) + continue; + + /* Map exists without previous preserve (or "None"); override. */ + if (entry->preserve.mods == 0) { + entry->preserve.mods = preserve_mods; + return true; + } + + /* Map exists with same preserve; do nothing. */ + if (entry->preserve.mods == preserve_mods) { + log_vrb(info->ctx, 10, + "Identical definitions for preserve[%s] in %s; " + "Ignored\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type)); + return true; + } + + /* Map exists with different preserve; latter wins. */ + log_vrb(info->ctx, 1, + "Multiple definitions for preserve[%s] in %s; " + "Using %s, ignoring %s\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type), + ModMaskText(info->ctx, &info->mods, preserve_mods), + ModMaskText(info->ctx, &info->mods, entry->preserve.mods)); + + entry->preserve.mods = preserve_mods; + return true; + } + + /* + * Map does not exist, i.e. preserve[] came before map[]. + * Create a map with the specified mask mapping to Level1. The level + * may be overridden later with an explicit map[] statement. + */ + new.level = 0; + new.mods.mods = mods; + new.preserve.mods = preserve_mods; + darray_append(type->entries, new); + return true; +} + +static bool +SetPreserve(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_mod_mask_t mods, preserve_mods; + + if (arrayNdx == NULL) + return ReportTypeShouldBeArray(info, type, "preserve entry"); + + if (!ExprResolveModMask(info->ctx, arrayNdx, MOD_BOTH, &info->mods, &mods)) + return ReportTypeBadType(info, type, "preserve entry", + "modifier mask"); + + if (mods & ~type->mods) { + const char *before, *after; + + before = ModMaskText(info->ctx, &info->mods, mods); + mods &= type->mods; + after = ModMaskText(info->ctx, &info->mods, mods); + + log_vrb(info->ctx, 1, + "Preserve for modifiers not used by the %s type; " + "Index %s converted to %s\n", + TypeTxt(info, type), before, after); + } + + if (!ExprResolveModMask(info->ctx, value, MOD_BOTH, &info->mods, + &preserve_mods)) { + log_err(info->ctx, + "Preserve value in a key type is not a modifier mask; " + "Ignoring preserve[%s] in type %s\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type)); + return false; + } + + if (preserve_mods & ~mods) { + const char *before, *after; + + before = ModMaskText(info->ctx, &info->mods, preserve_mods); + preserve_mods &= mods; + after = ModMaskText(info->ctx, &info->mods, preserve_mods); + + log_vrb(info->ctx, 1, + "Illegal value for preserve[%s] in type %s; " + "Converted %s to %s\n", + ModMaskText(info->ctx, &info->mods, mods), + TypeTxt(info, type), before, after); + } + + return AddPreserve(info, type, mods, preserve_mods); +} + +/***====================================================================***/ + +static bool +AddLevelName(KeyTypesInfo *info, KeyTypeInfo *type, + xkb_level_index_t level, xkb_atom_t name, bool clobber) +{ + /* New name. */ + if (level >= darray_size(type->level_names)) { + darray_resize0(type->level_names, level + 1); + goto finish; + } + + /* Same level, same name. */ + if (darray_item(type->level_names, level) == name) { + log_vrb(info->ctx, 10, + "Duplicate names for level %d of key type %s; Ignored\n", + level + 1, TypeTxt(info, type)); + return true; + } + + /* Same level, different name. */ + if (darray_item(type->level_names, level) != XKB_ATOM_NONE) { + const char *old, *new; + old = xkb_atom_text(info->ctx, + darray_item(type->level_names, level)); + new = xkb_atom_text(info->ctx, name); + log_vrb(info->ctx, 1, + "Multiple names for level %d of key type %s; " + "Using %s, ignoring %s\n", + level + 1, TypeTxt(info, type), + (clobber ? new : old), (clobber ? old : new)); + + if (!clobber) + return true; + } + + /* XXX: What about different level, same name? */ + +finish: + darray_item(type->level_names, level) = name; + return true; +} + +static bool +SetLevelName(KeyTypesInfo *info, KeyTypeInfo *type, ExprDef *arrayNdx, + ExprDef *value) +{ + xkb_level_index_t level; + xkb_atom_t level_name; + + if (arrayNdx == NULL) + return ReportTypeShouldBeArray(info, type, "level name"); + + if (!ExprResolveLevel(info->ctx, arrayNdx, &level)) + return ReportTypeBadType(info, type, "level name", "integer"); + + if (!ExprResolveString(info->ctx, value, &level_name)) { + log_err(info->ctx, + "Non-string name for level %d in key type %s; " + "Ignoring illegal level name definition\n", + level + 1, xkb_atom_text(info->ctx, type->name)); + return false; + } + + return AddLevelName(info, type, level, level_name, true); +} + +/***====================================================================***/ + +static bool +SetKeyTypeField(KeyTypesInfo *info, KeyTypeInfo *type, + const char *field, ExprDef *arrayNdx, ExprDef *value) +{ + bool ok = false; + enum type_field type_field = 0; + + if (istreq(field, "modifiers")) { + type_field = TYPE_FIELD_MASK; + ok = SetModifiers(info, type, arrayNdx, value); + } + else if (istreq(field, "map")) { + type_field = TYPE_FIELD_MAP; + ok = SetMapEntry(info, type, arrayNdx, value); + } + else if (istreq(field, "preserve")) { + type_field = TYPE_FIELD_PRESERVE; + ok = SetPreserve(info, type, arrayNdx, value); + } + else if (istreq(field, "levelname") || istreq(field, "level_name")) { + type_field = TYPE_FIELD_LEVEL_NAME; + ok = SetLevelName(info, type, arrayNdx, value); + } else { + log_err(info->ctx, + "Unknown field %s in key type %s; Definition ignored\n", + field, TypeTxt(info, type)); + } + + type->defined |= type_field; + return ok; +} + +static bool +HandleKeyTypeBody(KeyTypesInfo *info, VarDef *def, KeyTypeInfo *type) +{ + bool ok = true; + const char *elem, *field; + ExprDef *arrayNdx; + + for (; def; def = (VarDef *) def->common.next) { + ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, + &arrayNdx); + if (!ok) + continue; + + if (elem && istreq(elem, "type")) { + log_err(info->ctx, + "Support for changing the default type has been removed; " + "Statement ignored\n"); + continue; + } + + ok = SetKeyTypeField(info, type, field, arrayNdx, def->value); + } + + return ok; +} + +static bool +HandleKeyTypeDef(KeyTypesInfo *info, KeyTypeDef *def, enum merge_mode merge) +{ + KeyTypeInfo type = { + .defined = 0, + .merge = (def->merge == MERGE_DEFAULT ? merge : def->merge), + .name = def->name, + .mods = 0, + .num_levels = 1, + .entries = darray_new(), + .level_names = darray_new(), + }; + + if (!HandleKeyTypeBody(info, def->body, &type)) { + info->errorCount++; + return false; + } + + if (!AddKeyType(info, &type, true)) { + info->errorCount++; + return false; + } + + return true; +} + +static void +HandleKeyTypesFile(KeyTypesInfo *info, XkbFile *file, enum merge_mode merge) +{ + bool ok; + + free(info->name); + info->name = strdup_safe(file->name); + + for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) { + switch (stmt->type) { + case STMT_INCLUDE: + ok = HandleIncludeKeyTypes(info, (IncludeStmt *) stmt); + break; + case STMT_TYPE: + ok = HandleKeyTypeDef(info, (KeyTypeDef *) stmt, merge); + break; + case STMT_VAR: + log_err(info->ctx, + "Support for changing the default type has been removed; " + "Statement ignored\n"); + ok = true; + break; + case STMT_VMOD: + ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge); + break; + default: + log_err(info->ctx, + "Key type files may not include other declarations; " + "Ignoring %s\n", stmt_type_to_string(stmt->type)); + ok = false; + break; + } + + if (!ok) + info->errorCount++; + + if (info->errorCount > 10) { + log_err(info->ctx, + "Abandoning keytypes file \"%s\"\n", file->name); + break; + } + } +} + +/***====================================================================***/ + +static bool +CopyKeyTypesToKeymap(struct xkb_keymap *keymap, KeyTypesInfo *info) +{ + unsigned num_types; + struct xkb_key_type *types; + + num_types = darray_empty(info->types) ? 1 : darray_size(info->types); + types = calloc(num_types, sizeof(*types)); + if (!types) + return false; + + /* + * If no types were specified, a default unnamed one-level type is + * used for all keys. + */ + if (darray_empty(info->types)) { + struct xkb_key_type *type = &types[0]; + + type->mods.mods = 0; + type->num_levels = 1; + type->entries = NULL; + type->num_entries = 0; + type->name = xkb_atom_intern_literal(keymap->ctx, "default"); + type->level_names = NULL; + type->num_level_names = 0; + } + else { + for (unsigned i = 0; i < num_types; i++) { + KeyTypeInfo *def = &darray_item(info->types, i); + struct xkb_key_type *type = &types[i]; + + type->name = def->name; + type->mods.mods = def->mods; + type->num_levels = def->num_levels; + darray_steal(def->level_names, &type->level_names, &type->num_level_names); + darray_steal(def->entries, &type->entries, &type->num_entries); + } + } + + keymap->types_section_name = strdup_safe(info->name); + XkbEscapeMapName(keymap->types_section_name); + keymap->num_types = num_types; + keymap->types = types; + keymap->mods = info->mods; + return true; +} + +/***====================================================================***/ + +bool +CompileKeyTypes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge) +{ + KeyTypesInfo info; + + InitKeyTypesInfo(&info, keymap->ctx, &keymap->mods); + + HandleKeyTypesFile(&info, file, merge); + if (info.errorCount != 0) + goto err_info; + + if (!CopyKeyTypesToKeymap(keymap, &info)) + goto err_info; + + ClearKeyTypesInfo(&info); + return true; + +err_info: + ClearKeyTypesInfo(&info); + return false; +} diff --git a/src/xkbcomp/vmod.c b/src/xkbcomp/vmod.c new file mode 100644 index 0000000..0e8ac12 --- /dev/null +++ b/src/xkbcomp/vmod.c @@ -0,0 +1,107 @@ +/************************************************************ + * Copyright (c) 1994 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 "config.h" + +#include "xkbcomp-priv.h" +#include "text.h" +#include "expr.h" +#include "vmod.h" + +bool +HandleVModDef(struct xkb_context *ctx, struct xkb_mod_set *mods, + VModDef *stmt, enum merge_mode merge) +{ + xkb_mod_index_t i; + struct xkb_mod *mod; + xkb_mod_mask_t mapping; + + merge = (merge == MERGE_DEFAULT ? stmt->merge : merge); + + if (stmt->value) { + /* + * This is a statement such as 'virtualModifiers NumLock = Mod1'; + * it sets the vmod-to-real-mod[s] mapping directly instead of going + * through modifier_map or some such. + */ + if (!ExprResolveModMask(ctx, stmt->value, MOD_REAL, mods, &mapping)) { + log_err(ctx, + "Declaration of %s ignored\n", + xkb_atom_text(ctx, stmt->name)); + return false; + } + } + else { + mapping = 0; + } + + xkb_mods_enumerate(i, mod, mods) { + if (mod->name == stmt->name) { + if (mod->type != MOD_VIRT) { + log_err(ctx, + "Can't add a virtual modifier named \"%s\"; " + "there is already a non-virtual modifier with this name! Ignored\n", + xkb_atom_text(ctx, mod->name)); + return false; + } + + if (mod->mapping == mapping) + return true; + + if (mod->mapping != 0) { + xkb_mod_mask_t use, ignore; + + use = (merge == MERGE_OVERRIDE ? mapping : mod->mapping); + ignore = (merge == MERGE_OVERRIDE ? mod->mapping : mapping); + + log_warn(ctx, + "Virtual modifier %s defined multiple times; " + "Using %s, ignoring %s\n", + xkb_atom_text(ctx, stmt->name), + ModMaskText(ctx, mods, use), + ModMaskText(ctx, mods, ignore)); + + mapping = use; + } + + mod->mapping = mapping; + return true; + } + } + + if (mods->num_mods >= XKB_MAX_MODS) { + log_err(ctx, + "Too many modifiers defined (maximum %d)\n", + XKB_MAX_MODS); + return false; + } + + mods->mods[mods->num_mods].name = stmt->name; + mods->mods[mods->num_mods].type = MOD_VIRT; + mods->mods[mods->num_mods].mapping = mapping; + mods->num_mods++; + return true; +} diff --git a/src/xkbcomp/vmod.h b/src/xkbcomp/vmod.h new file mode 100644 index 0000000..546cf7e --- /dev/null +++ b/src/xkbcomp/vmod.h @@ -0,0 +1,34 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_VMOD_H +#define XKBCOMP_VMOD_H + +bool +HandleVModDef(struct xkb_context *ctx, struct xkb_mod_set *mods, + VModDef *stmt, enum merge_mode merge); + +#endif diff --git a/src/xkbcomp/xkbcomp-priv.h b/src/xkbcomp/xkbcomp-priv.h new file mode 100644 index 0000000..6cb774d --- /dev/null +++ b/src/xkbcomp/xkbcomp-priv.h @@ -0,0 +1,124 @@ +/************************************************************ + * Copyright (c) 1994 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. + * + ********************************************************/ + +#ifndef XKBCOMP_PRIV_H +#define XKBCOMP_PRIV_H + +#include "keymap.h" +#include "ast.h" + +struct xkb_component_names { + char *keycodes; + char *types; + char *compat; + char *symbols; +}; + +char * +text_v1_keymap_get_as_string(struct xkb_keymap *keymap); + +XkbFile * +XkbParseFile(struct xkb_context *ctx, FILE *file, + const char *file_name, const char *map); + +XkbFile * +XkbParseString(struct xkb_context *ctx, + const char *string, size_t len, + const char *file_name, const char *map); + +void +FreeXkbFile(XkbFile *file); + +XkbFile * +XkbFileFromComponents(struct xkb_context *ctx, + const struct xkb_component_names *kkctgs); + +bool +CompileKeycodes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileKeyTypes(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileSymbols(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +bool +CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, + enum merge_mode merge); + +/***====================================================================***/ + +static inline bool +ReportNotArray(struct xkb_context *ctx, const char *type, const char *field, + const char *name) +{ + log_err(ctx, + "The %s %s field is not an array; " + "Ignoring illegal assignment in %s\n", + type, field, name); + return false; +} + +static inline bool +ReportShouldBeArray(struct xkb_context *ctx, const char *type, + const char *field, const char *name) +{ + log_err(ctx, + "Missing subscript for %s %s; " + "Ignoring illegal assignment in %s\n", + type, field, name); + return false; +} + +static inline bool +ReportBadType(struct xkb_context *ctx, const char *type, const char *field, + const char *name, const char *wanted) +{ + log_err(ctx, "The %s %s field must be a %s; " + "Ignoring illegal assignment in %s\n", + type, field, wanted, name); + return false; +} + +static inline bool +ReportBadField(struct xkb_context *ctx, const char *type, const char *field, + const char *name) +{ + log_err(ctx, + "Unknown %s field %s in %s; " + "Ignoring assignment to unknown field in %s\n", + type, field, name, name); + return false; +} + +#endif diff --git a/src/xkbcomp/xkbcomp.c b/src/xkbcomp/xkbcomp.c new file mode 100644 index 0000000..48547c9 --- /dev/null +++ b/src/xkbcomp/xkbcomp.c @@ -0,0 +1,141 @@ +/* + * Copyright © 2009 Dan Nicholson + * Copyright © 2012 Intel Corporation + * 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. + * + * Authors: Dan Nicholson <dbn.lists@gmail.com> + * Ran Benita <ran234@gmail.com> + * Daniel Stone <daniel@fooishbar.org> + */ + +#include "config.h" + +#include "xkbcomp-priv.h" +#include "rules.h" + +static bool +compile_keymap_file(struct xkb_keymap *keymap, XkbFile *file) +{ + if (file->file_type != FILE_TYPE_KEYMAP) { + log_err(keymap->ctx, + "Cannot compile a %s file alone into a keymap\n", + xkb_file_type_to_string(file->file_type)); + return false; + } + + if (!CompileKeymap(file, keymap, MERGE_OVERRIDE)) { + log_err(keymap->ctx, + "Failed to compile keymap\n"); + return false; + } + + return true; +} + +static bool +text_v1_keymap_new_from_names(struct xkb_keymap *keymap, + const struct xkb_rule_names *rmlvo) +{ + bool ok; + struct xkb_component_names kccgst; + XkbFile *file; + + log_dbg(keymap->ctx, + "Compiling from RMLVO: rules '%s', model '%s', layout '%s', " + "variant '%s', options '%s'\n", + rmlvo->rules, rmlvo->model, rmlvo->layout, rmlvo->variant, + rmlvo->options); + + ok = xkb_components_from_rules(keymap->ctx, rmlvo, &kccgst); + if (!ok) { + log_err(keymap->ctx, + "Couldn't look up rules '%s', model '%s', layout '%s', " + "variant '%s', options '%s'\n", + rmlvo->rules, rmlvo->model, rmlvo->layout, rmlvo->variant, + rmlvo->options); + return false; + } + + log_dbg(keymap->ctx, + "Compiling from KcCGST: keycodes '%s', types '%s', " + "compat '%s', symbols '%s'\n", + kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); + + file = XkbFileFromComponents(keymap->ctx, &kccgst); + + free(kccgst.keycodes); + free(kccgst.types); + free(kccgst.compat); + free(kccgst.symbols); + + if (!file) { + log_err(keymap->ctx, + "Failed to generate parsed XKB file from components\n"); + return false; + } + + ok = compile_keymap_file(keymap, file); + FreeXkbFile(file); + return ok; +} + +static bool +text_v1_keymap_new_from_string(struct xkb_keymap *keymap, + const char *string, size_t len) +{ + bool ok; + XkbFile *xkb_file; + + xkb_file = XkbParseString(keymap->ctx, string, len, "(input string)", NULL); + if (!xkb_file) { + log_err(keymap->ctx, "Failed to parse input xkb string\n"); + return false; + } + + ok = compile_keymap_file(keymap, xkb_file); + FreeXkbFile(xkb_file); + return ok; +} + +static bool +text_v1_keymap_new_from_file(struct xkb_keymap *keymap, FILE *file) +{ + bool ok; + XkbFile *xkb_file; + + xkb_file = XkbParseFile(keymap->ctx, file, "(unknown file)", NULL); + if (!xkb_file) { + log_err(keymap->ctx, "Failed to parse input xkb file\n"); + return false; + } + + ok = compile_keymap_file(keymap, xkb_file); + FreeXkbFile(xkb_file); + return ok; +} + +const struct xkb_keymap_format_ops text_v1_keymap_format_ops = { + .keymap_new_from_names = text_v1_keymap_new_from_names, + .keymap_new_from_string = text_v1_keymap_new_from_string, + .keymap_new_from_file = text_v1_keymap_new_from_file, + .keymap_get_as_string = text_v1_keymap_get_as_string, +}; |
