Add ExprResolveButton helper
[platform/upstream/libxkbcommon.git] / src / xkbcomp / action.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.h"
28 #include "xkbmisc.h"
29 #include "tokens.h"
30 #include "expr.h"
31
32 #include "keycodes.h"
33 #include "vmod.h"
34 #include "misc.h"
35 #include "action.h"
36 #include "misc.h"
37
38 static Bool actionsInitialized;
39 static ExprDef constTrue;
40 static ExprDef constFalse;
41
42 /***====================================================================***/
43
44 static Bool
45 stringToAction(const char *str, unsigned *type_rtrn)
46 {
47     if (str == NULL)
48         return False;
49
50     if (uStrCaseCmp(str, "noaction") == 0)
51         *type_rtrn = XkbSA_NoAction;
52     else if (uStrCaseCmp(str, "setmods") == 0)
53         *type_rtrn = XkbSA_SetMods;
54     else if (uStrCaseCmp(str, "latchmods") == 0)
55         *type_rtrn = XkbSA_LatchMods;
56     else if (uStrCaseCmp(str, "lockmods") == 0)
57         *type_rtrn = XkbSA_LockMods;
58     else if (uStrCaseCmp(str, "setgroup") == 0)
59         *type_rtrn = XkbSA_SetGroup;
60     else if (uStrCaseCmp(str, "latchgroup") == 0)
61         *type_rtrn = XkbSA_LatchGroup;
62     else if (uStrCaseCmp(str, "lockgroup") == 0)
63         *type_rtrn = XkbSA_LockGroup;
64     else if (uStrCaseCmp(str, "moveptr") == 0)
65         *type_rtrn = XkbSA_MovePtr;
66     else if (uStrCaseCmp(str, "movepointer") == 0)
67         *type_rtrn = XkbSA_MovePtr;
68     else if (uStrCaseCmp(str, "ptrbtn") == 0)
69         *type_rtrn = XkbSA_PtrBtn;
70     else if (uStrCaseCmp(str, "pointerbutton") == 0)
71         *type_rtrn = XkbSA_PtrBtn;
72     else if (uStrCaseCmp(str, "lockptrbtn") == 0)
73         *type_rtrn = XkbSA_LockPtrBtn;
74     else if (uStrCaseCmp(str, "lockpointerbutton") == 0)
75         *type_rtrn = XkbSA_LockPtrBtn;
76     else if (uStrCaseCmp(str, "lockptrbutton") == 0)
77         *type_rtrn = XkbSA_LockPtrBtn;
78     else if (uStrCaseCmp(str, "lockpointerbtn") == 0)
79         *type_rtrn = XkbSA_LockPtrBtn;
80     else if (uStrCaseCmp(str, "setptrdflt") == 0)
81         *type_rtrn = XkbSA_SetPtrDflt;
82     else if (uStrCaseCmp(str, "setpointerdefault") == 0)
83         *type_rtrn = XkbSA_SetPtrDflt;
84     else if (uStrCaseCmp(str, "isolock") == 0)
85         *type_rtrn = XkbSA_ISOLock;
86     else if (uStrCaseCmp(str, "terminate") == 0)
87         *type_rtrn = XkbSA_Terminate;
88     else if (uStrCaseCmp(str, "terminateserver") == 0)
89         *type_rtrn = XkbSA_Terminate;
90     else if (uStrCaseCmp(str, "switchscreen") == 0)
91         *type_rtrn = XkbSA_SwitchScreen;
92     else if (uStrCaseCmp(str, "setcontrols") == 0)
93         *type_rtrn = XkbSA_SetControls;
94     else if (uStrCaseCmp(str, "lockcontrols") == 0)
95         *type_rtrn = XkbSA_LockControls;
96     else if (uStrCaseCmp(str, "actionmessage") == 0)
97         *type_rtrn = XkbSA_ActionMessage;
98     else if (uStrCaseCmp(str, "messageaction") == 0)
99         *type_rtrn = XkbSA_ActionMessage;
100     else if (uStrCaseCmp(str, "message") == 0)
101         *type_rtrn = XkbSA_ActionMessage;
102     else if (uStrCaseCmp(str, "redirect") == 0)
103         *type_rtrn = XkbSA_RedirectKey;
104     else if (uStrCaseCmp(str, "redirectkey") == 0)
105         *type_rtrn = XkbSA_RedirectKey;
106     else if (uStrCaseCmp(str, "devbtn") == 0)
107         *type_rtrn = XkbSA_DeviceBtn;
108     else if (uStrCaseCmp(str, "devicebtn") == 0)
109         *type_rtrn = XkbSA_DeviceBtn;
110     else if (uStrCaseCmp(str, "devbutton") == 0)
111         *type_rtrn = XkbSA_DeviceBtn;
112     else if (uStrCaseCmp(str, "devicebutton") == 0)
113         *type_rtrn = XkbSA_DeviceBtn;
114     else if (uStrCaseCmp(str, "lockdevbtn") == 0)
115         *type_rtrn = XkbSA_DeviceBtn;
116     else if (uStrCaseCmp(str, "lockdevicebtn") == 0)
117         *type_rtrn = XkbSA_LockDeviceBtn;
118     else if (uStrCaseCmp(str, "lockdevbutton") == 0)
119         *type_rtrn = XkbSA_LockDeviceBtn;
120     else if (uStrCaseCmp(str, "lockdevicebutton") == 0)
121         *type_rtrn = XkbSA_LockDeviceBtn;
122     else if (uStrCaseCmp(str, "devval") == 0)
123         *type_rtrn = XkbSA_DeviceValuator;
124     else if (uStrCaseCmp(str, "deviceval") == 0)
125         *type_rtrn = XkbSA_DeviceValuator;
126     else if (uStrCaseCmp(str, "devvaluator") == 0)
127         *type_rtrn = XkbSA_DeviceValuator;
128     else if (uStrCaseCmp(str, "devicevaluator") == 0)
129         *type_rtrn = XkbSA_DeviceValuator;
130     else if (uStrCaseCmp(str, "private") == 0)
131         *type_rtrn = PrivateAction;
132     else
133         return False;
134     return True;
135 }
136
137 static Bool
138 stringToField(char *str, unsigned *field_rtrn)
139 {
140
141     if (str == NULL)
142         return False;
143
144     if (uStrCaseCmp(str, "clearlocks") == 0)
145         *field_rtrn = F_ClearLocks;
146     else if (uStrCaseCmp(str, "latchtolock") == 0)
147         *field_rtrn = F_LatchToLock;
148     else if (uStrCaseCmp(str, "genkeyevent") == 0)
149         *field_rtrn = F_GenKeyEvent;
150     else if (uStrCaseCmp(str, "generatekeyevent") == 0)
151         *field_rtrn = F_GenKeyEvent;
152     else if (uStrCaseCmp(str, "report") == 0)
153         *field_rtrn = F_Report;
154     else if (uStrCaseCmp(str, "default") == 0)
155         *field_rtrn = F_Default;
156     else if (uStrCaseCmp(str, "affect") == 0)
157         *field_rtrn = F_Affect;
158     else if (uStrCaseCmp(str, "increment") == 0)
159         *field_rtrn = F_Increment;
160     else if (uStrCaseCmp(str, "mods") == 0)
161         *field_rtrn = F_Modifiers;
162     else if (uStrCaseCmp(str, "modifiers") == 0)
163         *field_rtrn = F_Modifiers;
164     else if (uStrCaseCmp(str, "group") == 0)
165         *field_rtrn = F_Group;
166     else if (uStrCaseCmp(str, "x") == 0)
167         *field_rtrn = F_X;
168     else if (uStrCaseCmp(str, "y") == 0)
169         *field_rtrn = F_Y;
170     else if (uStrCaseCmp(str, "accel") == 0)
171         *field_rtrn = F_Accel;
172     else if (uStrCaseCmp(str, "accelerate") == 0)
173         *field_rtrn = F_Accel;
174     else if (uStrCaseCmp(str, "repeat") == 0)
175         *field_rtrn = F_Accel;
176     else if (uStrCaseCmp(str, "button") == 0)
177         *field_rtrn = F_Button;
178     else if (uStrCaseCmp(str, "value") == 0)
179         *field_rtrn = F_Value;
180     else if (uStrCaseCmp(str, "controls") == 0)
181         *field_rtrn = F_Controls;
182     else if (uStrCaseCmp(str, "ctrls") == 0)
183         *field_rtrn = F_Controls;
184     else if (uStrCaseCmp(str, "type") == 0)
185         *field_rtrn = F_Type;
186     else if (uStrCaseCmp(str, "count") == 0)
187         *field_rtrn = F_Count;
188     else if (uStrCaseCmp(str, "screen") == 0)
189         *field_rtrn = F_Screen;
190     else if (uStrCaseCmp(str, "same") == 0)
191         *field_rtrn = F_Same;
192     else if (uStrCaseCmp(str, "sameserver") == 0)
193         *field_rtrn = F_Same;
194     else if (uStrCaseCmp(str, "data") == 0)
195         *field_rtrn = F_Data;
196     else if (uStrCaseCmp(str, "device") == 0)
197         *field_rtrn = F_Device;
198     else if (uStrCaseCmp(str, "dev") == 0)
199         *field_rtrn = F_Device;
200     else if (uStrCaseCmp(str, "key") == 0)
201         *field_rtrn = F_Keycode;
202     else if (uStrCaseCmp(str, "keycode") == 0)
203         *field_rtrn = F_Keycode;
204     else if (uStrCaseCmp(str, "kc") == 0)
205         *field_rtrn = F_Keycode;
206     else if (uStrCaseCmp(str, "clearmods") == 0)
207         *field_rtrn = F_ModsToClear;
208     else if (uStrCaseCmp(str, "clearmodifiers") == 0)
209         *field_rtrn = F_ModsToClear;
210     else
211         return False;
212     return True;
213 }
214
215 static char *
216 fieldText(unsigned field)
217 {
218     static char buf[32];
219
220     switch (field)
221     {
222     case F_ClearLocks:
223         strcpy(buf, "clearLocks");
224         break;
225     case F_LatchToLock:
226         strcpy(buf, "latchToLock");
227         break;
228     case F_GenKeyEvent:
229         strcpy(buf, "genKeyEvent");
230         break;
231     case F_Report:
232         strcpy(buf, "report");
233         break;
234     case F_Default:
235         strcpy(buf, "default");
236         break;
237     case F_Affect:
238         strcpy(buf, "affect");
239         break;
240     case F_Increment:
241         strcpy(buf, "increment");
242         break;
243     case F_Modifiers:
244         strcpy(buf, "modifiers");
245         break;
246     case F_Group:
247         strcpy(buf, "group");
248         break;
249     case F_X:
250         strcpy(buf, "x");
251         break;
252     case F_Y:
253         strcpy(buf, "y");
254         break;
255     case F_Accel:
256         strcpy(buf, "accel");
257         break;
258     case F_Button:
259         strcpy(buf, "button");
260         break;
261     case F_Value:
262         strcpy(buf, "value");
263         break;
264     case F_Controls:
265         strcpy(buf, "controls");
266         break;
267     case F_Type:
268         strcpy(buf, "type");
269         break;
270     case F_Count:
271         strcpy(buf, "count");
272         break;
273     case F_Screen:
274         strcpy(buf, "screen");
275         break;
276     case F_Same:
277         strcpy(buf, "sameServer");
278         break;
279     case F_Data:
280         strcpy(buf, "data");
281         break;
282     case F_Device:
283         strcpy(buf, "device");
284         break;
285     case F_Keycode:
286         strcpy(buf, "keycode");
287         break;
288     case F_ModsToClear:
289         strcpy(buf, "clearmods");
290         break;
291     default:
292         strcpy(buf, "unknown");
293         break;
294     }
295     return buf;
296 }
297
298 /***====================================================================***/
299
300 static Bool
301 ReportMismatch(unsigned action, unsigned field, const char *type)
302 {
303     ERROR("Value of %s field must be of type %s\n", fieldText(field), type);
304     ACTION("Action %s definition ignored\n", XkbcActionTypeText(action));
305     return False;
306 }
307
308 static Bool
309 ReportIllegal(unsigned action, unsigned field)
310 {
311     ERROR("Field %s is not defined for an action of type %s\n",
312            fieldText(field), XkbcActionTypeText(action));
313     ACTION("Action definition ignored\n");
314     return False;
315 }
316
317 static Bool
318 ReportActionNotArray(unsigned action, unsigned field)
319 {
320     ERROR("The %s field in the %s action is not an array\n",
321            fieldText(field), XkbcActionTypeText(action));
322     ACTION("Action definition ignored\n");
323     return False;
324 }
325
326 static Bool
327 ReportNotFound(unsigned action, unsigned field, const char *what, char *bad)
328 {
329     ERROR("%s named %s not found\n", what, bad);
330     ACTION("Ignoring the %s field of an %s action\n", fieldText(field),
331             XkbcActionTypeText(action));
332     return False;
333 }
334
335 static Bool
336 HandleNoAction(struct xkb_desc * xkb,
337                struct xkb_any_action * action,
338                unsigned field, ExprDef * array_ndx, ExprDef * value)
339 {
340     return ReportIllegal(action->type, field);
341 }
342
343 static Bool
344 CheckLatchLockFlags(unsigned action,
345                     unsigned field, ExprDef * value, unsigned *flags_inout)
346 {
347     unsigned tmp;
348     ExprResult result;
349
350     if (field == F_ClearLocks)
351         tmp = XkbSA_ClearLocks;
352     else if (field == F_LatchToLock)
353         tmp = XkbSA_LatchToLock;
354     else
355         return False;           /* WSGO! */
356     if (!ExprResolveBoolean(value, &result))
357         return ReportMismatch(action, field, "boolean");
358     if (result.uval)
359         *flags_inout |= tmp;
360     else
361         *flags_inout &= ~tmp;
362     return True;
363 }
364
365 static Bool
366 CheckModifierField(struct xkb_desc * xkb,
367                    unsigned action,
368                    ExprDef * value,
369                    unsigned *flags_inout, unsigned *mods_rtrn)
370 {
371     ExprResult rtrn;
372
373     if (value->op == ExprIdent)
374     {
375         const char *valStr;
376         valStr = XkbcAtomText(value->value.str);
377         if (valStr && ((uStrCaseCmp(valStr, "usemodmapmods") == 0) ||
378                        (uStrCaseCmp(valStr, "modmapmods") == 0)))
379         {
380
381             *mods_rtrn = 0;
382             *flags_inout |= XkbSA_UseModMapMods;
383             return True;
384         }
385     }
386     if (!ExprResolveVModMask(value, &rtrn, xkb))
387         return ReportMismatch(action, F_Modifiers, "modifier mask");
388     *mods_rtrn = rtrn.uval;
389     *flags_inout &= ~XkbSA_UseModMapMods;
390     return True;
391 }
392
393 static Bool
394 HandleSetLatchMods(struct xkb_desc * xkb,
395                    struct xkb_any_action * action,
396                    unsigned field, ExprDef * array_ndx, ExprDef * value)
397 {
398     struct xkb_mod_action *act;
399     unsigned rtrn;
400     unsigned t1, t2;
401
402     act = (struct xkb_mod_action *) action;
403     if (array_ndx != NULL)
404     {
405         switch (field)
406         {
407         case F_ClearLocks:
408         case F_LatchToLock:
409         case F_Modifiers:
410             return ReportActionNotArray(action->type, field);
411         }
412     }
413     switch (field)
414     {
415     case F_ClearLocks:
416     case F_LatchToLock:
417         rtrn = act->flags;
418         if (CheckLatchLockFlags(action->type, field, value, &rtrn))
419         {
420             act->flags = rtrn;
421             return True;
422         }
423         return False;
424     case F_Modifiers:
425         t1 = act->flags;
426         if (CheckModifierField(xkb, action->type, value, &t1, &t2))
427         {
428             act->flags = t1;
429             act->real_mods = act->mask = (t2 & 0xff);
430             act->vmods = (t2 >> 8) & 0xffff;
431             return True;
432         }
433         return False;
434     }
435     return ReportIllegal(action->type, field);
436 }
437
438 static Bool
439 HandleLockMods(struct xkb_desc * xkb,
440                struct xkb_any_action * action,
441                unsigned field, ExprDef * array_ndx, ExprDef * value)
442 {
443     struct xkb_mod_action *act;
444     unsigned t1, t2;
445
446     act = (struct xkb_mod_action *) action;
447     if ((array_ndx != NULL) && (field == F_Modifiers))
448         return ReportActionNotArray(action->type, field);
449     switch (field)
450     {
451     case F_Modifiers:
452         t1 = act->flags;
453         if (CheckModifierField(xkb, action->type, value, &t1, &t2))
454         {
455             act->flags = t1;
456             act->real_mods = act->mask = (t2 & 0xff);
457             act->vmods = (t2 >> 8) & 0xffff;
458             return True;
459         }
460         return False;
461     }
462     return ReportIllegal(action->type, field);
463 }
464
465 static LookupEntry groupNames[] = {
466     {"group1", 1},
467     {"group2", 2},
468     {"group3", 3},
469     {"group4", 4},
470     {"group5", 5},
471     {"group6", 6},
472     {"group7", 7},
473     {"group8", 8},
474     {NULL, 0},
475 };
476
477 static Bool
478 CheckGroupField(unsigned action,
479                 ExprDef * value, unsigned *flags_inout, int *grp_rtrn)
480 {
481     ExprDef *spec;
482     ExprResult rtrn;
483
484     if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
485     {
486         *flags_inout &= ~XkbSA_GroupAbsolute;
487         spec = value->value.child;
488     }
489     else
490     {
491         *flags_inout |= XkbSA_GroupAbsolute;
492         spec = value;
493     }
494
495     if (!ExprResolveInteger(spec, &rtrn, SimpleLookup, (char *) groupNames))
496         return ReportMismatch(action, F_Group, "integer (range 1..8)");
497     if ((rtrn.ival < 1) || (rtrn.ival > XkbNumKbdGroups))
498     {
499         ERROR("Illegal group %d (must be in the range 1..%d)\n", rtrn.ival,
500                XkbNumKbdGroups);
501         ACTION("Action %s definition ignored\n", XkbcActionTypeText(action));
502         return False;
503     }
504     if (value->op == OpNegate)
505         *grp_rtrn = -rtrn.ival;
506     else if (value->op == OpUnaryPlus)
507         *grp_rtrn = rtrn.ival;
508     else
509         *grp_rtrn = rtrn.ival - 1;
510     return True;
511 }
512
513 static Bool
514 HandleSetLatchGroup(struct xkb_desc * xkb,
515                     struct xkb_any_action * action,
516                     unsigned field, ExprDef * array_ndx, ExprDef * value)
517 {
518     struct xkb_group_action *act;
519     unsigned rtrn;
520     unsigned t1;
521     int t2;
522
523     act = (struct xkb_group_action *) action;
524     if (array_ndx != NULL)
525     {
526         switch (field)
527         {
528         case F_ClearLocks:
529         case F_LatchToLock:
530         case F_Group:
531             return ReportActionNotArray(action->type, field);
532         }
533     }
534     switch (field)
535     {
536     case F_ClearLocks:
537     case F_LatchToLock:
538         rtrn = act->flags;
539         if (CheckLatchLockFlags(action->type, field, value, &rtrn))
540         {
541             act->flags = rtrn;
542             return True;
543         }
544         return False;
545     case F_Group:
546         t1 = act->flags;
547         if (CheckGroupField(action->type, value, &t1, &t2))
548         {
549             act->flags = t1;
550             act->group = t2;
551             return True;
552         }
553         return False;
554     }
555     return ReportIllegal(action->type, field);
556 }
557
558 static Bool
559 HandleLockGroup(struct xkb_desc * xkb,
560                 struct xkb_any_action * action,
561                 unsigned field, ExprDef * array_ndx, ExprDef * value)
562 {
563     struct xkb_group_action *act;
564     unsigned t1;
565     int t2;
566
567     act = (struct xkb_group_action *) action;
568     if ((array_ndx != NULL) && (field == F_Group))
569         return ReportActionNotArray(action->type, field);
570     if (field == F_Group)
571     {
572         t1 = act->flags;
573         if (CheckGroupField(action->type, value, &t1, &t2))
574         {
575             act->flags = t1;
576             act->group = t2;
577             return True;
578         }
579         return False;
580     }
581     return ReportIllegal(action->type, field);
582 }
583
584 static Bool
585 HandleMovePtr(struct xkb_desc * xkb,
586               struct xkb_any_action * action,
587               unsigned field, ExprDef * array_ndx, ExprDef * value)
588 {
589     ExprResult rtrn;
590     struct xkb_pointer_action *act;
591     Bool absolute;
592
593     act = (struct xkb_pointer_action *) action;
594     if ((array_ndx != NULL) && ((field == F_X) || (field == F_Y)))
595         return ReportActionNotArray(action->type, field);
596
597     if ((field == F_X) || (field == F_Y))
598     {
599         if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
600             absolute = False;
601         else
602             absolute = True;
603         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
604             return ReportMismatch(action->type, field, "integer");
605         if (field == F_X)
606         {
607             if (absolute)
608                 act->flags |= XkbSA_MoveAbsoluteX;
609             act->x = rtrn.ival;
610         }
611         else
612         {
613             if (absolute)
614                 act->flags |= XkbSA_MoveAbsoluteY;
615             act->y = rtrn.ival;
616         }
617         return True;
618     }
619     else if (field == F_Accel)
620     {
621         if (!ExprResolveBoolean(value, &rtrn))
622             return ReportMismatch(action->type, field, "boolean");
623         if (rtrn.uval)
624             act->flags &= ~XkbSA_NoAcceleration;
625         else
626             act->flags |= XkbSA_NoAcceleration;
627     }
628     return ReportIllegal(action->type, field);
629 }
630
631 static LookupEntry lockWhich[] = {
632     {"both", 0},
633     {"lock", XkbSA_LockNoUnlock},
634     {"neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock)},
635     {"unlock", XkbSA_LockNoLock},
636     {NULL, 0}
637 };
638
639 static Bool
640 HandlePtrBtn(struct xkb_desc * xkb,
641              struct xkb_any_action * action,
642              unsigned field, ExprDef * array_ndx, ExprDef * value)
643 {
644     ExprResult rtrn;
645     struct xkb_pointer_button_action *act;
646
647     act = (struct xkb_pointer_button_action *) action;
648     if (field == F_Button)
649     {
650         if (array_ndx != NULL)
651             return ReportActionNotArray(action->type, field);
652         if (!ExprResolveButton(value, &rtrn))
653             return ReportMismatch(action->type, field,
654                                   "integer (range 1..5)");
655         if ((rtrn.ival < 0) || (rtrn.ival > 5))
656         {
657             ERROR("Button must specify default or be in the range 1..5\n");
658             ACTION("Illegal button value %d ignored\n", rtrn.ival);
659             return False;
660         }
661         act->button = rtrn.ival;
662         return True;
663     }
664     else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect))
665     {
666         if (array_ndx != NULL)
667             return ReportActionNotArray(action->type, field);
668         if (!ExprResolveEnum(value, &rtrn, lockWhich))
669             return ReportMismatch(action->type, field, "lock or unlock");
670         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
671         act->flags |= rtrn.ival;
672         return True;
673     }
674     else if (field == F_Count)
675     {
676         if (array_ndx != NULL)
677             return ReportActionNotArray(action->type, field);
678         if (!ExprResolveButton(value, &rtrn))
679             return ReportMismatch(action->type, field, "integer");
680         if ((rtrn.ival < 0) || (rtrn.ival > 255))
681         {
682             ERROR("The count field must have a value in the range 0..255\n");
683             ACTION("Illegal count %d ignored\n", rtrn.ival);
684             return False;
685         }
686         act->count = rtrn.ival;
687         return True;
688     }
689     return ReportIllegal(action->type, field);
690 }
691
692 static LookupEntry ptrDflts[] = {
693     {"dfltbtn", XkbSA_AffectDfltBtn},
694     {"defaultbutton", XkbSA_AffectDfltBtn},
695     {"button", XkbSA_AffectDfltBtn},
696     {NULL, 0}
697 };
698
699 static Bool
700 HandleSetPtrDflt(struct xkb_desc * xkb,
701                  struct xkb_any_action * action,
702                  unsigned field, ExprDef * array_ndx, ExprDef * value)
703 {
704     ExprResult rtrn;
705     struct xkb_pointer_default_action *act;
706
707     act = (struct xkb_pointer_default_action *) action;
708     if (field == F_Affect)
709     {
710         if (array_ndx != NULL)
711             return ReportActionNotArray(action->type, field);
712         if (!ExprResolveEnum(value, &rtrn, ptrDflts))
713             return ReportMismatch(action->type, field, "pointer component");
714         act->affect = rtrn.uval;
715         return True;
716     }
717     else if ((field == F_Button) || (field == F_Value))
718     {
719         ExprDef *btn;
720         if (array_ndx != NULL)
721             return ReportActionNotArray(action->type, field);
722         if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
723         {
724             act->flags &= ~XkbSA_DfltBtnAbsolute;
725             btn = value->value.child;
726         }
727         else
728         {
729             act->flags |= XkbSA_DfltBtnAbsolute;
730             btn = value;
731         }
732
733         if (!ExprResolveButton(btn, &rtrn))
734             return ReportMismatch(action->type, field,
735                                   "integer (range 1..5)");
736         if ((rtrn.ival < 0) || (rtrn.ival > 5))
737         {
738             ERROR("New default button value must be in the range 1..5\n");
739             ACTION("Illegal default button value %d ignored\n", rtrn.ival);
740             return False;
741         }
742         if (rtrn.ival == 0)
743         {
744             ERROR("Cannot set default pointer button to \"default\"\n");
745             ACTION("Illegal default button setting ignored\n");
746             return False;
747         }
748         if (value->op == OpNegate)
749             act->value = -rtrn.ival;
750         else
751             act->value = rtrn.ival;
752         return True;
753     }
754     return ReportIllegal(action->type, field);
755 }
756
757 static LookupEntry isoNames[] = {
758     {"mods", XkbSA_ISONoAffectMods},
759     {"modifiers", XkbSA_ISONoAffectMods},
760     {"group", XkbSA_ISONoAffectGroup},
761     {"groups", XkbSA_ISONoAffectGroup},
762     {"ptr", XkbSA_ISONoAffectPtr},
763     {"pointer", XkbSA_ISONoAffectPtr},
764     {"ctrls", XkbSA_ISONoAffectCtrls},
765     {"controls", XkbSA_ISONoAffectCtrls},
766     {"all", ~((unsigned) 0)},
767     {"none", 0},
768     {NULL, 0},
769 };
770
771 static Bool
772 HandleISOLock(struct xkb_desc * xkb,
773               struct xkb_any_action * action,
774               unsigned field, ExprDef * array_ndx, ExprDef * value)
775 {
776     ExprResult rtrn;
777     struct xkb_iso_action *act;
778     unsigned flags, mods;
779     int group;
780
781     act = (struct xkb_iso_action *) action;
782     switch (field)
783     {
784     case F_Modifiers:
785         if (array_ndx != NULL)
786             return ReportActionNotArray(action->type, field);
787         flags = act->flags;
788         if (CheckModifierField(xkb, action->type, value, &flags, &mods))
789         {
790             act->flags = flags & (~XkbSA_ISODfltIsGroup);
791             act->real_mods = mods & 0xff;
792             act->vmods = (mods >> 8) & 0xff;
793             return True;
794         }
795         return False;
796     case F_Group:
797         if (array_ndx != NULL)
798             return ReportActionNotArray(action->type, field);
799         flags = act->flags;
800         if (CheckGroupField(action->type, value, &flags, &group))
801         {
802             act->flags = flags | XkbSA_ISODfltIsGroup;
803             act->group = group;
804             return True;
805         }
806         return False;
807     case F_Affect:
808         if (array_ndx != NULL)
809             return ReportActionNotArray(action->type, field);
810         if (!ExprResolveMask(value, &rtrn, SimpleLookup, (char *) isoNames))
811             return ReportMismatch(action->type, field, "keyboard component");
812         act->affect = (~rtrn.uval) & XkbSA_ISOAffectMask;
813         return True;
814     }
815     return ReportIllegal(action->type, field);
816 }
817
818 static Bool
819 HandleSwitchScreen(struct xkb_desc * xkb,
820                    struct xkb_any_action * action,
821                    unsigned field, ExprDef * array_ndx, ExprDef * value)
822 {
823     ExprResult rtrn;
824     struct xkb_switch_screen_action *act;
825
826     act = (struct xkb_switch_screen_action *) action;
827     if (field == F_Screen)
828     {
829         ExprDef *scrn;
830         if (array_ndx != NULL)
831             return ReportActionNotArray(action->type, field);
832         if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
833         {
834             act->flags &= ~XkbSA_SwitchAbsolute;
835             scrn = value->value.child;
836         }
837         else
838         {
839             act->flags |= XkbSA_SwitchAbsolute;
840             scrn = value;
841         }
842
843         if (!ExprResolveInteger(scrn, &rtrn, NULL, NULL))
844             return ReportMismatch(action->type, field, "integer (0..255)");
845         if ((rtrn.ival < 0) || (rtrn.ival > 255))
846         {
847             ERROR("Screen index must be in the range 1..255\n");
848             ACTION("Illegal screen value %d ignored\n", rtrn.ival);
849             return False;
850         }
851         if (value->op == OpNegate)
852             act->screen = -rtrn.ival;
853         else
854             act->screen = rtrn.ival;
855         return True;
856     }
857     else if (field == F_Same)
858     {
859         if (array_ndx != NULL)
860             return ReportActionNotArray(action->type, field);
861         if (!ExprResolveBoolean(value, &rtrn))
862             return ReportMismatch(action->type, field, "boolean");
863         if (rtrn.uval)
864             act->flags &= ~XkbSA_SwitchApplication;
865         else
866             act->flags |= XkbSA_SwitchApplication;
867         return True;
868     }
869     return ReportIllegal(action->type, field);
870 }
871
872 LookupEntry ctrlNames[] = {
873     {"repeatkeys", XkbRepeatKeysMask}
874     ,
875     {"repeat", XkbRepeatKeysMask}
876     ,
877     {"autorepeat", XkbRepeatKeysMask}
878     ,
879     {"slowkeys", XkbSlowKeysMask}
880     ,
881     {"bouncekeys", XkbBounceKeysMask}
882     ,
883     {"stickykeys", XkbStickyKeysMask}
884     ,
885     {"mousekeys", XkbMouseKeysMask}
886     ,
887     {"mousekeysaccel", XkbMouseKeysAccelMask}
888     ,
889     {"accessxkeys", XkbAccessXKeysMask}
890     ,
891     {"accessxtimeout", XkbAccessXTimeoutMask}
892     ,
893     {"accessxfeedback", XkbAccessXFeedbackMask}
894     ,
895     {"audiblebell", XkbAudibleBellMask}
896     ,
897     {"overlay1", XkbOverlay1Mask}
898     ,
899     {"overlay2", XkbOverlay2Mask}
900     ,
901     {"ignoregrouplock", XkbIgnoreGroupLockMask}
902     ,
903     {"all", XkbAllBooleanCtrlsMask}
904     ,
905     {"none", 0}
906     ,
907     {NULL, 0}
908 };
909
910 static Bool
911 HandleSetLockControls(struct xkb_desc * xkb,
912                       struct xkb_any_action * action,
913                       unsigned field, ExprDef * array_ndx, ExprDef * value)
914 {
915     ExprResult rtrn;
916     struct xkb_controls_action *act;
917
918     act = (struct xkb_controls_action *) action;
919     if (field == F_Controls)
920     {
921         if (array_ndx != NULL)
922             return ReportActionNotArray(action->type, field);
923         if (!ExprResolveMask
924             (value, &rtrn, SimpleLookup, (char *) ctrlNames))
925             return ReportMismatch(action->type, field, "controls mask");
926         act->ctrls = rtrn.uval;
927         return True;
928     }
929     return ReportIllegal(action->type, field);
930 }
931
932 static LookupEntry evNames[] = {
933     {"press", XkbSA_MessageOnPress},
934     {"keypress", XkbSA_MessageOnPress},
935     {"release", XkbSA_MessageOnRelease},
936     {"keyrelease", XkbSA_MessageOnRelease},
937     {"all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease},
938     {"none", 0},
939     {NULL, 0}
940 };
941
942 static Bool
943 HandleActionMessage(struct xkb_desc * xkb,
944                     struct xkb_any_action * action,
945                     unsigned field, ExprDef * array_ndx, ExprDef * value)
946 {
947     ExprResult rtrn;
948     struct xkb_message_action *act;
949
950     act = (struct xkb_message_action *) action;
951     switch (field)
952     {
953     case F_Report:
954         if (array_ndx != NULL)
955             return ReportActionNotArray(action->type, field);
956         if (!ExprResolveMask(value, &rtrn, SimpleLookup, (char *) evNames))
957             return ReportMismatch(action->type, field, "key event mask");
958         act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
959         act->flags =
960             rtrn.uval & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
961         return True;
962     case F_GenKeyEvent:
963         if (array_ndx != NULL)
964             return ReportActionNotArray(action->type, field);
965         if (!ExprResolveBoolean(value, &rtrn))
966             return ReportMismatch(action->type, field, "boolean");
967         if (rtrn.uval)
968             act->flags |= XkbSA_MessageGenKeyEvent;
969         else
970             act->flags &= ~XkbSA_MessageGenKeyEvent;
971         return True;
972     case F_Data:
973         if (array_ndx == NULL)
974         {
975             if (!ExprResolveString(value, &rtrn))
976                 return ReportMismatch(action->type, field, "string");
977             else
978             {
979                 int len = strlen(rtrn.str);
980                 if ((len < 1) || (len > 6))
981                 {
982                     WARN("An action message can hold only 6 bytes\n");
983                     ACTION("Extra %d bytes ignored\n", len - 6);
984                 }
985                 strncpy((char *) act->message, rtrn.str, 6);
986             }
987             return True;
988         }
989         else
990         {
991             unsigned ndx;
992             if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
993             {
994                 ERROR("Array subscript must be integer\n");
995                 ACTION("Illegal subscript ignored\n");
996                 return False;
997             }
998             ndx = rtrn.uval;
999             if (ndx > 5)
1000             {
1001                 ERROR("An action message is at most 6 bytes long\n");
1002                 ACTION("Attempt to use data[%d] ignored\n", ndx);
1003                 return False;
1004             }
1005             if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1006                 return ReportMismatch(action->type, field, "integer");
1007             if ((rtrn.ival < 0) || (rtrn.ival > 255))
1008             {
1009                 ERROR("Message data must be in the range 0..255\n");
1010                 ACTION("Illegal datum %d ignored\n", rtrn.ival);
1011                 return False;
1012             }
1013             act->message[ndx] = rtrn.uval;
1014         }
1015         return True;
1016     }
1017     return ReportIllegal(action->type, field);
1018 }
1019
1020 static Bool
1021 HandleRedirectKey(struct xkb_desc * xkb,
1022                   struct xkb_any_action * action,
1023                   unsigned field, ExprDef * array_ndx, ExprDef * value)
1024 {
1025     ExprResult rtrn;
1026     struct xkb_redirect_key_action *act;
1027     unsigned t1, t2;
1028     xkb_keycode_t kc;
1029     unsigned long tmp;
1030
1031     if (array_ndx != NULL)
1032         return ReportActionNotArray(action->type, field);
1033
1034     act = (struct xkb_redirect_key_action *) action;
1035     switch (field)
1036     {
1037     case F_Keycode:
1038         if (!ExprResolveKeyName(value, &rtrn))
1039             return ReportMismatch(action->type, field, "key name");
1040         tmp = KeyNameToLong(rtrn.keyName.name);
1041         if (!FindNamedKey(xkb, tmp, &kc, True, CreateKeyNames(xkb), 0))
1042         {
1043             return ReportNotFound(action->type, field, "Key",
1044                                   XkbcKeyNameText(rtrn.keyName.name));
1045         }
1046         act->new_key = kc;
1047         return True;
1048     case F_ModsToClear:
1049     case F_Modifiers:
1050         t1 = 0;
1051         if (CheckModifierField(xkb, action->type, value, &t1, &t2))
1052         {
1053             act->mods_mask |= (t2 & 0xff);
1054             if (field == F_Modifiers)
1055                 act->mods |= (t2 & 0xff);
1056             else
1057                 act->mods &= ~(t2 & 0xff);
1058
1059             t2 = (t2 >> 8) & 0xffff;
1060             act->vmods_mask |= t2;
1061             if (field == F_Modifiers)
1062                 act->vmods |= t2;
1063             else
1064                 act->vmods &= ~t2;
1065             return True;
1066         }
1067         return True;
1068     }
1069     return ReportIllegal(action->type, field);
1070 }
1071
1072 static Bool
1073 HandleDeviceBtn(struct xkb_desc * xkb,
1074                 struct xkb_any_action * action,
1075                 unsigned field, ExprDef * array_ndx, ExprDef * value)
1076 {
1077     ExprResult rtrn;
1078     struct xkb_device_button_action *act;
1079
1080     act = (struct xkb_device_button_action *) action;
1081     if (field == F_Button)
1082     {
1083         if (array_ndx != NULL)
1084             return ReportActionNotArray(action->type, field);
1085         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1086             return ReportMismatch(action->type, field,
1087                                   "integer (range 1..255)");
1088         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1089         {
1090             ERROR("Button must specify default or be in the range 1..255\n");
1091             ACTION("Illegal button value %d ignored\n", rtrn.ival);
1092             return False;
1093         }
1094         act->button = rtrn.ival;
1095         return True;
1096     }
1097     else if ((action->type == XkbSA_LockDeviceBtn) && (field == F_Affect))
1098     {
1099         if (array_ndx != NULL)
1100             return ReportActionNotArray(action->type, field);
1101         if (!ExprResolveEnum(value, &rtrn, lockWhich))
1102             return ReportMismatch(action->type, field, "lock or unlock");
1103         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
1104         act->flags |= rtrn.ival;
1105         return True;
1106     }
1107     else if (field == F_Count)
1108     {
1109         if (array_ndx != NULL)
1110             return ReportActionNotArray(action->type, field);
1111         if (!ExprResolveButton(value, &rtrn))
1112             return ReportMismatch(action->type, field, "integer");
1113         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1114         {
1115             ERROR("The count field must have a value in the range 0..255\n");
1116             ACTION("Illegal count %d ignored\n", rtrn.ival);
1117             return False;
1118         }
1119         act->count = rtrn.ival;
1120         return True;
1121     }
1122     else if (field == F_Device)
1123     {
1124         if (array_ndx != NULL)
1125             return ReportActionNotArray(action->type, field);
1126         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1127             return ReportMismatch(action->type, field,
1128                                   "integer (range 1..255)");
1129         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1130         {
1131             ERROR("Device must specify default or be in the range 1..255\n");
1132             ACTION("Illegal device value %d ignored\n", rtrn.ival);
1133             return False;
1134         }
1135         act->device = rtrn.ival;
1136         return True;
1137     }
1138     return ReportIllegal(action->type, field);
1139 }
1140
1141 static Bool
1142 HandleDeviceValuator(struct xkb_desc * xkb,
1143                      struct xkb_any_action * action,
1144                      unsigned field, ExprDef * array_ndx, ExprDef * value)
1145 {
1146 #if 0
1147     ExprResult rtrn;
1148     struct xkb_device_valuator_action *act;
1149
1150     act = (struct xkb_device_valuator_action *) action;
1151     /*  XXX - Not yet implemented */
1152 #endif
1153     return False;
1154 }
1155
1156 static Bool
1157 HandlePrivate(struct xkb_desc * xkb,
1158               struct xkb_any_action * action,
1159               unsigned field, ExprDef * array_ndx, ExprDef * value)
1160 {
1161     ExprResult rtrn;
1162
1163     switch (field)
1164     {
1165     case F_Type:
1166         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1167             return ReportMismatch(PrivateAction, field, "integer");
1168         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1169         {
1170             ERROR("Private action type must be in the range 0..255\n");
1171             ACTION("Illegal type %d ignored\n", rtrn.ival);
1172             return False;
1173         }
1174         action->type = rtrn.uval;
1175         return True;
1176     case F_Data:
1177         if (array_ndx == NULL)
1178         {
1179             if (!ExprResolveString(value, &rtrn))
1180                 return ReportMismatch(action->type, field, "string");
1181             else
1182             {
1183                 int len = strlen(rtrn.str);
1184                 if ((len < 1) || (len > 7))
1185                 {
1186                     WARN("A private action has 7 data bytes\n");
1187                     ACTION("Extra %d bytes ignored\n", len - 6);
1188                     return False;
1189                 }
1190                 strncpy((char *) action->data, rtrn.str, sizeof action->data);
1191             }
1192             free(rtrn.str);
1193             return True;
1194         }
1195         else
1196         {
1197             unsigned ndx;
1198             if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
1199             {
1200                 ERROR("Array subscript must be integer\n");
1201                 ACTION("Illegal subscript ignored\n");
1202                 return False;
1203             }
1204             ndx = rtrn.uval;
1205             if (ndx >= sizeof action->data)
1206             {
1207                 ERROR("The data for a private action is 18 bytes long\n");
1208                 ACTION("Attempt to use data[%d] ignored\n", ndx);
1209                 return False;
1210             }
1211             if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1212                 return ReportMismatch(action->type, field, "integer");
1213             if ((rtrn.ival < 0) || (rtrn.ival > 255))
1214             {
1215                 ERROR("All data for a private action must be 0..255\n");
1216                 ACTION("Illegal datum %d ignored\n", rtrn.ival);
1217                 return False;
1218             }
1219             action->data[ndx] = rtrn.uval;
1220             return True;
1221         }
1222     }
1223     return ReportIllegal(PrivateAction, field);
1224 }
1225
1226 typedef Bool(*actionHandler) (struct xkb_desc * /* xkb */ ,
1227                               struct xkb_any_action * /* action */ ,
1228                               unsigned /* field */ ,
1229                               ExprDef * /* array_ndx */ ,
1230                               ExprDef * /* value */
1231     );
1232
1233 static actionHandler handleAction[XkbSA_NumActions + 1] = {
1234     HandleNoAction /* NoAction     */ ,
1235     HandleSetLatchMods /* SetMods      */ ,
1236     HandleSetLatchMods /* LatchMods    */ ,
1237     HandleLockMods /* LockMods     */ ,
1238     HandleSetLatchGroup /* SetGroup     */ ,
1239     HandleSetLatchGroup /* LatchGroup   */ ,
1240     HandleLockGroup /* LockGroup    */ ,
1241     HandleMovePtr /* MovePtr      */ ,
1242     HandlePtrBtn /* PtrBtn       */ ,
1243     HandlePtrBtn /* LockPtrBtn   */ ,
1244     HandleSetPtrDflt /* SetPtrDflt   */ ,
1245     HandleISOLock /* ISOLock      */ ,
1246     HandleNoAction /* Terminate    */ ,
1247     HandleSwitchScreen /* SwitchScreen */ ,
1248     HandleSetLockControls /* SetControls  */ ,
1249     HandleSetLockControls /* LockControls */ ,
1250     HandleActionMessage /* ActionMessage */ ,
1251     HandleRedirectKey /* RedirectKey  */ ,
1252     HandleDeviceBtn /* DeviceBtn    */ ,
1253     HandleDeviceBtn /* LockDeviceBtn */ ,
1254     HandleDeviceValuator /* DeviceValuatr */ ,
1255     HandlePrivate               /* Private      */
1256 };
1257
1258 /***====================================================================***/
1259
1260 static void
1261 ApplyActionFactoryDefaults(union xkb_action * action)
1262 {
1263     if (action->type == XkbSA_SetPtrDflt)
1264     {                           /* increment default button */
1265         action->dflt.affect = XkbSA_AffectDfltBtn;
1266         action->dflt.flags = 0;
1267         action->dflt.value = 1;
1268     }
1269     else if (action->type == XkbSA_ISOLock)
1270     {
1271         action->iso.real_mods = LockMask;
1272     }
1273     return;
1274 }
1275
1276 static void
1277 ActionsInit(void);
1278
1279 int
1280 HandleActionDef(ExprDef * def,
1281                 struct xkb_desc * xkb,
1282                 struct xkb_any_action * action, unsigned mergeMode, ActionInfo * info)
1283 {
1284     ExprDef *arg;
1285     const char *str;
1286     unsigned tmp, hndlrType;
1287
1288     if (!actionsInitialized)
1289         ActionsInit();
1290
1291     if (def->op != ExprActionDecl)
1292     {
1293         ERROR("Expected an action definition, found %s\n",
1294                exprOpText(def->op));
1295         return False;
1296     }
1297     str = XkbcAtomText(def->value.action.name);
1298     if (!str)
1299     {
1300         WSGO("Missing name in action definition!!\n");
1301         return False;
1302     }
1303     if (!stringToAction(str, &tmp))
1304     {
1305         ERROR("Unknown action %s\n", str);
1306         return False;
1307     }
1308     action->type = hndlrType = tmp;
1309     if (action->type != XkbSA_NoAction)
1310     {
1311         ApplyActionFactoryDefaults((union xkb_action *) action);
1312         while (info)
1313         {
1314             if ((info->action == XkbSA_NoAction)
1315                 || (info->action == hndlrType))
1316             {
1317                 if (!(*handleAction[hndlrType]) (xkb, action,
1318                                                  info->field,
1319                                                  info->array_ndx,
1320                                                  info->value))
1321                 {
1322                     return False;
1323                 }
1324             }
1325             info = info->next;
1326         }
1327     }
1328     for (arg = def->value.action.args; arg != NULL;
1329          arg = (ExprDef *) arg->common.next)
1330     {
1331         ExprDef *field, *value, *arrayRtrn;
1332         ExprResult elemRtrn, fieldRtrn;
1333         unsigned fieldNdx;
1334
1335         if (arg->op == OpAssign)
1336         {
1337             field = arg->value.binary.left;
1338             value = arg->value.binary.right;
1339         }
1340         else
1341         {
1342             if ((arg->op == OpNot) || (arg->op == OpInvert))
1343             {
1344                 field = arg->value.child;
1345                 value = &constFalse;
1346             }
1347             else
1348             {
1349                 field = arg;
1350                 value = &constTrue;
1351             }
1352         }
1353         if (!ExprResolveLhs(field, &elemRtrn, &fieldRtrn, &arrayRtrn))
1354             return False;       /* internal error -- already reported */
1355
1356         if (elemRtrn.str != NULL)
1357         {
1358             ERROR("Cannot change defaults in an action definition\n");
1359             ACTION("Ignoring attempt to change %s.%s\n", elemRtrn.str,
1360                     fieldRtrn.str);
1361             free(elemRtrn.str);
1362             free(fieldRtrn.str);
1363             return False;
1364         }
1365         if (!stringToField(fieldRtrn.str, &fieldNdx))
1366         {
1367             ERROR("Unknown field name %s\n", uStringText(fieldRtrn.str));
1368             free(elemRtrn.str);
1369             free(fieldRtrn.str);
1370             return False;
1371         }
1372         free(elemRtrn.str);
1373         free(fieldRtrn.str);
1374         if (!(*handleAction[hndlrType])
1375             (xkb, action, fieldNdx, arrayRtrn, value))
1376         {
1377             return False;
1378         }
1379     }
1380     return True;
1381 }
1382
1383 /***====================================================================***/
1384
1385 int
1386 SetActionField(struct xkb_desc * xkb,
1387                char *elem,
1388                char *field,
1389                ExprDef * array_ndx, ExprDef * value, ActionInfo ** info_rtrn)
1390 {
1391     ActionInfo *new, *old;
1392
1393     if (!actionsInitialized)
1394         ActionsInit();
1395
1396     new = uTypedAlloc(ActionInfo);
1397     if (new == NULL)
1398     {
1399         WSGO("Couldn't allocate space for action default\n");
1400         return False;
1401     }
1402     if (uStrCaseCmp(elem, "action") == 0)
1403         new->action = XkbSA_NoAction;
1404     else
1405     {
1406         if (!stringToAction(elem, &new->action))
1407         {
1408             free(new);
1409             return False;
1410         }
1411         if (new->action == XkbSA_NoAction)
1412         {
1413             ERROR("\"%s\" is not a valid field in a NoAction action\n",
1414                    field);
1415             free(new);
1416             return False;
1417         }
1418     }
1419     if (!stringToField(field, &new->field))
1420     {
1421         ERROR("\"%s\" is not a legal field name\n", field);
1422         free(new);
1423         return False;
1424     }
1425     new->array_ndx = array_ndx;
1426     new->value = value;
1427     new->next = NULL;
1428     old = *info_rtrn;
1429     while ((old) && (old->next))
1430         old = old->next;
1431     if (old == NULL)
1432         *info_rtrn = new;
1433     else
1434         old->next = new;
1435     return True;
1436 }
1437
1438 /***====================================================================***/
1439
1440 static void
1441 ActionsInit(void)
1442 {
1443     if (!actionsInitialized)
1444     {
1445         bzero((char *) &constTrue, sizeof(constTrue));
1446         bzero((char *) &constFalse, sizeof(constFalse));
1447         constTrue.common.stmtType = StmtExpr;
1448         constTrue.common.next = NULL;
1449         constTrue.op = ExprIdent;
1450         constTrue.type = TypeBoolean;
1451         constTrue.value.str = xkb_intern_atom("true");
1452         constFalse.common.stmtType = StmtExpr;
1453         constFalse.common.next = NULL;
1454         constFalse.op = ExprIdent;
1455         constFalse.type = TypeBoolean;
1456         constFalse.value.str = xkb_intern_atom("false");
1457         actionsInitialized = 1;
1458     }
1459     return;
1460 }