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 <X11/Xos.h>
28 #include "xkbcomp.h"
29 #include "tokens.h"
30 #include "expr.h"
31 #include "vmod.h"
32 #include "misc.h"
33 #include "indicators.h"
34 #include "action.h"
35 #include "compat.h"
36 
37 typedef struct _SymInterpInfo
38 {
39     CommonInfo defs;
40     XkbSymInterpretRec interp;
41 } SymInterpInfo;
42 
43 #define	_SI_VirtualMod		(1<<0)
44 #define	_SI_Action		(1<<1)
45 #define	_SI_AutoRepeat		(1<<2)
46 #define	_SI_LockingKey		(1<<3)
47 #define	_SI_LevelOneOnly	(1<<4)
48 
49 typedef struct _GroupCompatInfo
50 {
51     unsigned char fileID;
52     unsigned char merge;
53     Bool defined;
54     unsigned char real_mods;
55     unsigned short vmods;
56 } GroupCompatInfo;
57 
58 typedef struct _CompatInfo
59 {
60     char *name;
61     unsigned fileID;
62     int errorCount;
63     int nInterps;
64     SymInterpInfo *interps;
65     SymInterpInfo dflt;
66     LEDInfo ledDflt;
67     GroupCompatInfo groupCompat[XkbNumKbdGroups];
68     LEDInfo *leds;
69     VModInfo vmods;
70     ActionInfo *act;
71     XkbDescPtr xkb;
72 } CompatInfo;
73 
74 /***====================================================================***/
75 
76 #define	ReportSINotArray(si,f,i) \
77 	ReportNotArray("symbol interpretation",(f),siText((si),(i)))
78 #define	ReportSIBadType(si,f,w,i) \
79 	ReportBadType("symbol interpretation",(f),siText((si),(i)),(w))
80 
81 /***====================================================================***/
82 
83 static char *
siText(SymInterpInfo * si,CompatInfo * info)84 siText(SymInterpInfo * si, CompatInfo * info)
85 {
86     static char buf[128];
87 
88     if (si == &info->dflt)
89     {
90         snprintf(buf, sizeof(buf), "default");
91     }
92     else
93     {
94         snprintf(buf, sizeof(buf), "%s+%s(%s)",
95 		XkbKeysymText(si->interp.sym, XkbMessage),
96                 XkbSIMatchText(si->interp.match, XkbMessage),
97                 XkbModMaskText(si->interp.mods, XkbMessage));
98     }
99     return buf;
100 }
101 
102 static void
InitCompatInfo(CompatInfo * info,XkbDescPtr xkb)103 InitCompatInfo(CompatInfo * info, XkbDescPtr xkb)
104 {
105     register int i;
106 
107     info->xkb = xkb;
108     info->name = NULL;
109     info->fileID = 0;
110     info->errorCount = 0;
111     info->nInterps = 0;
112     info->interps = NULL;
113     info->act = NULL;
114     info->dflt.defs.fileID = info->fileID;
115     info->dflt.defs.defined = 0;
116     info->dflt.defs.merge = MergeOverride;
117     info->dflt.interp.flags = 0;
118     info->dflt.interp.virtual_mod = XkbNoModifier;
119     info->dflt.interp.act.type = XkbSA_NoAction;
120     for (i = 0; i < XkbAnyActionDataSize; i++)
121     {
122         info->dflt.interp.act.data[i] = 0;
123     }
124     ClearIndicatorMapInfo(xkb->dpy, &info->ledDflt);
125     info->ledDflt.defs.fileID = info->fileID;
126     info->ledDflt.defs.defined = 0;
127     info->ledDflt.defs.merge = MergeOverride;
128     bzero((char *) &info->groupCompat[0],
129           XkbNumKbdGroups * sizeof(GroupCompatInfo));
130     info->leds = NULL;
131     InitVModInfo(&info->vmods, xkb);
132     return;
133 }
134 
135 static void
ClearCompatInfo(CompatInfo * info,XkbDescPtr xkb)136 ClearCompatInfo(CompatInfo * info, XkbDescPtr xkb)
137 {
138     register int i;
139 
140     if (info->name != NULL)
141         uFree(info->name);
142     info->name = NULL;
143     info->dflt.defs.defined = 0;
144     info->dflt.defs.merge = MergeAugment;
145     info->dflt.interp.flags = 0;
146     info->dflt.interp.virtual_mod = XkbNoModifier;
147     info->dflt.interp.act.type = XkbSA_NoAction;
148     for (i = 0; i < XkbAnyActionDataSize; i++)
149     {
150         info->dflt.interp.act.data[i] = 0;
151     }
152     ClearIndicatorMapInfo(xkb->dpy, &info->ledDflt);
153     info->nInterps = 0;
154     info->interps = (SymInterpInfo *) ClearCommonInfo(&info->interps->defs);
155     bzero((char *) &info->groupCompat[0],
156           XkbNumKbdGroups * sizeof(GroupCompatInfo));
157     info->leds = (LEDInfo *) ClearCommonInfo(&info->leds->defs);
158     /* 3/30/94 (ef) -- XXX! Should free action info here */
159     ClearVModInfo(&info->vmods, xkb);
160     return;
161 }
162 
163 static SymInterpInfo *
NextInterp(CompatInfo * info)164 NextInterp(CompatInfo * info)
165 {
166     SymInterpInfo *si;
167 
168     si = uTypedAlloc(SymInterpInfo);
169     if (si)
170     {
171         bzero((char *) si, sizeof(SymInterpInfo));
172         info->interps =
173             (SymInterpInfo *) AddCommonInfo(&info->interps->defs,
174                                             (CommonInfo *) si);
175         info->nInterps++;
176     }
177     return si;
178 }
179 
180 static SymInterpInfo *
FindMatchingInterp(CompatInfo * info,SymInterpInfo * new)181 FindMatchingInterp(CompatInfo * info, SymInterpInfo * new)
182 {
183     SymInterpInfo *old;
184 
185     for (old = info->interps; old != NULL;
186          old = (SymInterpInfo *) old->defs.next)
187     {
188         if ((old->interp.sym == new->interp.sym) &&
189             (old->interp.mods == new->interp.mods) &&
190             (old->interp.match == new->interp.match))
191         {
192             return old;
193         }
194     }
195     return NULL;
196 }
197 
198 static Bool
AddInterp(CompatInfo * info,SymInterpInfo * new)199 AddInterp(CompatInfo * info, SymInterpInfo * new)
200 {
201     unsigned collide;
202     SymInterpInfo *old;
203 
204     collide = 0;
205     old = FindMatchingInterp(info, new);
206     if (old != NULL)
207     {
208         if (new->defs.merge == MergeReplace)
209         {
210             SymInterpInfo *next = (SymInterpInfo *) old->defs.next;
211             if (((old->defs.fileID == new->defs.fileID)
212                  && (warningLevel > 0)) || (warningLevel > 9))
213             {
214                 WARN("Multiple definitions for \"%s\"\n", siText(new, info));
215                 ACTION("Earlier interpretation ignored\n");
216             }
217             *old = *new;
218             old->defs.next = &next->defs;
219             return True;
220         }
221         if (UseNewField(_SI_VirtualMod, &old->defs, &new->defs, &collide))
222         {
223             old->interp.virtual_mod = new->interp.virtual_mod;
224             old->defs.defined |= _SI_VirtualMod;
225         }
226         if (UseNewField(_SI_Action, &old->defs, &new->defs, &collide))
227         {
228             old->interp.act = new->interp.act;
229             old->defs.defined |= _SI_Action;
230         }
231         if (UseNewField(_SI_AutoRepeat, &old->defs, &new->defs, &collide))
232         {
233             old->interp.flags &= ~XkbSI_AutoRepeat;
234             old->interp.flags |= (new->interp.flags & XkbSI_AutoRepeat);
235             old->defs.defined |= _SI_AutoRepeat;
236         }
237         if (UseNewField(_SI_LockingKey, &old->defs, &new->defs, &collide))
238         {
239             old->interp.flags &= ~XkbSI_LockingKey;
240             old->interp.flags |= (new->interp.flags & XkbSI_LockingKey);
241             old->defs.defined |= _SI_LockingKey;
242         }
243         if (UseNewField(_SI_LevelOneOnly, &old->defs, &new->defs, &collide))
244         {
245             old->interp.match &= ~XkbSI_LevelOneOnly;
246             old->interp.match |= (new->interp.match & XkbSI_LevelOneOnly);
247             old->defs.defined |= _SI_LevelOneOnly;
248         }
249         if (collide)
250         {
251             WARN("Multiple interpretations of \"%s\"\n", siText(new, info));
252             ACTION("Using %s definition for duplicate fields\n",
253                     (new->defs.merge != MergeAugment ? "last" : "first"));
254         }
255         return True;
256     }
257     old = new;
258     if ((new = NextInterp(info)) == NULL)
259         return False;
260     *new = *old;
261     new->defs.next = NULL;
262     return True;
263 }
264 
265 static Bool
AddGroupCompat(CompatInfo * info,unsigned group,GroupCompatInfo * newGC)266 AddGroupCompat(CompatInfo * info, unsigned group, GroupCompatInfo * newGC)
267 {
268     GroupCompatInfo *gc;
269     unsigned merge;
270 
271     merge = newGC->merge;
272     gc = &info->groupCompat[group];
273     if (((gc->real_mods == newGC->real_mods) && (gc->vmods == newGC->vmods)))
274     {
275         return True;
276     }
277     if (((gc->defined && gc->fileID == newGC->fileID) && (warningLevel > 0))
278         || (warningLevel > 9))
279     {
280         WARN("Compat map for group %d redefined\n", group + 1);
281         ACTION("Using %s definition\n",
282                 (merge == MergeAugment ? "old" : "new"));
283     }
284     if(newGC->defined && (merge != MergeAugment || !gc->defined))
285 	*gc = *newGC;
286     return True;
287 }
288 
289 /***====================================================================***/
290 
291 static Bool
ResolveStateAndPredicate(ExprDef * expr,unsigned * pred_rtrn,unsigned * mods_rtrn,CompatInfo * info)292 ResolveStateAndPredicate(ExprDef * expr,
293                          unsigned *pred_rtrn,
294                          unsigned *mods_rtrn, CompatInfo * info)
295 {
296     ExprResult result;
297 
298     if (expr == NULL)
299     {
300         *pred_rtrn = XkbSI_AnyOfOrNone;
301         *mods_rtrn = ~0;
302         return True;
303     }
304 
305     *pred_rtrn = XkbSI_Exactly;
306     if (expr->op == ExprActionDecl)
307     {
308         char *pred_txt =
309             XkbAtomText(NULL, expr->value.action.name, XkbMessage);
310         if (uStrCaseCmp(pred_txt, "noneof") == 0)
311             *pred_rtrn = XkbSI_NoneOf;
312         else if (uStrCaseCmp(pred_txt, "anyofornone") == 0)
313             *pred_rtrn = XkbSI_AnyOfOrNone;
314         else if (uStrCaseCmp(pred_txt, "anyof") == 0)
315             *pred_rtrn = XkbSI_AnyOf;
316         else if (uStrCaseCmp(pred_txt, "allof") == 0)
317             *pred_rtrn = XkbSI_AllOf;
318         else if (uStrCaseCmp(pred_txt, "exactly") == 0)
319             *pred_rtrn = XkbSI_Exactly;
320         else
321         {
322             ERROR("Illegal modifier predicate \"%s\"\n", pred_txt);
323             ACTION("Ignored\n");
324             return False;
325         }
326         expr = expr->value.action.args;
327     }
328     else if (expr->op == ExprIdent)
329     {
330         char *pred_txt = XkbAtomText(NULL, expr->value.str, XkbMessage);
331         if ((pred_txt) && (uStrCaseCmp(pred_txt, "any") == 0))
332         {
333             *pred_rtrn = XkbSI_AnyOf;
334             *mods_rtrn = 0xff;
335             return True;
336         }
337     }
338 
339     if (ExprResolveModMask(expr, &result, NULL, NULL))
340     {
341         *mods_rtrn = result.uval;
342         return True;
343     }
344     return False;
345 }
346 
347 /***====================================================================***/
348 
349 static void
MergeIncludedCompatMaps(CompatInfo * into,CompatInfo * from,unsigned merge)350 MergeIncludedCompatMaps(CompatInfo * into, CompatInfo * from, unsigned merge)
351 {
352     SymInterpInfo *si;
353     LEDInfo *led, *rtrn, *next;
354     GroupCompatInfo *gcm;
355     register int i;
356 
357     if (from->errorCount > 0)
358     {
359         into->errorCount += from->errorCount;
360         return;
361     }
362     if (into->name == NULL)
363     {
364         into->name = from->name;
365         from->name = NULL;
366     }
367     for (si = from->interps; si; si = (SymInterpInfo *) si->defs.next)
368     {
369         if (merge != MergeDefault)
370             si->defs.merge = merge;
371         if (!AddInterp(into, si))
372             into->errorCount++;
373     }
374     for (i = 0, gcm = &from->groupCompat[0]; i < XkbNumKbdGroups; i++, gcm++)
375     {
376         if (merge != MergeDefault)
377             gcm->merge = merge;
378         if (!AddGroupCompat(into, i, gcm))
379             into->errorCount++;
380     }
381     for (led = from->leds; led != NULL; led = next)
382     {
383         next = (LEDInfo *) led->defs.next;
384         if (merge != MergeDefault)
385             led->defs.merge = merge;
386         rtrn = AddIndicatorMap(into->leds, led);
387         if (rtrn != NULL)
388             into->leds = rtrn;
389         else
390             into->errorCount++;
391     }
392     return;
393 }
394 
395 typedef void (*FileHandler) (XkbFile * /* rtrn */ ,
396                              XkbDescPtr /* xkb */ ,
397                              unsigned /* merge */ ,
398                              CompatInfo *       /* info */
399     );
400 
401 static Bool
HandleIncludeCompatMap(IncludeStmt * stmt,XkbDescPtr xkb,CompatInfo * info,FileHandler hndlr)402 HandleIncludeCompatMap(IncludeStmt * stmt,
403                        XkbDescPtr xkb, CompatInfo * info, FileHandler hndlr)
404 {
405     unsigned newMerge;
406     XkbFile *rtrn;
407     CompatInfo included;
408     Bool haveSelf;
409 
410     haveSelf = False;
411     if ((stmt->file == NULL) && (stmt->map == NULL))
412     {
413         haveSelf = True;
414         included = *info;
415         bzero(info, sizeof(CompatInfo));
416     }
417     else if (ProcessIncludeFile(stmt, XkmCompatMapIndex, &rtrn, &newMerge))
418     {
419         InitCompatInfo(&included, xkb);
420         included.fileID = rtrn->id;
421         included.dflt = info->dflt;
422         included.dflt.defs.fileID = rtrn->id;
423         included.dflt.defs.merge = newMerge;
424         included.ledDflt.defs.fileID = rtrn->id;
425         included.ledDflt.defs.merge = newMerge;
426         included.act = info->act;
427         (*hndlr) (rtrn, xkb, MergeOverride, &included);
428         if (stmt->stmt != NULL)
429         {
430             if (included.name != NULL)
431                 uFree(included.name);
432             included.name = stmt->stmt;
433             stmt->stmt = NULL;
434         }
435     }
436     else
437     {
438         info->errorCount += 10;
439         return False;
440     }
441     if ((stmt->next != NULL) && (included.errorCount < 1))
442     {
443         IncludeStmt *next;
444         unsigned op;
445         CompatInfo next_incl;
446 
447         for (next = stmt->next; next != NULL; next = next->next)
448         {
449             if ((next->file == NULL) && (next->map == NULL))
450             {
451                 haveSelf = True;
452                 MergeIncludedCompatMaps(&included, info, next->merge);
453                 ClearCompatInfo(info, xkb);
454             }
455             else if (ProcessIncludeFile(next, XkmCompatMapIndex, &rtrn, &op))
456             {
457                 InitCompatInfo(&next_incl, xkb);
458                 next_incl.fileID = rtrn->id;
459                 next_incl.dflt = info->dflt;
460                 next_incl.dflt.defs.fileID = rtrn->id;
461                 next_incl.dflt.defs.merge = op;
462                 next_incl.ledDflt.defs.fileID = rtrn->id;
463                 next_incl.ledDflt.defs.merge = op;
464                 next_incl.act = info->act;
465                 (*hndlr) (rtrn, xkb, MergeOverride, &next_incl);
466                 MergeIncludedCompatMaps(&included, &next_incl, op);
467                 ClearCompatInfo(&next_incl, xkb);
468             }
469             else
470             {
471                 info->errorCount += 10;
472                 return False;
473             }
474         }
475     }
476     if (haveSelf)
477         *info = included;
478     else
479     {
480         MergeIncludedCompatMaps(info, &included, newMerge);
481         ClearCompatInfo(&included, xkb);
482     }
483     return (info->errorCount == 0);
484 }
485 
486 static LookupEntry useModMapValues[] = {
487     {"levelone", 1},
488     {"level1", 1},
489     {"anylevel", 0},
490     {"any", 0},
491     {NULL, 0}
492 };
493 
494 static int
SetInterpField(SymInterpInfo * si,XkbDescPtr xkb,const char * field,ExprDef * arrayNdx,ExprDef * value,CompatInfo * info)495 SetInterpField(SymInterpInfo * si,
496                XkbDescPtr xkb,
497                const char *field,
498                ExprDef * arrayNdx, ExprDef * value, CompatInfo * info)
499 {
500     int ok = 1;
501     ExprResult tmp;
502 
503     if (uStrCaseCmp(field, "action") == 0)
504     {
505         if (arrayNdx != NULL)
506             return ReportSINotArray(si, field, info);
507         ok = HandleActionDef(value, xkb, &si->interp.act, si->defs.merge,
508                              info->act);
509         if (ok)
510             si->defs.defined |= _SI_Action;
511     }
512     else if ((uStrCaseCmp(field, "virtualmodifier") == 0) ||
513              (uStrCaseCmp(field, "virtualmod") == 0))
514     {
515         if (arrayNdx != NULL)
516             return ReportSINotArray(si, field, info);
517         ok = ResolveVirtualModifier(value, &tmp, &info->vmods);
518         if (ok)
519         {
520             si->interp.virtual_mod = tmp.uval;
521             si->defs.defined |= _SI_VirtualMod;
522         }
523         else
524             return ReportSIBadType(si, field, "virtual modifier", info);
525     }
526     else if (uStrCaseCmp(field, "repeat") == 0)
527     {
528         if (arrayNdx != NULL)
529             return ReportSINotArray(si, field, info);
530         ok = ExprResolveBoolean(value, &tmp, NULL, NULL);
531         if (ok)
532         {
533             if (tmp.uval)
534                 si->interp.flags |= XkbSI_AutoRepeat;
535             else
536                 si->interp.flags &= ~XkbSI_AutoRepeat;
537             si->defs.defined |= _SI_AutoRepeat;
538         }
539         else
540             return ReportSIBadType(si, field, "boolean", info);
541     }
542     else if (uStrCaseCmp(field, "locking") == 0)
543     {
544         if (arrayNdx != NULL)
545             return ReportSINotArray(si, field, info);
546         ok = ExprResolveBoolean(value, &tmp, NULL, NULL);
547         if (ok)
548         {
549             if (tmp.uval)
550                 si->interp.flags |= XkbSI_LockingKey;
551             else
552                 si->interp.flags &= ~XkbSI_LockingKey;
553             si->defs.defined |= _SI_LockingKey;
554         }
555         else
556             return ReportSIBadType(si, field, "boolean", info);
557     }
558     else if ((uStrCaseCmp(field, "usemodmap") == 0) ||
559              (uStrCaseCmp(field, "usemodmapmods") == 0))
560     {
561         if (arrayNdx != NULL)
562             return ReportSINotArray(si, field, info);
563         ok = ExprResolveEnum(value, &tmp, useModMapValues);
564         if (ok)
565         {
566             if (tmp.uval)
567                 si->interp.match |= XkbSI_LevelOneOnly;
568             else
569                 si->interp.match &= ~XkbSI_LevelOneOnly;
570             si->defs.defined |= _SI_LevelOneOnly;
571         }
572         else
573             return ReportSIBadType(si, field, "level specification", info);
574     }
575     else
576     {
577         ok = ReportBadField("symbol interpretation", field, siText(si, info));
578     }
579     return ok;
580 }
581 
582 LookupEntry groupNames[] = {
583     {"group1", 0x01}
584     ,
585     {"group2", 0x02}
586     ,
587     {"group3", 0x04}
588     ,
589     {"group4", 0x08}
590     ,
591     {"group5", 0x10}
592     ,
593     {"group6", 0x20}
594     ,
595     {"group7", 0x40}
596     ,
597     {"group8", 0x80}
598     ,
599     {"none", 0x00}
600     ,
601     {"all", 0xff}
602     ,
603     {NULL, 0}
604 };
605 
606 static int
HandleInterpVar(VarDef * stmt,XkbDescPtr xkb,CompatInfo * info)607 HandleInterpVar(VarDef * stmt, XkbDescPtr xkb, CompatInfo * info)
608 {
609     ExprResult elem, field;
610     ExprDef *ndx;
611 
612     if (ExprResolveLhs(stmt->name, &elem, &field, &ndx) == 0)
613         return 0;               /* internal error, already reported */
614     if (elem.str && (uStrCaseCmp(elem.str, "interpret") == 0))
615         return SetInterpField(&info->dflt, xkb, field.str, ndx, stmt->value,
616                               info);
617     if (elem.str && (uStrCaseCmp(elem.str, "indicator") == 0))
618     {
619         return SetIndicatorMapField(&info->ledDflt, xkb, field.str, ndx,
620                                     stmt->value);
621     }
622     return SetActionField(xkb, elem.str, field.str, ndx, stmt->value,
623                           &info->act);
624 }
625 
626 static int
HandleInterpBody(VarDef * def,XkbDescPtr xkb,SymInterpInfo * si,CompatInfo * info)627 HandleInterpBody(VarDef * def, XkbDescPtr xkb, SymInterpInfo * si,
628                  CompatInfo * info)
629 {
630     int ok = 1;
631     ExprResult tmp, field;
632     ExprDef *arrayNdx;
633 
634     for (; def != NULL; def = (VarDef *) def->common.next)
635     {
636         if ((def->name) && (def->name->type == ExprFieldRef))
637         {
638             ok = HandleInterpVar(def, xkb, info);
639             continue;
640         }
641         ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
642         if (ok)
643             ok = SetInterpField(si, xkb, field.str, arrayNdx, def->value,
644                                 info);
645     }
646     return ok;
647 }
648 
649 static int
HandleInterpDef(InterpDef * def,XkbDescPtr xkb,unsigned merge,CompatInfo * info)650 HandleInterpDef(InterpDef * def, XkbDescPtr xkb, unsigned merge,
651                 CompatInfo * info)
652 {
653     unsigned pred, mods;
654     SymInterpInfo si;
655 
656     if (!ResolveStateAndPredicate(def->match, &pred, &mods, info))
657     {
658         ERROR("Couldn't determine matching modifiers\n");
659         ACTION("Symbol interpretation ignored\n");
660         return True;
661     }
662     if (def->ignore)
663     {
664         ERROR("Couldn't lookup keysym\n");
665         ACTION("Symbol interpretation ignored\n");
666         return True;
667     }
668 
669     if (def->merge != MergeDefault)
670         merge = def->merge;
671 
672     si = info->dflt;
673     si.defs.merge = merge;
674     si.interp.sym = def->sym;
675     si.interp.match = pred & XkbSI_OpMask;
676     si.interp.mods = mods;
677     if (!HandleInterpBody(def->def, xkb, &si, info))
678     {
679         info->errorCount++;
680         return False;
681     }
682 
683     if (!AddInterp(info, &si))
684     {
685         info->errorCount++;
686         return False;
687     }
688     return True;
689 }
690 
691 static int
HandleGroupCompatDef(GroupCompatDef * def,XkbDescPtr xkb,unsigned merge,CompatInfo * info)692 HandleGroupCompatDef(GroupCompatDef * def,
693                      XkbDescPtr xkb, unsigned merge, CompatInfo * info)
694 {
695     ExprResult val;
696     GroupCompatInfo tmp;
697 
698     if (def->merge != MergeDefault)
699         merge = def->merge;
700     if (!XkbIsLegalGroup(def->group - 1))
701     {
702         ERROR("Keyboard group must be in the range 1..%d\n",
703                XkbNumKbdGroups + 1);
704         ACTION("Compatibility map for illegal group %d ignored\n",
705                 def->group);
706         return False;
707     }
708     tmp.fileID = info->fileID;
709     tmp.merge = merge;
710     if (!ExprResolveModMask(def->def, &val, LookupVModMask, (XPointer) xkb))
711     {
712         ERROR("Expected a modifier mask in group compatibility definition\n");
713         ACTION("Ignoring illegal compatibility map for group %d\n",
714                 def->group);
715         return False;
716     }
717     tmp.real_mods = val.uval & 0xff;
718     tmp.vmods = (val.uval >> 8) & 0xffff;
719     tmp.defined = True;
720     return AddGroupCompat(info, def->group - 1, &tmp);
721 }
722 
723 static void
HandleCompatMapFile(XkbFile * file,XkbDescPtr xkb,unsigned merge,CompatInfo * info)724 HandleCompatMapFile(XkbFile * file,
725                     XkbDescPtr xkb, unsigned merge, CompatInfo * info)
726 {
727     ParseCommon *stmt;
728 
729     if (merge == MergeDefault)
730         merge = MergeAugment;
731     info->name = uStringDup(file->name);
732     stmt = file->defs;
733     while (stmt)
734     {
735         switch (stmt->stmtType)
736         {
737         case StmtInclude:
738             if (!HandleIncludeCompatMap((IncludeStmt *) stmt, xkb, info,
739                                         HandleCompatMapFile))
740                 info->errorCount++;
741             break;
742         case StmtInterpDef:
743             if (!HandleInterpDef((InterpDef *) stmt, xkb, merge, info))
744                 info->errorCount++;
745             break;
746         case StmtGroupCompatDef:
747             if (!HandleGroupCompatDef
748                 ((GroupCompatDef *) stmt, xkb, merge, info))
749                 info->errorCount++;
750             break;
751         case StmtIndicatorMapDef:
752         {
753             LEDInfo *rtrn;
754             rtrn = HandleIndicatorMapDef((IndicatorMapDef *) stmt, xkb,
755                                          &info->ledDflt, info->leds, merge);
756             if (rtrn != NULL)
757                 info->leds = rtrn;
758             else
759                 info->errorCount++;
760         }
761             break;
762         case StmtVarDef:
763             if (!HandleInterpVar((VarDef *) stmt, xkb, info))
764                 info->errorCount++;
765             break;
766         case StmtVModDef:
767             if (!HandleVModDef((VModDef *) stmt, merge, &info->vmods))
768                 info->errorCount++;
769             break;
770         case StmtKeycodeDef:
771             ERROR("Interpretation files may not include other types\n");
772             ACTION("Ignoring definition of key name\n");
773             info->errorCount++;
774             break;
775         default:
776             WSGO("Unexpected statement type %d in HandleCompatMapFile\n",
777                   stmt->stmtType);
778             break;
779         }
780         stmt = stmt->next;
781         if (info->errorCount > 10)
782         {
783 #ifdef NOISY
784             ERROR("Too many errors\n");
785 #endif
786             ACTION("Abandoning compatibility map \"%s\"\n", file->topName);
787             break;
788         }
789     }
790     return;
791 }
792 
793 static void
CopyInterps(CompatInfo * info,XkbCompatMapPtr compat,Bool needSymbol,unsigned pred)794 CopyInterps(CompatInfo * info,
795             XkbCompatMapPtr compat, Bool needSymbol, unsigned pred)
796 {
797     SymInterpInfo *si;
798 
799     for (si = info->interps; si; si = (SymInterpInfo *) si->defs.next)
800     {
801         if (((si->interp.match & XkbSI_OpMask) != pred) ||
802             (needSymbol && (si->interp.sym == NoSymbol)) ||
803             ((!needSymbol) && (si->interp.sym != NoSymbol)))
804             continue;
805         if (compat->num_si >= compat->size_si)
806         {
807             WSGO("No room to merge symbol interpretations\n");
808             ACTION("Symbol interpretations lost\n");
809             return;
810         }
811         compat->sym_interpret[compat->num_si++] = si->interp;
812     }
813     return;
814 }
815 
816 Bool
CompileCompatMap(XkbFile * file,XkbFileInfo * result,unsigned merge,LEDInfo ** unboundLEDs)817 CompileCompatMap(XkbFile * file,
818                  XkbFileInfo * result, unsigned merge, LEDInfo ** unboundLEDs)
819 {
820     int i;
821     CompatInfo info;
822     XkbDescPtr xkb;
823     GroupCompatInfo *gcm;
824 
825     xkb = result->xkb;
826     InitCompatInfo(&info, xkb);
827     info.dflt.defs.merge = merge;
828     info.ledDflt.defs.merge = merge;
829     HandleCompatMapFile(file, xkb, merge, &info);
830 
831     if (info.errorCount == 0)
832     {
833         int size;
834         if (XkbAllocCompatMap(xkb, XkbAllCompatMask, info.nInterps) !=
835             Success)
836         {
837             WSGO("Couldn't allocate compatibility map\n");
838             ACTION("Exiting\n");
839             return False;
840         }
841         if (info.name != NULL)
842         {
843             if (XkbAllocNames(xkb, XkbCompatNameMask, 0, 0) == Success)
844                 xkb->names->compat =
845                     XkbInternAtom(xkb->dpy, info.name, False);
846             else
847             {
848                 WSGO("Couldn't allocate space for compat name\n");
849                 ACTION("Name \"%s\" (from %s) NOT assigned\n",
850                         scanFile, info.name);
851             }
852         }
853         size = info.nInterps * sizeof(XkbSymInterpretRec);
854         if (size > 0)
855         {
856             CopyInterps(&info, xkb->compat, True, XkbSI_Exactly);
857             CopyInterps(&info, xkb->compat, True, XkbSI_AllOf | XkbSI_NoneOf);
858             CopyInterps(&info, xkb->compat, True, XkbSI_AnyOf);
859             CopyInterps(&info, xkb->compat, True, XkbSI_AnyOfOrNone);
860             CopyInterps(&info, xkb->compat, False, XkbSI_Exactly);
861             CopyInterps(&info, xkb->compat, False,
862                         XkbSI_AllOf | XkbSI_NoneOf);
863             CopyInterps(&info, xkb->compat, False, XkbSI_AnyOf);
864             CopyInterps(&info, xkb->compat, False, XkbSI_AnyOfOrNone);
865         }
866         for (i = 0, gcm = &info.groupCompat[0]; i < XkbNumKbdGroups;
867              i++, gcm++)
868         {
869             if ((gcm->fileID != 0) || (gcm->real_mods != 0)
870                 || (gcm->vmods != 0))
871             {
872                 xkb->compat->groups[i].mask = gcm->real_mods;
873                 xkb->compat->groups[i].real_mods = gcm->real_mods;
874                 xkb->compat->groups[i].vmods = gcm->vmods;
875             }
876         }
877         if (info.leds != NULL)
878         {
879             if (!CopyIndicatorMapDefs(result, info.leds, unboundLEDs))
880                 info.errorCount++;
881             info.leds = NULL;
882         }
883         ClearCompatInfo(&info, xkb);
884         return True;
885     }
886     if (info.interps != NULL)
887         uFree(info.interps);
888     return False;
889 }
890