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