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 true; 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 true; 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; return true; default: break; } log_wsgo(ctx, "Unexpected operator %d in ResolveLhs\n", expr->expr.op); return false; }
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 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 (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 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: 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; }
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; }
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; }
bool HandleActionDef(ExprDef *def, struct xkb_keymap *keymap, union xkb_action *action, ActionsInfo *info) { ExprDef *arg; const char *str; unsigned handler_type; if (def->expr.op != EXPR_ACTION_DECL) { log_err(keymap->ctx, "Expected an action definition, found %s\n", expr_op_type_to_string(def->expr.op)); return false; } str = xkb_atom_text(keymap->ctx, def->action.name); if (!stringToAction(str, &handler_type)) { log_err(keymap->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(keymap->ctx, field, &elemRtrn, &fieldRtrn, &arrayRtrn)) return false; if (elemRtrn) { log_err(keymap->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(keymap->ctx, "Unknown field name %s\n", fieldRtrn); return false; } if (!handleAction[handler_type](keymap, action, fieldNdx, arrayRtrn, value)) return false; } return true; }
static bool HandlePrivate(struct xkb_keymap *keymap, 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 (!ExprResolveInteger(keymap->ctx, value, &type)) return ReportMismatch(keymap, ACTION_TYPE_PRIVATE, field, "integer"); if (type < 0 || type > 255) { log_err(keymap->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(keymap->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; int len; if (!ExprResolveString(keymap->ctx, value, &val)) return ReportMismatch(keymap, action->type, field, "string"); str = xkb_atom_text(keymap->ctx, val); len = strlen(str); if (len < 1 || len > 7) { log_warn(keymap->ctx, "A private action has 7 data bytes; " "Extra %d bytes ignored\n", len - 6); return false; } strncpy((char *) act->data, str, sizeof(act->data)); return true; } else { int ndx, datum; if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) { log_err(keymap->ctx, "Array subscript must be integer; " "Illegal subscript ignored\n"); return false; } if (ndx < 0 || ndx >= sizeof(act->data)) { log_err(keymap->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(keymap->ctx, value, &datum)) return ReportMismatch(keymap, act->type, field, "integer"); if (datum < 0 || datum > 255) { log_err(keymap->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(keymap, ACTION_TYPE_NONE, field); }
static bool ExprResolveMaskLookup(struct xkb_context *ctx, const ExprDef *expr, unsigned int *val_rtrn, IdentLookupFunc lookup, const void *lookupPriv) { bool ok = 0; unsigned int l, r; 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; }
/** * 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 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, 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: 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; }