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