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