compat.c revision a57d84fe
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(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
102static void
103InitCompatInfo(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
135static void
136ClearCompatInfo(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
163static SymInterpInfo *
164NextInterp(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
180static SymInterpInfo *
181FindMatchingInterp(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
198static Bool
199AddInterp(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 && (warningLevel > 0))
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
265static Bool
266AddGroupCompat(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
291static Bool
292ResolveStateAndPredicate(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
349static void
350MergeIncludedCompatMaps(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
395typedef void (*FileHandler) (XkbFile * /* rtrn */ ,
396                             XkbDescPtr /* xkb */ ,
397                             unsigned /* merge */ ,
398                             CompatInfo *       /* info */
399    );
400
401static Bool
402HandleIncludeCompatMap(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
486static LookupEntry useModMapValues[] = {
487    {"levelone", 1},
488    {"level1", 1},
489    {"anylevel", 0},
490    {"any", 0},
491    {NULL, 0}
492};
493
494static int
495SetInterpField(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
582LookupEntry 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
606static int
607HandleInterpVar(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
626static int
627HandleInterpBody(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
649static int
650HandleInterpDef(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
691static int
692HandleGroupCompatDef(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
723static void
724HandleCompatMapFile(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
793static void
794CopyInterps(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
816Bool
817CompileCompatMap(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