xkbcomp: Don't leak atom text and string exprs
[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(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(XkbcDescPtr xkb,
337                XkbAnyAction * 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, NULL, NULL))
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(XkbcDescPtr 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         register 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 (!ExprResolveModMask(value, &rtrn, LookupVModMask, (char *) 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(XkbcDescPtr xkb,
395                    XkbAnyAction * action,
396                    unsigned field, ExprDef * array_ndx, ExprDef * value)
397 {
398     XkbcModAction *act;
399     unsigned rtrn;
400     unsigned t1, t2;
401
402     act = (XkbcModAction *) 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(XkbcDescPtr xkb,
440                XkbAnyAction * action,
441                unsigned field, ExprDef * array_ndx, ExprDef * value)
442 {
443     XkbcModAction *act;
444     unsigned t1, t2;
445
446     act = (XkbcModAction *) 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(XkbcDescPtr xkb,
515                     XkbAnyAction * action,
516                     unsigned field, ExprDef * array_ndx, ExprDef * value)
517 {
518     XkbGroupAction *act;
519     unsigned rtrn;
520     unsigned t1;
521     int t2;
522
523     act = (XkbGroupAction *) 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             XkbSASetGroup(act, t2);
551             return True;
552         }
553         return False;
554     }
555     return ReportIllegal(action->type, field);
556 }
557
558 static Bool
559 HandleLockGroup(XkbcDescPtr xkb,
560                 XkbAnyAction * action,
561                 unsigned field, ExprDef * array_ndx, ExprDef * value)
562 {
563     XkbGroupAction *act;
564     unsigned t1;
565     int t2;
566
567     act = (XkbGroupAction *) 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             XkbSASetGroup(act, t2);
577             return True;
578         }
579         return False;
580     }
581     return ReportIllegal(action->type, field);
582 }
583
584 static Bool
585 HandleMovePtr(XkbcDescPtr xkb,
586               XkbAnyAction * action,
587               unsigned field, ExprDef * array_ndx, ExprDef * value)
588 {
589     ExprResult rtrn;
590     XkbcPtrAction *act;
591     Bool absolute;
592
593     act = (XkbcPtrAction *) 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, NULL, NULL))
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 btnNames[] = {
632     {"button1", 1},
633     {"button2", 2},
634     {"button3", 3},
635     {"button4", 4},
636     {"button5", 5},
637     {"default", 0},
638     {NULL, 0}
639 };
640
641 static LookupEntry lockWhich[] = {
642     {"both", 0},
643     {"lock", XkbSA_LockNoUnlock},
644     {"neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock)},
645     {"unlock", XkbSA_LockNoLock},
646     {NULL, 0}
647 };
648
649 static Bool
650 HandlePtrBtn(XkbcDescPtr xkb,
651              XkbAnyAction * action,
652              unsigned field, ExprDef * array_ndx, ExprDef * value)
653 {
654     ExprResult rtrn;
655     XkbPtrBtnAction *act;
656
657     act = (XkbPtrBtnAction *) action;
658     if (field == F_Button)
659     {
660         if (array_ndx != NULL)
661             return ReportActionNotArray(action->type, field);
662         if (!ExprResolveInteger
663             (value, &rtrn, SimpleLookup, (char *) btnNames))
664             return ReportMismatch(action->type, field,
665                                   "integer (range 1..5)");
666         if ((rtrn.ival < 0) || (rtrn.ival > 5))
667         {
668             ERROR("Button must specify default or be in the range 1..5\n");
669             ACTION("Illegal button value %d ignored\n", rtrn.ival);
670             return False;
671         }
672         act->button = rtrn.ival;
673         return True;
674     }
675     else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect))
676     {
677         if (array_ndx != NULL)
678             return ReportActionNotArray(action->type, field);
679         if (!ExprResolveEnum(value, &rtrn, lockWhich))
680             return ReportMismatch(action->type, field, "lock or unlock");
681         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
682         act->flags |= rtrn.ival;
683         return True;
684     }
685     else if (field == F_Count)
686     {
687         if (array_ndx != NULL)
688             return ReportActionNotArray(action->type, field);
689         if (!ExprResolveInteger
690             (value, &rtrn, SimpleLookup, (char *) btnNames))
691             return ReportMismatch(action->type, field, "integer");
692         if ((rtrn.ival < 0) || (rtrn.ival > 255))
693         {
694             ERROR("The count field must have a value in the range 0..255\n");
695             ACTION("Illegal count %d ignored\n", rtrn.ival);
696             return False;
697         }
698         act->count = rtrn.ival;
699         return True;
700     }
701     return ReportIllegal(action->type, field);
702 }
703
704 static LookupEntry ptrDflts[] = {
705     {"dfltbtn", XkbSA_AffectDfltBtn},
706     {"defaultbutton", XkbSA_AffectDfltBtn},
707     {"button", XkbSA_AffectDfltBtn},
708     {NULL, 0}
709 };
710
711 static Bool
712 HandleSetPtrDflt(XkbcDescPtr xkb,
713                  XkbAnyAction * action,
714                  unsigned field, ExprDef * array_ndx, ExprDef * value)
715 {
716     ExprResult rtrn;
717     XkbPtrDfltAction *act;
718
719     act = (XkbPtrDfltAction *) action;
720     if (field == F_Affect)
721     {
722         if (array_ndx != NULL)
723             return ReportActionNotArray(action->type, field);
724         if (!ExprResolveEnum(value, &rtrn, ptrDflts))
725             return ReportMismatch(action->type, field, "pointer component");
726         act->affect = rtrn.uval;
727         return True;
728     }
729     else if ((field == F_Button) || (field == F_Value))
730     {
731         ExprDef *btn;
732         if (array_ndx != NULL)
733             return ReportActionNotArray(action->type, field);
734         if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
735         {
736             act->flags &= ~XkbSA_DfltBtnAbsolute;
737             btn = value->value.child;
738         }
739         else
740         {
741             act->flags |= XkbSA_DfltBtnAbsolute;
742             btn = value;
743         }
744
745         if (!ExprResolveInteger
746             (btn, &rtrn, SimpleLookup, (char *) btnNames))
747             return ReportMismatch(action->type, field,
748                                   "integer (range 1..5)");
749         if ((rtrn.ival < 0) || (rtrn.ival > 5))
750         {
751             ERROR("New default button value must be in the range 1..5\n");
752             ACTION("Illegal default button value %d ignored\n", rtrn.ival);
753             return False;
754         }
755         if (rtrn.ival == 0)
756         {
757             ERROR("Cannot set default pointer button to \"default\"\n");
758             ACTION("Illegal default button setting ignored\n");
759             return False;
760         }
761         if (value->op == OpNegate)
762             XkbSASetPtrDfltValue(act, -rtrn.ival);
763         else
764             XkbSASetPtrDfltValue(act, rtrn.ival);
765         return True;
766     }
767     return ReportIllegal(action->type, field);
768 }
769
770 static LookupEntry isoNames[] = {
771     {"mods", XkbSA_ISONoAffectMods},
772     {"modifiers", XkbSA_ISONoAffectMods},
773     {"group", XkbSA_ISONoAffectGroup},
774     {"groups", XkbSA_ISONoAffectGroup},
775     {"ptr", XkbSA_ISONoAffectPtr},
776     {"pointer", XkbSA_ISONoAffectPtr},
777     {"ctrls", XkbSA_ISONoAffectCtrls},
778     {"controls", XkbSA_ISONoAffectCtrls},
779     {"all", ~((unsigned) 0)},
780     {"none", 0},
781     {NULL, 0},
782 };
783
784 static Bool
785 HandleISOLock(XkbcDescPtr xkb,
786               XkbAnyAction * action,
787               unsigned field, ExprDef * array_ndx, ExprDef * value)
788 {
789     ExprResult rtrn;
790     XkbcISOAction *act;
791     unsigned flags, mods;
792     int group;
793
794     act = (XkbcISOAction *) action;
795     switch (field)
796     {
797     case F_Modifiers:
798         if (array_ndx != NULL)
799             return ReportActionNotArray(action->type, field);
800         flags = act->flags;
801         if (CheckModifierField(xkb, action->type, value, &flags, &mods))
802         {
803             act->flags = flags & (~XkbSA_ISODfltIsGroup);
804             act->real_mods = mods & 0xff;
805             act->vmods = (mods >> 8) & 0xff;
806             return True;
807         }
808         return False;
809     case F_Group:
810         if (array_ndx != NULL)
811             return ReportActionNotArray(action->type, field);
812         flags = act->flags;
813         if (CheckGroupField(action->type, value, &flags, &group))
814         {
815             act->flags = flags | XkbSA_ISODfltIsGroup;
816             XkbSASetGroup(act, group);
817             return True;
818         }
819         return False;
820     case F_Affect:
821         if (array_ndx != NULL)
822             return ReportActionNotArray(action->type, field);
823         if (!ExprResolveMask(value, &rtrn, SimpleLookup, (char *) isoNames))
824             return ReportMismatch(action->type, field, "keyboard component");
825         act->affect = (~rtrn.uval) & XkbSA_ISOAffectMask;
826         return True;
827     }
828     return ReportIllegal(action->type, field);
829 }
830
831 static Bool
832 HandleSwitchScreen(XkbcDescPtr xkb,
833                    XkbAnyAction * action,
834                    unsigned field, ExprDef * array_ndx, ExprDef * value)
835 {
836     ExprResult rtrn;
837     XkbSwitchScreenAction *act;
838
839     act = (XkbSwitchScreenAction *) action;
840     if (field == F_Screen)
841     {
842         ExprDef *scrn;
843         if (array_ndx != NULL)
844             return ReportActionNotArray(action->type, field);
845         if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
846         {
847             act->flags &= ~XkbSA_SwitchAbsolute;
848             scrn = value->value.child;
849         }
850         else
851         {
852             act->flags |= XkbSA_SwitchAbsolute;
853             scrn = value;
854         }
855
856         if (!ExprResolveInteger(scrn, &rtrn, NULL, NULL))
857             return ReportMismatch(action->type, field, "integer (0..255)");
858         if ((rtrn.ival < 0) || (rtrn.ival > 255))
859         {
860             ERROR("Screen index must be in the range 1..255\n");
861             ACTION("Illegal screen value %d ignored\n", rtrn.ival);
862             return False;
863         }
864         if (value->op == OpNegate)
865             XkbSASetScreen(act, -rtrn.ival);
866         else
867             XkbSASetScreen(act, rtrn.ival);
868         return True;
869     }
870     else if (field == F_Same)
871     {
872         if (array_ndx != NULL)
873             return ReportActionNotArray(action->type, field);
874         if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
875             return ReportMismatch(action->type, field, "boolean");
876         if (rtrn.uval)
877             act->flags &= ~XkbSA_SwitchApplication;
878         else
879             act->flags |= XkbSA_SwitchApplication;
880         return True;
881     }
882     return ReportIllegal(action->type, field);
883 }
884
885 LookupEntry ctrlNames[] = {
886     {"repeatkeys", XkbRepeatKeysMask}
887     ,
888     {"repeat", XkbRepeatKeysMask}
889     ,
890     {"autorepeat", XkbRepeatKeysMask}
891     ,
892     {"slowkeys", XkbSlowKeysMask}
893     ,
894     {"bouncekeys", XkbBounceKeysMask}
895     ,
896     {"stickykeys", XkbStickyKeysMask}
897     ,
898     {"mousekeys", XkbMouseKeysMask}
899     ,
900     {"mousekeysaccel", XkbMouseKeysAccelMask}
901     ,
902     {"accessxkeys", XkbAccessXKeysMask}
903     ,
904     {"accessxtimeout", XkbAccessXTimeoutMask}
905     ,
906     {"accessxfeedback", XkbAccessXFeedbackMask}
907     ,
908     {"audiblebell", XkbAudibleBellMask}
909     ,
910     {"overlay1", XkbOverlay1Mask}
911     ,
912     {"overlay2", XkbOverlay2Mask}
913     ,
914     {"ignoregrouplock", XkbIgnoreGroupLockMask}
915     ,
916     {"all", XkbAllBooleanCtrlsMask}
917     ,
918     {"none", 0}
919     ,
920     {NULL, 0}
921 };
922
923 static Bool
924 HandleSetLockControls(XkbcDescPtr xkb,
925                       XkbAnyAction * action,
926                       unsigned field, ExprDef * array_ndx, ExprDef * value)
927 {
928     ExprResult rtrn;
929     XkbcCtrlsAction *act;
930
931     act = (XkbcCtrlsAction *) action;
932     if (field == F_Controls)
933     {
934         if (array_ndx != NULL)
935             return ReportActionNotArray(action->type, field);
936         if (!ExprResolveMask
937             (value, &rtrn, SimpleLookup, (char *) ctrlNames))
938             return ReportMismatch(action->type, field, "controls mask");
939         act->ctrls = rtrn.uval;
940         return True;
941     }
942     return ReportIllegal(action->type, field);
943 }
944
945 static LookupEntry evNames[] = {
946     {"press", XkbSA_MessageOnPress},
947     {"keypress", XkbSA_MessageOnPress},
948     {"release", XkbSA_MessageOnRelease},
949     {"keyrelease", XkbSA_MessageOnRelease},
950     {"all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease},
951     {"none", 0},
952     {NULL, 0}
953 };
954
955 static Bool
956 HandleActionMessage(XkbcDescPtr xkb,
957                     XkbAnyAction * action,
958                     unsigned field, ExprDef * array_ndx, ExprDef * value)
959 {
960     ExprResult rtrn;
961     XkbMessageAction *act;
962
963     act = (XkbMessageAction *) action;
964     switch (field)
965     {
966     case F_Report:
967         if (array_ndx != NULL)
968             return ReportActionNotArray(action->type, field);
969         if (!ExprResolveMask(value, &rtrn, SimpleLookup, (char *) evNames))
970             return ReportMismatch(action->type, field, "key event mask");
971         act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
972         act->flags =
973             rtrn.uval & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
974         return True;
975     case F_GenKeyEvent:
976         if (array_ndx != NULL)
977             return ReportActionNotArray(action->type, field);
978         if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
979             return ReportMismatch(action->type, field, "boolean");
980         if (rtrn.uval)
981             act->flags |= XkbSA_MessageGenKeyEvent;
982         else
983             act->flags &= ~XkbSA_MessageGenKeyEvent;
984         return True;
985     case F_Data:
986         if (array_ndx == NULL)
987         {
988             if (!ExprResolveString(value, &rtrn, NULL, NULL))
989                 return ReportMismatch(action->type, field, "string");
990             else
991             {
992                 int len = strlen(rtrn.str);
993                 if ((len < 1) || (len > 6))
994                 {
995                     WARN("An action message can hold only 6 bytes\n");
996                     ACTION("Extra %d bytes ignored\n", len - 6);
997                 }
998                 strncpy((char *) act->message, rtrn.str, 6);
999             }
1000             return True;
1001         }
1002         else
1003         {
1004             unsigned ndx;
1005             if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
1006             {
1007                 ERROR("Array subscript must be integer\n");
1008                 ACTION("Illegal subscript ignored\n");
1009                 return False;
1010             }
1011             ndx = rtrn.uval;
1012             if (ndx > 5)
1013             {
1014                 ERROR("An action message is at most 6 bytes long\n");
1015                 ACTION("Attempt to use data[%d] ignored\n", ndx);
1016                 return False;
1017             }
1018             if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1019                 return ReportMismatch(action->type, field, "integer");
1020             if ((rtrn.ival < 0) || (rtrn.ival > 255))
1021             {
1022                 ERROR("Message data must be in the range 0..255\n");
1023                 ACTION("Illegal datum %d ignored\n", rtrn.ival);
1024                 return False;
1025             }
1026             act->message[ndx] = rtrn.uval;
1027         }
1028         return True;
1029     }
1030     return ReportIllegal(action->type, field);
1031 }
1032
1033 static Bool
1034 HandleRedirectKey(XkbcDescPtr xkb,
1035                   XkbAnyAction * action,
1036                   unsigned field, ExprDef * array_ndx, ExprDef * value)
1037 {
1038     ExprResult rtrn;
1039     XkbcRedirectKeyAction *act;
1040     unsigned t1, t2;
1041     unsigned long tmp;
1042
1043     if (array_ndx != NULL)
1044         return ReportActionNotArray(action->type, field);
1045
1046     act = (XkbcRedirectKeyAction *) action;
1047     switch (field)
1048     {
1049     case F_Keycode:
1050         if (!ExprResolveKeyName(value, &rtrn, NULL, NULL))
1051             return ReportMismatch(action->type, field, "key name");
1052         tmp = KeyNameToLong(rtrn.keyName.name);
1053         if (!FindNamedKey(xkb, tmp, &t1, True, CreateKeyNames(xkb), 0))
1054         {
1055             return ReportNotFound(action->type, field, "Key",
1056                                   XkbcKeyNameText(rtrn.keyName.name));
1057         }
1058         act->new_key = t1;
1059         return True;
1060     case F_ModsToClear:
1061     case F_Modifiers:
1062         t1 = 0;
1063         if (CheckModifierField(xkb, action->type, value, &t1, &t2))
1064         {
1065             act->mods_mask |= (t2 & 0xff);
1066             if (field == F_Modifiers)
1067                 act->mods |= (t2 & 0xff);
1068             else
1069                 act->mods &= ~(t2 & 0xff);
1070
1071             t2 = (t2 >> 8) & 0xffff;
1072             act->vmods_mask |= t2;
1073             if (field == F_Modifiers)
1074                 act->vmods |= t2;
1075             else
1076                 act->vmods &= ~t2;
1077             return True;
1078         }
1079         return True;
1080     }
1081     return ReportIllegal(action->type, field);
1082 }
1083
1084 static Bool
1085 HandleDeviceBtn(XkbcDescPtr xkb,
1086                 XkbAnyAction * action,
1087                 unsigned field, ExprDef * array_ndx, ExprDef * value)
1088 {
1089     ExprResult rtrn;
1090     XkbDeviceBtnAction *act;
1091
1092     act = (XkbDeviceBtnAction *) action;
1093     if (field == F_Button)
1094     {
1095         if (array_ndx != NULL)
1096             return ReportActionNotArray(action->type, field);
1097         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1098             return ReportMismatch(action->type, field,
1099                                   "integer (range 1..255)");
1100         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1101         {
1102             ERROR("Button must specify default or be in the range 1..255\n");
1103             ACTION("Illegal button value %d ignored\n", rtrn.ival);
1104             return False;
1105         }
1106         act->button = rtrn.ival;
1107         return True;
1108     }
1109     else if ((action->type == XkbSA_LockDeviceBtn) && (field == F_Affect))
1110     {
1111         if (array_ndx != NULL)
1112             return ReportActionNotArray(action->type, field);
1113         if (!ExprResolveEnum(value, &rtrn, lockWhich))
1114             return ReportMismatch(action->type, field, "lock or unlock");
1115         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
1116         act->flags |= rtrn.ival;
1117         return True;
1118     }
1119     else if (field == F_Count)
1120     {
1121         if (array_ndx != NULL)
1122             return ReportActionNotArray(action->type, field);
1123         if (!ExprResolveInteger
1124             (value, &rtrn, SimpleLookup, (char *) btnNames))
1125             return ReportMismatch(action->type, field, "integer");
1126         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1127         {
1128             ERROR("The count field must have a value in the range 0..255\n");
1129             ACTION("Illegal count %d ignored\n", rtrn.ival);
1130             return False;
1131         }
1132         act->count = rtrn.ival;
1133         return True;
1134     }
1135     else if (field == F_Device)
1136     {
1137         if (array_ndx != NULL)
1138             return ReportActionNotArray(action->type, field);
1139         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1140             return ReportMismatch(action->type, field,
1141                                   "integer (range 1..255)");
1142         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1143         {
1144             ERROR("Device must specify default or be in the range 1..255\n");
1145             ACTION("Illegal device value %d ignored\n", rtrn.ival);
1146             return False;
1147         }
1148         act->device = rtrn.ival;
1149         return True;
1150     }
1151     return ReportIllegal(action->type, field);
1152 }
1153
1154 static Bool
1155 HandleDeviceValuator(XkbcDescPtr xkb,
1156                      XkbAnyAction * action,
1157                      unsigned field, ExprDef * array_ndx, ExprDef * value)
1158 {
1159 #if 0
1160     ExprResult rtrn;
1161     XkbDeviceValuatorAction *act;
1162
1163     act = (XkbDeviceValuatorAction *) action;
1164     /*  XXX - Not yet implemented */
1165 #endif
1166     return False;
1167 }
1168
1169 static Bool
1170 HandlePrivate(XkbcDescPtr xkb,
1171               XkbAnyAction * action,
1172               unsigned field, ExprDef * array_ndx, ExprDef * value)
1173 {
1174     ExprResult rtrn;
1175
1176     switch (field)
1177     {
1178     case F_Type:
1179         if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1180             return ReportMismatch(PrivateAction, field, "integer");
1181         if ((rtrn.ival < 0) || (rtrn.ival > 255))
1182         {
1183             ERROR("Private action type must be in the range 0..255\n");
1184             ACTION("Illegal type %d ignored\n", rtrn.ival);
1185             return False;
1186         }
1187         action->type = rtrn.uval;
1188         return True;
1189     case F_Data:
1190         if (array_ndx == NULL)
1191         {
1192             if (!ExprResolveString(value, &rtrn, NULL, NULL))
1193                 return ReportMismatch(action->type, field, "string");
1194             else
1195             {
1196                 int len = strlen(rtrn.str);
1197                 if ((len < 1) || (len > 7))
1198                 {
1199                     WARN("A private action has 7 data bytes\n");
1200                     ACTION("Extra %d bytes ignored\n", len - 6);
1201                     return False;
1202                 }
1203                 strncpy((char *) action->data, rtrn.str, 7);
1204             }
1205             free(rtrn.str);
1206             return True;
1207         }
1208         else
1209         {
1210             unsigned ndx;
1211             if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
1212             {
1213                 ERROR("Array subscript must be integer\n");
1214                 ACTION("Illegal subscript ignored\n");
1215                 return False;
1216             }
1217             ndx = rtrn.uval;
1218             if (ndx > 6)
1219             {
1220                 ERROR("The data for a private action is 7 bytes long\n");
1221                 ACTION("Attempt to use data[%d] ignored\n", ndx);
1222                 return False;
1223             }
1224             if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1225                 return ReportMismatch(action->type, field, "integer");
1226             if ((rtrn.ival < 0) || (rtrn.ival > 255))
1227             {
1228                 ERROR("All data for a private action must be 0..255\n");
1229                 ACTION("Illegal datum %d ignored\n", rtrn.ival);
1230                 return False;
1231             }
1232             action->data[ndx] = rtrn.uval;
1233             return True;
1234         }
1235     }
1236     return ReportIllegal(PrivateAction, field);
1237 }
1238
1239 typedef Bool(*actionHandler) (XkbcDescPtr /* xkb */ ,
1240                               XkbAnyAction * /* action */ ,
1241                               unsigned /* field */ ,
1242                               ExprDef * /* array_ndx */ ,
1243                               ExprDef * /* value */
1244     );
1245
1246 static actionHandler handleAction[XkbSA_NumActions + 1] = {
1247     HandleNoAction /* NoAction     */ ,
1248     HandleSetLatchMods /* SetMods      */ ,
1249     HandleSetLatchMods /* LatchMods    */ ,
1250     HandleLockMods /* LockMods     */ ,
1251     HandleSetLatchGroup /* SetGroup     */ ,
1252     HandleSetLatchGroup /* LatchGroup   */ ,
1253     HandleLockGroup /* LockGroup    */ ,
1254     HandleMovePtr /* MovePtr      */ ,
1255     HandlePtrBtn /* PtrBtn       */ ,
1256     HandlePtrBtn /* LockPtrBtn   */ ,
1257     HandleSetPtrDflt /* SetPtrDflt   */ ,
1258     HandleISOLock /* ISOLock      */ ,
1259     HandleNoAction /* Terminate    */ ,
1260     HandleSwitchScreen /* SwitchScreen */ ,
1261     HandleSetLockControls /* SetControls  */ ,
1262     HandleSetLockControls /* LockControls */ ,
1263     HandleActionMessage /* ActionMessage */ ,
1264     HandleRedirectKey /* RedirectKey  */ ,
1265     HandleDeviceBtn /* DeviceBtn    */ ,
1266     HandleDeviceBtn /* LockDeviceBtn */ ,
1267     HandleDeviceValuator /* DeviceValuatr */ ,
1268     HandlePrivate               /* Private      */
1269 };
1270
1271 /***====================================================================***/
1272
1273 static void
1274 ApplyActionFactoryDefaults(XkbcAction * action)
1275 {
1276     if (action->type == XkbSA_SetPtrDflt)
1277     {                           /* increment default button */
1278         action->dflt.affect = XkbSA_AffectDfltBtn;
1279         action->dflt.flags = 0;
1280         XkbSASetPtrDfltValue(&action->dflt, 1);
1281     }
1282     else if (action->type == XkbSA_ISOLock)
1283     {
1284         action->iso.real_mods = LockMask;
1285     }
1286     return;
1287 }
1288
1289
1290 int
1291 HandleActionDef(ExprDef * def,
1292                 XkbcDescPtr xkb,
1293                 XkbAnyAction * action, unsigned mergeMode, ActionInfo * info)
1294 {
1295     ExprDef *arg;
1296     register char *str;
1297     unsigned tmp, hndlrType;
1298
1299     if (!actionsInitialized)
1300         ActionsInit();
1301
1302     if (def->op != ExprActionDecl)
1303     {
1304         ERROR("Expected an action definition, found %s\n",
1305                exprOpText(def->op));
1306         return False;
1307     }
1308     str = XkbcAtomGetString(def->value.action.name);
1309     if (!str)
1310     {
1311         WSGO("Missing name in action definition!!\n");
1312         return False;
1313     }
1314     if (!stringToAction(str, &tmp))
1315     {
1316         ERROR("Unknown action %s\n", str);
1317         return False;
1318     }
1319     action->type = hndlrType = tmp;
1320     if (action->type != XkbSA_NoAction)
1321     {
1322         ApplyActionFactoryDefaults((XkbcAction *) action);
1323         while (info)
1324         {
1325             if ((info->action == XkbSA_NoAction)
1326                 || (info->action == hndlrType))
1327             {
1328                 if (!(*handleAction[hndlrType]) (xkb, action,
1329                                                  info->field,
1330                                                  info->array_ndx,
1331                                                  info->value))
1332                 {
1333                     return False;
1334                 }
1335             }
1336             info = info->next;
1337         }
1338     }
1339     for (arg = def->value.action.args; arg != NULL;
1340          arg = (ExprDef *) arg->common.next)
1341     {
1342         ExprDef *field, *value, *arrayRtrn;
1343         ExprResult elemRtrn, fieldRtrn;
1344         unsigned fieldNdx;
1345
1346         if (arg->op == OpAssign)
1347         {
1348             field = arg->value.binary.left;
1349             value = arg->value.binary.right;
1350         }
1351         else
1352         {
1353             if ((arg->op == OpNot) || (arg->op == OpInvert))
1354             {
1355                 field = arg->value.child;
1356                 value = &constFalse;
1357             }
1358             else
1359             {
1360                 field = arg;
1361                 value = &constTrue;
1362             }
1363         }
1364         if (!ExprResolveLhs(field, &elemRtrn, &fieldRtrn, &arrayRtrn))
1365             return False;       /* internal error -- already reported */
1366
1367         if (elemRtrn.str != NULL)
1368         {
1369             ERROR("Cannot change defaults in an action definition\n");
1370             ACTION("Ignoring attempt to change %s.%s\n", elemRtrn.str,
1371                     fieldRtrn.str);
1372             free(elemRtrn.str);
1373             free(fieldRtrn.str);
1374             return False;
1375         }
1376         if (!stringToField(fieldRtrn.str, &fieldNdx))
1377         {
1378             ERROR("Unknown field name %s\n", uStringText(fieldRtrn.str));
1379             free(elemRtrn.str);
1380             free(fieldRtrn.str);
1381             return False;
1382         }
1383         free(elemRtrn.str);
1384         free(fieldRtrn.str);
1385         if (!(*handleAction[hndlrType])
1386             (xkb, action, fieldNdx, arrayRtrn, value))
1387         {
1388             return False;
1389         }
1390     }
1391     return True;
1392 }
1393
1394 /***====================================================================***/
1395
1396 int
1397 SetActionField(XkbcDescPtr xkb,
1398                char *elem,
1399                char *field,
1400                ExprDef * array_ndx, ExprDef * value, ActionInfo ** info_rtrn)
1401 {
1402     ActionInfo *new, *old;
1403
1404     if (!actionsInitialized)
1405         ActionsInit();
1406
1407     new = uTypedAlloc(ActionInfo);
1408     if (new == NULL)
1409     {
1410         WSGO("Couldn't allocate space for action default\n");
1411         return False;
1412     }
1413     if (uStrCaseCmp(elem, "action") == 0)
1414         new->action = XkbSA_NoAction;
1415     else
1416     {
1417         if (!stringToAction(elem, &new->action))
1418         {
1419             free(new);
1420             return False;
1421         }
1422         if (new->action == XkbSA_NoAction)
1423         {
1424             ERROR("\"%s\" is not a valid field in a NoAction action\n",
1425                    field);
1426             free(new);
1427             return False;
1428         }
1429     }
1430     if (!stringToField(field, &new->field))
1431     {
1432         ERROR("\"%s\" is not a legal field name\n", field);
1433         free(new);
1434         return False;
1435     }
1436     new->array_ndx = array_ndx;
1437     new->value = value;
1438     new->next = NULL;
1439     old = *info_rtrn;
1440     while ((old) && (old->next))
1441         old = old->next;
1442     if (old == NULL)
1443         *info_rtrn = new;
1444     else
1445         old->next = new;
1446     return True;
1447 }
1448
1449 /***====================================================================***/
1450
1451 void
1452 ActionsInit(void)
1453 {
1454     if (!actionsInitialized)
1455     {
1456         bzero((char *) &constTrue, sizeof(constTrue));
1457         bzero((char *) &constFalse, sizeof(constFalse));
1458         constTrue.common.stmtType = StmtExpr;
1459         constTrue.common.next = NULL;
1460         constTrue.op = ExprIdent;
1461         constTrue.type = TypeBoolean;
1462         constTrue.value.str = XkbcInternAtom("true", False);
1463         constFalse.common.stmtType = StmtExpr;
1464         constFalse.common.next = NULL;
1465         constFalse.op = ExprIdent;
1466         constFalse.type = TypeBoolean;
1467         constFalse.value.str = XkbcInternAtom("false", False);
1468         actionsInitialized = 1;
1469     }
1470     return;
1471 }