Structured log messages with a message registry
[platform/upstream/libxkbcommon.git] / src / xkbcomp / expr.c
1 /************************************************************
2  * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of Silicon Graphics not be
10  * used in advertising or publicity pertaining to distribution
11  * of the software without specific prior written permission.
12  * Silicon Graphics makes no representation about the suitability
13  * of this software for any purpose. It is provided "as is"
14  * without any express or implied warranty.
15  *
16  * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19  * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  ********************************************************/
26
27 #include "config.h"
28
29 #include "xkbcomp-priv.h"
30 #include "text.h"
31 #include "expr.h"
32 #include "keysym.h"
33
34 typedef bool (*IdentLookupFunc)(struct xkb_context *ctx, const void *priv,
35                                 xkb_atom_t field, enum expr_value_type type,
36                                 unsigned int *val_rtrn);
37
38 bool
39 ExprResolveLhs(struct xkb_context *ctx, const ExprDef *expr,
40                const char **elem_rtrn, const char **field_rtrn,
41                ExprDef **index_rtrn)
42 {
43     switch (expr->expr.op) {
44     case EXPR_IDENT:
45         *elem_rtrn = NULL;
46         *field_rtrn = xkb_atom_text(ctx, expr->ident.ident);
47         *index_rtrn = NULL;
48         return (*field_rtrn != NULL);
49     case EXPR_FIELD_REF:
50         *elem_rtrn = xkb_atom_text(ctx, expr->field_ref.element);
51         *field_rtrn = xkb_atom_text(ctx, expr->field_ref.field);
52         *index_rtrn = NULL;
53         return (*elem_rtrn != NULL && *field_rtrn != NULL);
54     case EXPR_ARRAY_REF:
55         *elem_rtrn = xkb_atom_text(ctx, expr->array_ref.element);
56         *field_rtrn = xkb_atom_text(ctx, expr->array_ref.field);
57         *index_rtrn = expr->array_ref.entry;
58         if (expr->array_ref.element != XKB_ATOM_NONE && *elem_rtrn == NULL)
59             return false;
60         if (*field_rtrn == NULL)
61             return false;
62         return true;
63     default:
64         break;
65     }
66     log_wsgo(ctx, "Unexpected operator %d in ResolveLhs\n", expr->expr.op);
67     return false;
68 }
69
70 static bool
71 SimpleLookup(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
72              enum expr_value_type type, unsigned int *val_rtrn)
73 {
74     const LookupEntry *entry;
75     const char *str;
76
77     if (!priv || field == XKB_ATOM_NONE || type != EXPR_TYPE_INT)
78         return false;
79
80     str = xkb_atom_text(ctx, field);
81     for (entry = priv; entry && entry->name; entry++) {
82         if (istreq(str, entry->name)) {
83             *val_rtrn = entry->value;
84             return true;
85         }
86     }
87
88     return false;
89 }
90
91 /* Data passed in the *priv argument for LookupModMask. */
92 typedef struct {
93     const struct xkb_mod_set *mods;
94     enum mod_type mod_type;
95 } LookupModMaskPriv;
96
97 static bool
98 LookupModMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
99               enum expr_value_type type, xkb_mod_mask_t *val_rtrn)
100 {
101     const char *str;
102     xkb_mod_index_t ndx;
103     const LookupModMaskPriv *arg = priv;
104     const struct xkb_mod_set *mods = arg->mods;
105     enum mod_type mod_type = arg->mod_type;
106
107     if (type != EXPR_TYPE_INT)
108         return false;
109
110     str = xkb_atom_text(ctx, field);
111     if (!str)
112         return false;
113
114     if (istreq(str, "all")) {
115         *val_rtrn  = MOD_REAL_MASK_ALL;
116         return true;
117     }
118
119     if (istreq(str, "none")) {
120         *val_rtrn = 0;
121         return true;
122     }
123
124     ndx = XkbModNameToIndex(mods, field, mod_type);
125     if (ndx == XKB_MOD_INVALID)
126         return false;
127
128     *val_rtrn = (1u << ndx);
129     return true;
130 }
131
132 bool
133 ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr,
134                    bool *set_rtrn)
135 {
136     bool ok = false;
137     const char *ident;
138
139     switch (expr->expr.op) {
140     case EXPR_VALUE:
141         if (expr->expr.value_type != EXPR_TYPE_BOOLEAN) {
142             log_err(ctx,
143                     "Found constant of type %s where boolean was expected\n",
144                     expr_value_type_to_string(expr->expr.value_type));
145             return false;
146         }
147         *set_rtrn = expr->boolean.set;
148         return true;
149
150     case EXPR_IDENT:
151         ident = xkb_atom_text(ctx, expr->ident.ident);
152         if (ident) {
153             if (istreq(ident, "true") ||
154                 istreq(ident, "yes") ||
155                 istreq(ident, "on")) {
156                 *set_rtrn = true;
157                 return true;
158             }
159             else if (istreq(ident, "false") ||
160                      istreq(ident, "no") ||
161                      istreq(ident, "off")) {
162                 *set_rtrn = false;
163                 return true;
164             }
165         }
166         log_err(ctx, "Identifier \"%s\" of type boolean is unknown\n", ident);
167         return false;
168
169     case EXPR_FIELD_REF:
170         log_err(ctx, "Default \"%s.%s\" of type boolean is unknown\n",
171                 xkb_atom_text(ctx, expr->field_ref.element),
172                 xkb_atom_text(ctx, expr->field_ref.field));
173         return false;
174
175     case EXPR_INVERT:
176     case EXPR_NOT:
177         ok = ExprResolveBoolean(ctx, expr->unary.child, set_rtrn);
178         if (ok)
179             *set_rtrn = !*set_rtrn;
180         return ok;
181     case EXPR_ADD:
182     case EXPR_SUBTRACT:
183     case EXPR_MULTIPLY:
184     case EXPR_DIVIDE:
185     case EXPR_ASSIGN:
186     case EXPR_NEGATE:
187     case EXPR_UNARY_PLUS:
188     case EXPR_ACTION_DECL:
189     case EXPR_ACTION_LIST:
190     case EXPR_KEYSYM_LIST:
191         log_err(ctx, "%s of boolean values not permitted\n",
192                 expr_op_type_to_string(expr->expr.op));
193         break;
194
195     default:
196         log_wsgo(ctx, "Unknown operator %d in ResolveBoolean\n",
197                  expr->expr.op);
198         break;
199     }
200
201     return false;
202 }
203
204 bool
205 ExprResolveKeyCode(struct xkb_context *ctx, const ExprDef *expr,
206                    xkb_keycode_t *kc)
207 {
208     xkb_keycode_t leftRtrn, rightRtrn;
209
210     switch (expr->expr.op) {
211     case EXPR_VALUE:
212         if (expr->expr.value_type != EXPR_TYPE_INT) {
213             log_err(ctx,
214                     "Found constant of type %s where an int was expected\n",
215                     expr_value_type_to_string(expr->expr.value_type));
216             return false;
217         }
218
219         *kc = (xkb_keycode_t) expr->integer.ival;
220         return true;
221
222     case EXPR_ADD:
223     case EXPR_SUBTRACT:
224     case EXPR_MULTIPLY:
225     case EXPR_DIVIDE:
226         if (!ExprResolveKeyCode(ctx, expr->binary.left, &leftRtrn) ||
227             !ExprResolveKeyCode(ctx, expr->binary.right, &rightRtrn))
228             return false;
229
230         switch (expr->expr.op) {
231         case EXPR_ADD:
232             *kc = leftRtrn + rightRtrn;
233             break;
234         case EXPR_SUBTRACT:
235             *kc = leftRtrn - rightRtrn;
236             break;
237         case EXPR_MULTIPLY:
238             *kc = leftRtrn * rightRtrn;
239             break;
240         case EXPR_DIVIDE:
241             if (rightRtrn == 0) {
242                 log_err(ctx, "Cannot divide by zero: %d / %d\n",
243                         leftRtrn, rightRtrn);
244                 return false;
245             }
246
247             *kc = leftRtrn / rightRtrn;
248             break;
249         default:
250             break;
251         }
252
253         return true;
254
255     case EXPR_NEGATE:
256         if (!ExprResolveKeyCode(ctx, expr->unary.child, &leftRtrn))
257             return false;
258
259         *kc = ~leftRtrn;
260         return true;
261
262     case EXPR_UNARY_PLUS:
263         return ExprResolveKeyCode(ctx, expr->unary.child, kc);
264
265     default:
266         log_wsgo(ctx, "Unknown operator %d in ResolveKeyCode\n",
267                  expr->expr.op);
268         break;
269     }
270
271     return false;
272 }
273
274 /**
275  * This function returns ... something.  It's a bit of a guess, really.
276  *
277  * If an integer is given in value ctx, it will be returned in ival.
278  * If an ident or field reference is given, the lookup function (if given)
279  * will be called.  At the moment, only SimpleLookup use this, and they both
280  * return the results in uval.  And don't support field references.
281  *
282  * Cool.
283  */
284 static bool
285 ExprResolveIntegerLookup(struct xkb_context *ctx, const ExprDef *expr,
286                          int *val_rtrn, IdentLookupFunc lookup,
287                          const void *lookupPriv)
288 {
289     bool ok = false;
290     int l, r;
291     unsigned u;
292     ExprDef *left, *right;
293
294     switch (expr->expr.op) {
295     case EXPR_VALUE:
296         if (expr->expr.value_type != EXPR_TYPE_INT) {
297             log_err(ctx,
298                     "Found constant of type %s where an int was expected\n",
299                     expr_value_type_to_string(expr->expr.value_type));
300             return false;
301         }
302
303         *val_rtrn = expr->integer.ival;
304         return true;
305
306     case EXPR_IDENT:
307         if (lookup)
308             ok = lookup(ctx, lookupPriv, expr->ident.ident, EXPR_TYPE_INT, &u);
309
310         if (!ok)
311             log_err(ctx, "Identifier \"%s\" of type int is unknown\n",
312                     xkb_atom_text(ctx, expr->ident.ident));
313         else
314             *val_rtrn = (int) u;
315
316         return ok;
317
318     case EXPR_FIELD_REF:
319         log_err(ctx, "Default \"%s.%s\" of type int is unknown\n",
320                 xkb_atom_text(ctx, expr->field_ref.element),
321                 xkb_atom_text(ctx, expr->field_ref.field));
322         return false;
323
324     case EXPR_ADD:
325     case EXPR_SUBTRACT:
326     case EXPR_MULTIPLY:
327     case EXPR_DIVIDE:
328         left = expr->binary.left;
329         right = expr->binary.right;
330         if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv) ||
331             !ExprResolveIntegerLookup(ctx, right, &r, lookup, lookupPriv))
332             return false;
333
334         switch (expr->expr.op) {
335         case EXPR_ADD:
336             *val_rtrn = l + r;
337             break;
338         case EXPR_SUBTRACT:
339             *val_rtrn = l - r;
340             break;
341         case EXPR_MULTIPLY:
342             *val_rtrn = l * r;
343             break;
344         case EXPR_DIVIDE:
345             if (r == 0) {
346                 log_err(ctx, "Cannot divide by zero: %d / %d\n", l, r);
347                 return false;
348             }
349             *val_rtrn = l / r;
350             break;
351         default:
352             log_err(ctx, "%s of integers not permitted\n",
353                     expr_op_type_to_string(expr->expr.op));
354             return false;
355         }
356
357         return true;
358
359     case EXPR_ASSIGN:
360         log_wsgo(ctx, "Assignment operator not implemented yet\n");
361         break;
362
363     case EXPR_NOT:
364         log_err(ctx, "The ! operator cannot be applied to an integer\n");
365         return false;
366
367     case EXPR_INVERT:
368     case EXPR_NEGATE:
369         left = expr->unary.child;
370         if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv))
371             return false;
372
373         *val_rtrn = (expr->expr.op == EXPR_NEGATE ? -l : ~l);
374         return true;
375
376     case EXPR_UNARY_PLUS:
377         left = expr->unary.child;
378         return ExprResolveIntegerLookup(ctx, left, val_rtrn, lookup,
379                                         lookupPriv);
380
381     default:
382         log_wsgo(ctx, "Unknown operator %d in ResolveInteger\n",
383                  expr->expr.op);
384         break;
385     }
386
387     return false;
388 }
389
390 bool
391 ExprResolveInteger(struct xkb_context *ctx, const ExprDef *expr,
392                    int *val_rtrn)
393 {
394     return ExprResolveIntegerLookup(ctx, expr, val_rtrn, NULL, NULL);
395 }
396
397 bool
398 ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr,
399                  xkb_layout_index_t *group_rtrn)
400 {
401     bool ok;
402     int result;
403
404     ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup,
405                                   groupNames);
406     if (!ok)
407         return false;
408
409     if (result <= 0 || result > XKB_MAX_GROUPS) {
410         log_err_with_code(ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX,
411                           "Group index %u is out of range (1..%d)\n",
412                           result, XKB_MAX_GROUPS);
413         return false;
414     }
415
416     *group_rtrn = (xkb_layout_index_t) result;
417     return true;
418 }
419
420 bool
421 ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr,
422                  xkb_level_index_t *level_rtrn)
423 {
424     bool ok;
425     int result;
426
427     ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup,
428                                   levelNames);
429     if (!ok)
430         return false;
431
432     if (result < 1) {
433         log_err_with_code(ctx, XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL,
434                           "Shift level %d is out of range\n", result);
435         return false;
436     }
437
438     /* Level is zero-indexed from now on. */
439     *level_rtrn = (unsigned int) (result - 1);
440     return true;
441 }
442
443 bool
444 ExprResolveButton(struct xkb_context *ctx, const ExprDef *expr, int *btn_rtrn)
445 {
446     return ExprResolveIntegerLookup(ctx, expr, btn_rtrn, SimpleLookup,
447                                     buttonNames);
448 }
449
450 bool
451 ExprResolveString(struct xkb_context *ctx, const ExprDef *expr,
452                   xkb_atom_t *val_rtrn)
453 {
454     switch (expr->expr.op) {
455     case EXPR_VALUE:
456         if (expr->expr.value_type != EXPR_TYPE_STRING) {
457             log_err(ctx, "Found constant of type %s, expected a string\n",
458                     expr_value_type_to_string(expr->expr.value_type));
459             return false;
460         }
461
462         *val_rtrn = expr->string.str;
463         return true;
464
465     case EXPR_IDENT:
466         log_err(ctx, "Identifier \"%s\" of type string not found\n",
467                 xkb_atom_text(ctx, expr->ident.ident));
468         return false;
469
470     case EXPR_FIELD_REF:
471         log_err(ctx, "Default \"%s.%s\" of type string not found\n",
472                 xkb_atom_text(ctx, expr->field_ref.element),
473                 xkb_atom_text(ctx, expr->field_ref.field));
474         return false;
475
476     case EXPR_ADD:
477     case EXPR_SUBTRACT:
478     case EXPR_MULTIPLY:
479     case EXPR_DIVIDE:
480     case EXPR_ASSIGN:
481     case EXPR_NEGATE:
482     case EXPR_INVERT:
483     case EXPR_NOT:
484     case EXPR_UNARY_PLUS:
485     case EXPR_ACTION_DECL:
486     case EXPR_ACTION_LIST:
487     case EXPR_KEYSYM_LIST:
488         log_err(ctx, "%s of strings not permitted\n",
489                 expr_op_type_to_string(expr->expr.op));
490         return false;
491
492     default:
493         log_wsgo(ctx, "Unknown operator %d in ResolveString\n",
494                  expr->expr.op);
495         break;
496     }
497     return false;
498 }
499
500 bool
501 ExprResolveEnum(struct xkb_context *ctx, const ExprDef *expr,
502                 unsigned int *val_rtrn, const LookupEntry *values)
503 {
504     if (expr->expr.op != EXPR_IDENT) {
505         log_err(ctx, "Found a %s where an enumerated value was expected\n",
506                 expr_op_type_to_string(expr->expr.op));
507         return false;
508     }
509
510     if (!SimpleLookup(ctx, values, expr->ident.ident, EXPR_TYPE_INT,
511                       val_rtrn)) {
512         log_err(ctx, "Illegal identifier %s; expected one of:\n",
513                 xkb_atom_text(ctx, expr->ident.ident));
514         while (values && values->name)
515         {
516             log_err(ctx, "\t%s\n", values->name);
517             values++;
518         }
519         return false;
520     }
521
522     return true;
523 }
524
525 static bool
526 ExprResolveMaskLookup(struct xkb_context *ctx, const ExprDef *expr,
527                       unsigned int *val_rtrn, IdentLookupFunc lookup,
528                       const void *lookupPriv)
529 {
530     bool ok = false;
531     unsigned int l = 0, r = 0;
532     int v;
533     ExprDef *left, *right;
534     const char *bogus = NULL;
535
536     switch (expr->expr.op) {
537     case EXPR_VALUE:
538         if (expr->expr.value_type != EXPR_TYPE_INT) {
539             log_err(ctx,
540                     "Found constant of type %s where a mask was expected\n",
541                     expr_value_type_to_string(expr->expr.value_type));
542             return false;
543         }
544         *val_rtrn = (unsigned int) expr->integer.ival;
545         return true;
546
547     case EXPR_IDENT:
548         ok = lookup(ctx, lookupPriv, expr->ident.ident, EXPR_TYPE_INT,
549                     val_rtrn);
550         if (!ok)
551             log_err(ctx, "Identifier \"%s\" of type int is unknown\n",
552                     xkb_atom_text(ctx, expr->ident.ident));
553         return ok;
554
555     case EXPR_FIELD_REF:
556         log_err(ctx, "Default \"%s.%s\" of type int is unknown\n",
557                 xkb_atom_text(ctx, expr->field_ref.element),
558                 xkb_atom_text(ctx, expr->field_ref.field));
559         return false;
560
561     case EXPR_ARRAY_REF:
562         bogus = "array reference";
563         /* fallthrough */
564     case EXPR_ACTION_DECL:
565         if (bogus == NULL)
566             bogus = "function use";
567         log_err(ctx,
568                 "Unexpected %s in mask expression; Expression Ignored\n",
569                 bogus);
570         return false;
571
572     case EXPR_ADD:
573     case EXPR_SUBTRACT:
574     case EXPR_MULTIPLY:
575     case EXPR_DIVIDE:
576         left = expr->binary.left;
577         right = expr->binary.right;
578         if (!ExprResolveMaskLookup(ctx, left, &l, lookup, lookupPriv) ||
579             !ExprResolveMaskLookup(ctx, right, &r, lookup, lookupPriv))
580             return false;
581
582         switch (expr->expr.op) {
583         case EXPR_ADD:
584             *val_rtrn = l | r;
585             break;
586         case EXPR_SUBTRACT:
587             *val_rtrn = l & (~r);
588             break;
589         case EXPR_MULTIPLY:
590         case EXPR_DIVIDE:
591             log_err(ctx, "Cannot %s masks; Illegal operation ignored\n",
592                     (expr->expr.op == EXPR_DIVIDE ? "divide" : "multiply"));
593             return false;
594         default:
595             break;
596         }
597
598         return true;
599
600     case EXPR_ASSIGN:
601         log_wsgo(ctx, "Assignment operator not implemented yet\n");
602         break;
603
604     case EXPR_INVERT:
605         left = expr->unary.child;
606         if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv))
607             return false;
608
609         *val_rtrn = ~v;
610         return true;
611
612     case EXPR_UNARY_PLUS:
613     case EXPR_NEGATE:
614     case EXPR_NOT:
615         left = expr->unary.child;
616         if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv))
617             log_err(ctx, "The %s operator cannot be used with a mask\n",
618                     (expr->expr.op == EXPR_NEGATE ? "-" : "!"));
619         return false;
620
621     default:
622         log_wsgo(ctx, "Unknown operator %d in ResolveMask\n",
623                  expr->expr.op);
624         break;
625     }
626
627     return false;
628 }
629
630 bool
631 ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr,
632                 unsigned int *mask_rtrn, const LookupEntry *values)
633 {
634     return ExprResolveMaskLookup(ctx, expr, mask_rtrn, SimpleLookup, values);
635 }
636
637 bool
638 ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr,
639                    enum mod_type mod_type, const struct xkb_mod_set *mods,
640                    xkb_mod_mask_t *mask_rtrn)
641 {
642     LookupModMaskPriv priv = { .mods = mods, .mod_type = mod_type };
643     return ExprResolveMaskLookup(ctx, expr, mask_rtrn, LookupModMask, &priv);
644 }
645
646 bool
647 ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr,
648                   xkb_keysym_t *sym_rtrn)
649 {
650     int val;
651
652     if (expr->expr.op == EXPR_IDENT) {
653         const char *str = xkb_atom_text(ctx, expr->ident.ident);
654         *sym_rtrn = xkb_keysym_from_name(str, 0);
655         if (*sym_rtrn != XKB_KEY_NoSymbol)
656             return true;
657     }
658
659     if (!ExprResolveInteger(ctx, expr, &val))
660         return false;
661
662     if (val < XKB_KEYSYM_MIN) {
663         log_warn_with_code(ctx, XKB_WARNING_UNRECOGNIZED_KEYSYM,
664                            "unrecognized keysym \"-0x%x\" (%d)\n",
665                            (unsigned int) -val, val);
666         return false;
667     }
668
669     /* Special case for digits 0..9 */
670     if (val < 10) {
671         *sym_rtrn = XKB_KEY_0 + (xkb_keysym_t) val;
672         return true;
673     }
674
675     if (val <= XKB_KEYSYM_MAX) {
676         *sym_rtrn = (xkb_keysym_t) val;
677         return true;
678     }
679
680     log_warn_with_code(ctx, XKB_WARNING_UNRECOGNIZED_KEYSYM,
681                        "unrecognized keysym \"0x%x\" (%d)\n",
682                        (unsigned int) val, val);
683     return false;
684
685 }
686
687 bool
688 ExprResolveMod(struct xkb_context *ctx, const ExprDef *def,
689                enum mod_type mod_type, const struct xkb_mod_set *mods,
690                xkb_mod_index_t *ndx_rtrn)
691 {
692     xkb_mod_index_t ndx;
693     xkb_atom_t name;
694
695     if (def->expr.op != EXPR_IDENT) {
696         log_err(ctx,
697                 "Cannot resolve virtual modifier: "
698                 "found %s where a virtual modifier name was expected\n",
699                 expr_op_type_to_string(def->expr.op));
700         return false;
701     }
702
703     name = def->ident.ident;
704     ndx = XkbModNameToIndex(mods, name, mod_type);
705     if (ndx == XKB_MOD_INVALID) {
706         log_err(ctx,
707                 "Cannot resolve virtual modifier: "
708                 "\"%s\" was not previously declared\n",
709                 xkb_atom_text(ctx, name));
710         return false;
711     }
712
713     *ndx_rtrn = ndx;
714     return true;
715 }