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