expr: move op_type/value_type_to_string functions to ast
[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 "xkbcomp-priv.h"
28 #include "text.h"
29 #include "expr.h"
30
31 typedef bool (*IdentLookupFunc)(struct xkb_context *ctx, const void *priv,
32                                 xkb_atom_t field, enum expr_value_type type,
33                                 unsigned int *val_rtrn);
34
35 bool
36 ExprResolveLhs(struct xkb_context *ctx, const ExprDef *expr,
37                const char **elem_rtrn, const char **field_rtrn,
38                ExprDef **index_rtrn)
39 {
40     switch (expr->op) {
41     case EXPR_IDENT:
42         *elem_rtrn = NULL;
43         *field_rtrn = xkb_atom_text(ctx, expr->value.str);
44         *index_rtrn = NULL;
45         return true;
46     case EXPR_FIELD_REF:
47         *elem_rtrn = xkb_atom_text(ctx, expr->value.field.element);
48         *field_rtrn = xkb_atom_text(ctx, expr->value.field.field);
49         *index_rtrn = NULL;
50         return true;
51     case EXPR_ARRAY_REF:
52         *elem_rtrn = xkb_atom_text(ctx, expr->value.array.element);
53         *field_rtrn = xkb_atom_text(ctx, expr->value.array.field);
54         *index_rtrn = expr->value.array.entry;
55         return true;
56     default:
57         break;
58     }
59     log_wsgo(ctx, "Unexpected operator %d in ResolveLhs\n", expr->op);
60     return false;
61 }
62
63 static bool
64 SimpleLookup(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
65              enum expr_value_type type, unsigned int *val_rtrn)
66 {
67     const LookupEntry *entry;
68     const char *str;
69
70     if (!priv || field == XKB_ATOM_NONE || type != EXPR_TYPE_INT)
71         return false;
72
73     str = xkb_atom_text(ctx, field);
74     for (entry = priv; entry && entry->name; entry++) {
75         if (istreq(str, entry->name)) {
76             *val_rtrn = entry->value;
77             return true;
78         }
79     }
80
81     return false;
82 }
83
84 bool
85 LookupModIndex(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
86                enum expr_value_type type, xkb_mod_index_t *val_rtrn)
87 {
88     const char *name = xkb_atom_text(ctx, field);
89
90     if (istreq(name, "none")) {
91         *val_rtrn = XkbNoModifier;
92         return true;
93     }
94
95     *val_rtrn = ModNameToIndex(name);
96     return (*val_rtrn != XKB_MOD_INVALID);
97 }
98
99 bool
100 LookupModMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field,
101               enum expr_value_type type, xkb_mod_mask_t *val_rtrn)
102 {
103     const char *str;
104     xkb_mod_index_t ndx;
105
106     if (type != EXPR_TYPE_INT)
107         return false;
108
109     str = xkb_atom_text(ctx, field);
110
111     if (istreq(str, "all"))
112         *val_rtrn  = 0xff;
113     else if (istreq(str, "none"))
114         *val_rtrn = 0;
115     else if (LookupModIndex(ctx, priv, field, type, &ndx))
116         *val_rtrn = (1 << ndx);
117     else
118         return false;
119
120     return true;
121 }
122
123 bool
124 ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr,
125                    bool *set_rtrn)
126 {
127     bool ok = false;
128     const char *ident;
129
130     switch (expr->op) {
131     case EXPR_VALUE:
132         if (expr->value_type != EXPR_TYPE_BOOLEAN) {
133             log_err(ctx,
134                     "Found constant of type %s where boolean was expected\n",
135                     expr_value_type_to_string(expr->value_type));
136             return false;
137         }
138         *set_rtrn = !!expr->value.ival;
139         return true;
140
141     case EXPR_IDENT:
142         ident = xkb_atom_text(ctx, expr->value.str);
143         if (ident) {
144             if (istreq(ident, "true") ||
145                 istreq(ident, "yes") ||
146                 istreq(ident, "on")) {
147                 *set_rtrn = true;
148                 return true;
149             }
150             else if (istreq(ident, "false") ||
151                      istreq(ident, "no") ||
152                      istreq(ident, "off")) {
153                 *set_rtrn = false;
154                 return true;
155             }
156         }
157         log_err(ctx, "Identifier \"%s\" of type boolean is unknown\n",
158                 xkb_atom_text(ctx, expr->value.str));
159         return false;
160
161     case EXPR_FIELD_REF:
162         log_err(ctx, "Default \"%s.%s\" of type boolean is unknown\n",
163                 xkb_atom_text(ctx, expr->value.field.element),
164                 xkb_atom_text(ctx, expr->value.field.field));
165         return false;
166
167     case EXPR_INVERT:
168     case EXPR_NOT:
169         ok = ExprResolveBoolean(ctx, expr, set_rtrn);
170         if (ok)
171             *set_rtrn = !*set_rtrn;
172         return ok;
173     case EXPR_ADD:
174     case EXPR_SUBTRACT:
175     case EXPR_MULTIPLY:
176     case EXPR_DIVIDE:
177     case EXPR_ASSIGN:
178     case EXPR_NEGATE:
179     case EXPR_UNARY_PLUS:
180         log_err(ctx, "%s of boolean values not permitted\n",
181                 expr_op_type_to_string(expr->op));
182         break;
183
184     default:
185         log_wsgo(ctx, "Unknown operator %d in ResolveBoolean\n", expr->op);
186         break;
187     }
188
189     return false;
190 }
191
192 bool
193 ExprResolveKeyCode(struct xkb_context *ctx, const ExprDef *expr,
194                    xkb_keycode_t *kc)
195 {
196     xkb_keycode_t leftRtrn, rightRtrn;
197     ExprDef *left, *right;
198
199     switch (expr->op) {
200     case EXPR_VALUE:
201         if (expr->value_type != EXPR_TYPE_INT) {
202             log_err(ctx,
203                     "Found constant of type %s where an int was expected\n",
204                     expr_value_type_to_string(expr->value_type));
205             return false;
206         }
207
208         *kc = expr->value.uval;
209         return true;
210
211     case EXPR_ADD:
212     case EXPR_SUBTRACT:
213     case EXPR_MULTIPLY:
214     case EXPR_DIVIDE:
215         left = expr->value.binary.left;
216         right = expr->value.binary.right;
217
218         if (!ExprResolveKeyCode(ctx, left, &leftRtrn) ||
219             !ExprResolveKeyCode(ctx, right, &rightRtrn))
220             return false;
221
222         switch (expr->op) {
223         case EXPR_ADD:
224             *kc = leftRtrn + rightRtrn;
225             break;
226         case EXPR_SUBTRACT:
227             *kc = leftRtrn - rightRtrn;
228             break;
229         case EXPR_MULTIPLY:
230             *kc = leftRtrn * rightRtrn;
231             break;
232         case EXPR_DIVIDE:
233             if (rightRtrn == 0) {
234                 log_err(ctx, "Cannot divide by zero: %d / %d\n",
235                         leftRtrn, rightRtrn);
236                 return false;
237             }
238
239             *kc = leftRtrn / rightRtrn;
240             break;
241         default:
242             break;
243         }
244
245         return true;
246
247     case EXPR_NEGATE:
248         left = expr->value.child;
249         if (!ExprResolveKeyCode(ctx, left, &leftRtrn))
250             return false;
251
252         *kc = ~leftRtrn;
253         return true;
254
255     case EXPR_UNARY_PLUS:
256         left = expr->value.child;
257         return ExprResolveKeyCode(ctx, left, kc);
258
259     default:
260         log_wsgo(ctx, "Unknown operator %d in ResolveKeyCode\n", expr->op);
261         break;
262     }
263
264     return false;
265 }
266
267 /**
268  * This function returns ... something.  It's a bit of a guess, really.
269  *
270  * If an integer is given in value ctx, it will be returned in ival.
271  * If an ident or field reference is given, the lookup function (if given)
272  * will be called.  At the moment, only SimpleLookup use this, and they both
273  * return the results in uval.  And don't support field references.
274  *
275  * Cool.
276  */
277 static bool
278 ExprResolveIntegerLookup(struct xkb_context *ctx, const ExprDef *expr,
279                          int *val_rtrn, IdentLookupFunc lookup,
280                          const void *lookupPriv)
281 {
282     bool ok = false;
283     int l, r;
284     unsigned u;
285     ExprDef *left, *right;
286
287     switch (expr->op) {
288     case EXPR_VALUE:
289         if (expr->value_type != EXPR_TYPE_INT) {
290             log_err(ctx,
291                     "Found constant of type %s where an int was expected\n",
292                     expr_value_type_to_string(expr->value_type));
293             return false;
294         }
295
296         *val_rtrn = expr->value.ival;
297         return true;
298
299     case EXPR_IDENT:
300         if (lookup)
301             ok = lookup(ctx, lookupPriv, expr->value.str, EXPR_TYPE_INT, &u);
302
303         if (!ok)
304             log_err(ctx, "Identifier \"%s\" of type int is unknown\n",
305                     xkb_atom_text(ctx, expr->value.str));
306         else
307             *val_rtrn = (int) u;
308
309         return ok;
310
311     case EXPR_FIELD_REF:
312         log_err(ctx, "Default \"%s.%s\" of type int is unknown\n",
313                 xkb_atom_text(ctx, expr->value.field.element),
314                 xkb_atom_text(ctx, expr->value.field.field));
315         return false;
316
317     case EXPR_ADD:
318     case EXPR_SUBTRACT:
319     case EXPR_MULTIPLY:
320     case EXPR_DIVIDE:
321         left = expr->value.binary.left;
322         right = expr->value.binary.right;
323         if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv) ||
324             !ExprResolveIntegerLookup(ctx, right, &r, lookup, lookupPriv))
325             return false;
326
327         switch (expr->op) {
328         case EXPR_ADD:
329             *val_rtrn = l + r;
330             break;
331         case EXPR_SUBTRACT:
332             *val_rtrn = l - r;
333             break;
334         case EXPR_MULTIPLY:
335             *val_rtrn = l * r;
336             break;
337         case EXPR_DIVIDE:
338             if (r == 0) {
339                 log_err(ctx, "Cannot divide by zero: %d / %d\n", l, r);
340                 return false;
341             }
342             *val_rtrn = l / r;
343             break;
344         default:
345             break;
346         }
347
348         return true;
349
350     case EXPR_ASSIGN:
351         log_wsgo(ctx, "Assignment operator not implemented yet\n");
352         break;
353
354     case EXPR_NOT:
355         log_err(ctx, "The ! operator cannot be applied to an integer\n");
356         return false;
357
358     case EXPR_INVERT:
359     case EXPR_NEGATE:
360         left = expr->value.child;
361         if (!ExprResolveIntegerLookup(ctx, left, &l, lookup, lookupPriv))
362             return false;
363
364         *val_rtrn = (expr->op == EXPR_NEGATE ? -l : ~l);
365         return true;
366
367     case EXPR_UNARY_PLUS:
368         left = expr->value.child;
369         return ExprResolveIntegerLookup(ctx, left, val_rtrn, lookup,
370                                         lookupPriv);
371
372     default:
373         log_wsgo(ctx, "Unknown operator %d in ResolveInteger\n", expr->op);
374         break;
375     }
376
377     return false;
378 }
379
380 bool
381 ExprResolveInteger(struct xkb_context *ctx, const ExprDef *expr,
382                    int *val_rtrn)
383 {
384     return ExprResolveIntegerLookup(ctx, expr, val_rtrn, NULL, NULL);
385 }
386
387 bool
388 ExprResolveGroup(struct xkb_context *ctx, const ExprDef *expr,
389                  xkb_group_index_t *group_rtrn)
390 {
391     bool ok;
392     int result;
393     static const LookupEntry group_names[] = {
394         { "group1", 1 },
395         { "group2", 2 },
396         { "group3", 3 },
397         { "group4", 4 },
398         { "group5", 5 },
399         { "group6", 6 },
400         { "group7", 7 },
401         { "group8", 8 },
402         { NULL, 0 }
403     };
404
405     ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup,
406                                   group_names);
407     if (!ok)
408         return false;
409
410     if (result <= 0 || result > XkbNumKbdGroups) {
411         log_err(ctx, "Group index %u is out of range (1..%d)\n",
412                 result, XkbNumKbdGroups);
413         return false;
414     }
415
416     *group_rtrn = (xkb_group_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     static const LookupEntry level_names[] = {
427         { "level1", 1 },
428         { "level2", 2 },
429         { "level3", 3 },
430         { "level4", 4 },
431         { "level5", 5 },
432         { "level6", 6 },
433         { "level7", 7 },
434         { "level8", 8 },
435         { NULL, 0 }
436     };
437
438     ok = ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup,
439                                   level_names);
440     if (!ok)
441         return false;
442
443     if (result < 1 || result > XkbMaxShiftLevel) {
444         log_err(ctx, "Shift level %d is out of range (1..%d)\n",
445                 result, XkbMaxShiftLevel);
446         return false;
447     }
448
449     /* Level is zero-indexed from now on. */
450     *level_rtrn = (unsigned int) (result - 1);
451     return true;
452 }
453
454 bool
455 ExprResolveButton(struct xkb_context *ctx, const ExprDef *expr, int *btn_rtrn)
456 {
457     int result;
458     static const LookupEntry button_names[] = {
459         { "button1", 1 },
460         { "button2", 2 },
461         { "button3", 3 },
462         { "button4", 4 },
463         { "button5", 5 },
464         { "default", 0 },
465         { NULL, 0 }
466     };
467
468     if (!ExprResolveIntegerLookup(ctx, expr, &result, SimpleLookup,
469                                   button_names))
470         return false;
471
472     *btn_rtrn = result;
473     return true;
474 }
475
476 bool
477 ExprResolveString(struct xkb_context *ctx, const ExprDef *expr,
478                   const char **val_rtrn)
479 {
480     switch (expr->op) {
481     case EXPR_VALUE:
482         if (expr->value_type != EXPR_TYPE_STRING) {
483             log_err(ctx, "Found constant of type %s, expected a string\n",
484                     expr_value_type_to_string(expr->value_type));
485             return false;
486         }
487
488         *val_rtrn = xkb_atom_text(ctx, expr->value.str);
489         return true;
490
491     case EXPR_IDENT:
492         log_err(ctx, "Identifier \"%s\" of type string not found\n",
493                 xkb_atom_text(ctx, expr->value.str));
494         return false;
495
496     case EXPR_FIELD_REF:
497         log_err(ctx, "Default \"%s.%s\" of type string not found\n",
498                 xkb_atom_text(ctx, expr->value.field.element),
499                 xkb_atom_text(ctx, expr->value.field.field));
500         return false;
501
502     case EXPR_ADD:
503     case EXPR_SUBTRACT:
504     case EXPR_MULTIPLY:
505     case EXPR_DIVIDE:
506     case EXPR_ASSIGN:
507     case EXPR_NEGATE:
508     case EXPR_INVERT:
509     case EXPR_NOT:
510     case EXPR_UNARY_PLUS:
511         log_err(ctx, "%s of strings not permitted\n",
512                 expr_op_type_to_string(expr->op));
513         return false;
514
515     default:
516         log_wsgo(ctx, "Unknown operator %d in ResolveString\n", expr->op);
517         break;
518     }
519     return false;
520 }
521
522 bool
523 ExprResolveKeyName(struct xkb_context *ctx, const ExprDef *expr,
524                    char name[XkbKeyNameLength])
525 {
526     switch (expr->op) {
527     case EXPR_VALUE:
528         if (expr->value_type != EXPR_TYPE_KEYNAME) {
529             log_err(ctx, "Found constant of type %s, expected a key name\n",
530                     expr_value_type_to_string(expr->value_type));
531             return false;
532         }
533         strncpy(name, expr->value.keyName, XkbKeyNameLength);
534         return true;
535
536     case EXPR_IDENT:
537         log_err(ctx, "Identifier \"%s\" of type string not found\n",
538                 xkb_atom_text(ctx, expr->value.str));
539         return false;
540
541     case EXPR_FIELD_REF:
542         log_err(ctx, "Default \"%s.%s\" of type key name not found\n",
543                 xkb_atom_text(ctx, expr->value.field.element),
544                 xkb_atom_text(ctx, expr->value.field.field));
545         return false;
546
547     case EXPR_ADD:
548     case EXPR_SUBTRACT:
549     case EXPR_MULTIPLY:
550     case EXPR_DIVIDE:
551     case EXPR_ASSIGN:
552     case EXPR_NEGATE:
553     case EXPR_INVERT:
554     case EXPR_NOT:
555     case EXPR_UNARY_PLUS:
556         log_err(ctx, "%s of key name values not permitted\n",
557                 expr_op_type_to_string(expr->op));
558         return false;
559
560     default:
561         log_wsgo(ctx, "Unknown operator %d in ResolveKeyName\n", expr->op);
562         break;
563     }
564     return false;
565 }
566
567 bool
568 ExprResolveEnum(struct xkb_context *ctx, const ExprDef *expr,
569                 unsigned int *val_rtrn, const LookupEntry *values)
570 {
571     if (expr->op != EXPR_IDENT) {
572         log_err(ctx, "Found a %s where an enumerated value was expected\n",
573                 expr_op_type_to_string(expr->op));
574         return false;
575     }
576
577     if (!SimpleLookup(ctx, values, expr->value.str, EXPR_TYPE_INT,
578                       val_rtrn)) {
579         log_err(ctx, "Illegal identifier %s; expected one of:\n",
580                 xkb_atom_text(ctx, expr->value.str));
581         while (values && values->name)
582         {
583             log_err(ctx, "\t%s\n", values->name);
584             values++;
585         }
586         return false;
587     }
588
589     return true;
590 }
591
592 static bool
593 ExprResolveMaskLookup(struct xkb_context *ctx, const ExprDef *expr,
594                       unsigned int *val_rtrn, IdentLookupFunc lookup,
595                       const void *lookupPriv)
596 {
597     bool ok = 0;
598     unsigned int l, r;
599     int v;
600     ExprDef *left, *right;
601     const char *bogus = NULL;
602
603     switch (expr->op) {
604     case EXPR_VALUE:
605         if (expr->value_type != EXPR_TYPE_INT) {
606             log_err(ctx,
607                     "Found constant of type %s where a mask was expected\n",
608                     expr_value_type_to_string(expr->value_type));
609             return false;
610         }
611         *val_rtrn = (unsigned int) expr->value.ival;
612         return true;
613
614     case EXPR_IDENT:
615         ok = lookup(ctx, lookupPriv, expr->value.str, EXPR_TYPE_INT,
616                     val_rtrn);
617         if (!ok)
618             log_err(ctx, "Identifier \"%s\" of type int is unknown\n",
619                     xkb_atom_text(ctx, expr->value.str));
620         return ok;
621
622     case EXPR_FIELD_REF:
623         log_err(ctx, "Default \"%s.%s\" of type int is unknown\n",
624                 xkb_atom_text(ctx, expr->value.field.element),
625                 xkb_atom_text(ctx, expr->value.field.field));
626         return false;
627
628     case EXPR_ARRAY_REF:
629         bogus = "array reference";
630
631     case EXPR_ACTION_DECL:
632         if (bogus == NULL)
633             bogus = "function use";
634         log_err(ctx,
635                 "Unexpected %s in mask expression; Expression Ignored\n",
636                 bogus);
637         return false;
638
639     case EXPR_ADD:
640     case EXPR_SUBTRACT:
641     case EXPR_MULTIPLY:
642     case EXPR_DIVIDE:
643         left = expr->value.binary.left;
644         right = expr->value.binary.right;
645         if (!ExprResolveMaskLookup(ctx, left, &l, lookup, lookupPriv) ||
646             !ExprResolveMaskLookup(ctx, right, &r, lookup, lookupPriv))
647             return false;
648
649         switch (expr->op) {
650         case EXPR_ADD:
651             *val_rtrn = l | r;
652             break;
653         case EXPR_SUBTRACT:
654             *val_rtrn = l & (~r);
655             break;
656         case EXPR_MULTIPLY:
657         case EXPR_DIVIDE:
658             log_err(ctx, "Cannot %s masks; Illegal operation ignored\n",
659                     (expr->op == EXPR_DIVIDE ? "divide" : "multiply"));
660             return false;
661         default:
662             break;
663         }
664
665         return true;
666
667     case EXPR_ASSIGN:
668         log_wsgo(ctx, "Assignment operator not implemented yet\n");
669         break;
670
671     case EXPR_INVERT:
672         left = expr->value.child;
673         if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv))
674             return false;
675
676         *val_rtrn = ~v;
677         return true;
678
679     case EXPR_UNARY_PLUS:
680     case EXPR_NEGATE:
681     case EXPR_NOT:
682         left = expr->value.child;
683         if (!ExprResolveIntegerLookup(ctx, left, &v, lookup, lookupPriv))
684             log_err(ctx, "The %s operator cannot be used with a mask\n",
685                     (expr->op == EXPR_NEGATE ? "-" : "!"));
686         return false;
687
688     default:
689         log_wsgo(ctx, "Unknown operator %d in ResolveMask\n", expr->op);
690         break;
691     }
692
693     return false;
694 }
695
696 bool
697 ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr,
698                 unsigned int *mask_rtrn, const LookupEntry *values)
699 {
700     return ExprResolveMaskLookup(ctx, expr, mask_rtrn, SimpleLookup, values);
701 }
702
703 bool
704 ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr,
705                    xkb_mod_mask_t *mask_rtrn)
706 {
707     return ExprResolveMaskLookup(ctx, expr, mask_rtrn, LookupModMask, NULL);
708 }
709
710 bool
711 ExprResolveVModMask(struct xkb_keymap *keymap, const ExprDef *expr,
712                     xkb_mod_mask_t *mask_rtrn)
713 {
714     return ExprResolveMaskLookup(keymap->ctx, expr, mask_rtrn, LookupVModMask,
715                                  keymap);
716 }
717
718 bool
719 ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr,
720                   xkb_keysym_t *sym_rtrn)
721 {
722     int val;
723
724     if (expr->op == EXPR_IDENT) {
725         const char *str;
726         str = xkb_atom_text(ctx, expr->value.str);
727         *sym_rtrn = xkb_keysym_from_name(str);
728         if (*sym_rtrn != XKB_KEY_NoSymbol)
729             return true;
730     }
731
732     if (!ExprResolveInteger(ctx, expr, &val))
733         return false;
734
735     if (val < 0 || val >= 10)
736         return false;
737
738     *sym_rtrn = ((xkb_keysym_t) val) + '0';
739     return true;
740 }