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#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include <stdio.h>
32
33#include <X11/Xos.h>
34#include <X11/Xfuncs.h>
35
36#include <X11/X.h>
37#include <X11/Xproto.h>
38#include <X11/keysym.h>
39#include <X11/extensions/XKMformat.h>
40#include "misc.h"
41#include "inputstr.h"
42#include "xkbstr.h"
43#include "xkbsrv.h"
44#include "xkbgeom.h"
45
46Atom
47XkbInternAtom(char *str, Bool only_if_exists)
48{
49    if (str == NULL)
50        return None;
51    return MakeAtom(str, strlen(str), !only_if_exists);
52}
53
54/***====================================================================***/
55
56static void *
57XkmInsureSize(void *oldPtr, int oldCount, int *newCountRtrn, int elemSize)
58{
59    int newCount = *newCountRtrn;
60
61    if (oldPtr == NULL) {
62        if (newCount == 0)
63            return NULL;
64        oldPtr = calloc(newCount, elemSize);
65    }
66    else if (oldCount < newCount) {
67        oldPtr = reallocarray(oldPtr, newCount, elemSize);
68        if (oldPtr != NULL) {
69            char *tmp = (char *) oldPtr;
70
71            memset(&tmp[oldCount * elemSize], 0,
72                   (newCount - oldCount) * elemSize);
73        }
74    }
75    else if (newCount < oldCount) {
76        *newCountRtrn = oldCount;
77    }
78    return oldPtr;
79}
80
81#define	XkmInsureTypedSize(p,o,n,t) ((p)=((t *)XkmInsureSize((char *)(p),(o),(n),sizeof(t))))
82
83static CARD8
84XkmGetCARD8(FILE * file, int *pNRead)
85{
86    int tmp;
87
88    tmp = getc(file);
89    if (pNRead && (tmp != EOF))
90        (*pNRead) += 1;
91    return tmp;
92}
93
94static CARD16
95XkmGetCARD16(FILE * file, int *pNRead)
96{
97    CARD16 val;
98
99    if ((fread(&val, 2, 1, file) == 1) && (pNRead))
100        (*pNRead) += 2;
101    return val;
102}
103
104static CARD32
105XkmGetCARD32(FILE * file, int *pNRead)
106{
107    CARD32 val;
108
109    if ((fread(&val, 4, 1, file) == 1) && (pNRead))
110        (*pNRead) += 4;
111    return val;
112}
113
114static int
115XkmSkipPadding(FILE * file, unsigned pad)
116{
117    register int i, nRead = 0;
118
119    for (i = 0; i < pad; i++) {
120        if (getc(file) != EOF)
121            nRead++;
122    }
123    return nRead;
124}
125
126static int
127XkmGetCountedString(FILE * file, char *str, int max_len)
128{
129    int count, nRead = 0;
130
131    count = XkmGetCARD16(file, &nRead);
132    if (count > 0) {
133        int tmp;
134
135        if (count > max_len) {
136            tmp = fread(str, 1, max_len, file);
137            while (tmp < count) {
138                if ((getc(file)) != EOF)
139                    tmp++;
140                else
141                    break;
142            }
143        }
144        else {
145            tmp = fread(str, 1, count, file);
146        }
147        nRead += tmp;
148    }
149    if (count >= max_len)
150        str[max_len - 1] = '\0';
151    else
152        str[count] = '\0';
153    count = XkbPaddedSize(nRead) - nRead;
154    if (count > 0)
155        nRead += XkmSkipPadding(file, count);
156    return nRead;
157}
158
159/***====================================================================***/
160
161static int
162ReadXkmVirtualMods(FILE * file, XkbDescPtr xkb, XkbChangesPtr changes)
163{
164    register unsigned int i, bit;
165    unsigned int bound, named, tmp;
166    int nRead = 0;
167
168    if (XkbAllocServerMap(xkb, XkbVirtualModsMask, 0) != Success) {
169        _XkbLibError(_XkbErrBadAlloc, "ReadXkmVirtualMods", 0);
170        return -1;
171    }
172    bound = XkmGetCARD16(file, &nRead);
173    named = XkmGetCARD16(file, &nRead);
174    for (i = tmp = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
175        if (bound & bit) {
176            xkb->server->vmods[i] = XkmGetCARD8(file, &nRead);
177            if (changes)
178                changes->map.vmods |= bit;
179            tmp++;
180        }
181    }
182    if ((i = XkbPaddedSize(tmp) - tmp) > 0)
183        nRead += XkmSkipPadding(file, i);
184    if (XkbAllocNames(xkb, XkbVirtualModNamesMask, 0, 0) != Success) {
185        _XkbLibError(_XkbErrBadAlloc, "ReadXkmVirtualMods", 0);
186        return -1;
187    }
188    for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
189        char name[100];
190
191        if (named & bit) {
192            if (nRead += XkmGetCountedString(file, name, 100)) {
193                xkb->names->vmods[i] = XkbInternAtom(name, FALSE);
194                if (changes)
195                    changes->names.changed_vmods |= bit;
196            }
197        }
198    }
199    return nRead;
200}
201
202/***====================================================================***/
203
204static int
205ReadXkmKeycodes(FILE * file, XkbDescPtr xkb, XkbChangesPtr changes)
206{
207    register int i;
208    unsigned minKC, maxKC, nAl;
209    int nRead = 0;
210    char name[100];
211    XkbKeyNamePtr pN;
212
213    name[0] = '\0';
214    nRead += XkmGetCountedString(file, name, 100);
215    minKC = XkmGetCARD8(file, &nRead);
216    maxKC = XkmGetCARD8(file, &nRead);
217    if (xkb->min_key_code == 0) {
218        xkb->min_key_code = minKC;
219        xkb->max_key_code = maxKC;
220    }
221    else {
222        if (minKC < xkb->min_key_code)
223            xkb->min_key_code = minKC;
224        if (maxKC > xkb->max_key_code) {
225            _XkbLibError(_XkbErrBadValue, "ReadXkmKeycodes", maxKC);
226            return -1;
227        }
228    }
229    nAl = XkmGetCARD8(file, &nRead);
230    nRead += XkmSkipPadding(file, 1);
231
232#define WANTED (XkbKeycodesNameMask|XkbKeyNamesMask|XkbKeyAliasesMask)
233    if (XkbAllocNames(xkb, WANTED, 0, nAl) != Success) {
234        _XkbLibError(_XkbErrBadAlloc, "ReadXkmKeycodes", 0);
235        return -1;
236    }
237    if (name[0] != '\0') {
238        xkb->names->keycodes = XkbInternAtom(name, FALSE);
239    }
240
241    for (pN = &xkb->names->keys[minKC], i = minKC; i <= (int) maxKC; i++, pN++) {
242        if (fread(pN, 1, XkbKeyNameLength, file) != XkbKeyNameLength) {
243            _XkbLibError(_XkbErrBadLength, "ReadXkmKeycodes", 0);
244            return -1;
245        }
246        nRead += XkbKeyNameLength;
247    }
248    if (nAl > 0) {
249        XkbKeyAliasPtr pAl;
250
251        for (pAl = xkb->names->key_aliases, i = 0; i < nAl; i++, pAl++) {
252            int tmp;
253
254            tmp = fread(pAl, 1, 2 * XkbKeyNameLength, file);
255            if (tmp != 2 * XkbKeyNameLength) {
256                _XkbLibError(_XkbErrBadLength, "ReadXkmKeycodes", 0);
257                return -1;
258            }
259            nRead += 2 * XkbKeyNameLength;
260        }
261        if (changes)
262            changes->names.changed |= XkbKeyAliasesMask;
263    }
264    if (changes)
265        changes->names.changed |= XkbKeyNamesMask;
266    return nRead;
267}
268
269/***====================================================================***/
270
271static int
272ReadXkmKeyTypes(FILE * file, XkbDescPtr xkb, XkbChangesPtr changes)
273{
274    register unsigned i, n;
275    unsigned num_types;
276    int nRead = 0;
277    int tmp;
278    XkbKeyTypePtr type;
279    xkmKeyTypeDesc wire;
280    XkbKTMapEntryPtr entry;
281    xkmKTMapEntryDesc wire_entry;
282    char buf[100];
283
284    if ((tmp = XkmGetCountedString(file, buf, 100)) < 1) {
285        _XkbLibError(_XkbErrBadLength, "ReadXkmKeyTypes", 0);
286        return -1;
287    }
288    nRead += tmp;
289    if (buf[0] != '\0') {
290        if (XkbAllocNames(xkb, XkbTypesNameMask, 0, 0) != Success) {
291            _XkbLibError(_XkbErrBadAlloc, "ReadXkmKeyTypes", 0);
292            return -1;
293        }
294        xkb->names->types = XkbInternAtom(buf, FALSE);
295    }
296    num_types = XkmGetCARD16(file, &nRead);
297    nRead += XkmSkipPadding(file, 2);
298    if (num_types < 1)
299        return nRead;
300    if (XkbAllocClientMap(xkb, XkbKeyTypesMask, num_types) != Success) {
301        _XkbLibError(_XkbErrBadAlloc, "ReadXkmKeyTypes", 0);
302        return nRead;
303    }
304    xkb->map->num_types = num_types;
305    if (num_types < XkbNumRequiredTypes) {
306        _XkbLibError(_XkbErrMissingReqTypes, "ReadXkmKeyTypes", 0);
307        return -1;
308    }
309    type = xkb->map->types;
310    for (i = 0; i < num_types; i++, type++) {
311        if ((int) fread(&wire, SIZEOF(xkmKeyTypeDesc), 1, file) < 1) {
312            _XkbLibError(_XkbErrBadLength, "ReadXkmKeyTypes", 0);
313            return -1;
314        }
315        nRead += SIZEOF(xkmKeyTypeDesc);
316        if (((i == XkbOneLevelIndex) && (wire.numLevels != 1)) ||
317            (((i == XkbTwoLevelIndex) || (i == XkbAlphabeticIndex) ||
318              ((i) == XkbKeypadIndex)) && (wire.numLevels != 2))) {
319            _XkbLibError(_XkbErrBadTypeWidth, "ReadXkmKeyTypes", i);
320            return -1;
321        }
322        tmp = wire.nMapEntries;
323        XkmInsureTypedSize(type->map, type->map_count, &tmp, XkbKTMapEntryRec);
324        if ((wire.nMapEntries > 0) && (type->map == NULL)) {
325            _XkbLibError(_XkbErrBadValue, "ReadXkmKeyTypes", wire.nMapEntries);
326            return -1;
327        }
328        for (n = 0, entry = type->map; n < wire.nMapEntries; n++, entry++) {
329            if (fread(&wire_entry, SIZEOF(xkmKTMapEntryDesc), 1, file) <
330                (int) 1) {
331                _XkbLibError(_XkbErrBadLength, "ReadXkmKeyTypes", 0);
332                return -1;
333            }
334            nRead += SIZEOF(xkmKTMapEntryDesc);
335            entry->active = (wire_entry.virtualMods == 0);
336            entry->level = wire_entry.level;
337            entry->mods.mask = wire_entry.realMods;
338            entry->mods.real_mods = wire_entry.realMods;
339            entry->mods.vmods = wire_entry.virtualMods;
340        }
341        nRead += XkmGetCountedString(file, buf, 100);
342        if (((i == XkbOneLevelIndex) && (strcmp(buf, "ONE_LEVEL") != 0)) ||
343            ((i == XkbTwoLevelIndex) && (strcmp(buf, "TWO_LEVEL") != 0)) ||
344            ((i == XkbAlphabeticIndex) && (strcmp(buf, "ALPHABETIC") != 0)) ||
345            ((i == XkbKeypadIndex) && (strcmp(buf, "KEYPAD") != 0))) {
346            _XkbLibError(_XkbErrBadTypeName, "ReadXkmKeyTypes", 0);
347            return -1;
348        }
349        if (buf[0] != '\0') {
350            type->name = XkbInternAtom(buf, FALSE);
351        }
352        else
353            type->name = None;
354
355        if (wire.preserve) {
356            xkmModsDesc p_entry;
357            XkbModsPtr pre;
358
359            XkmInsureTypedSize(type->preserve, type->map_count, &tmp,
360                               XkbModsRec);
361            if (type->preserve == NULL) {
362                _XkbLibError(_XkbErrBadMatch, "ReadXkmKeycodes", 0);
363                return -1;
364            }
365            for (n = 0, pre = type->preserve; n < wire.nMapEntries; n++, pre++) {
366                if (fread(&p_entry, SIZEOF(xkmModsDesc), 1, file) < 1) {
367                    _XkbLibError(_XkbErrBadLength, "ReadXkmKeycodes", 0);
368                    return -1;
369                }
370                nRead += SIZEOF(xkmModsDesc);
371                pre->mask = p_entry.realMods;
372                pre->real_mods = p_entry.realMods;
373                pre->vmods = p_entry.virtualMods;
374            }
375        }
376        if (wire.nLevelNames > 0) {
377            int width = wire.numLevels;
378
379            if (wire.nLevelNames > (unsigned) width) {
380                _XkbLibError(_XkbErrBadMatch, "ReadXkmKeycodes", 0);
381                return -1;
382            }
383            XkmInsureTypedSize(type->level_names, type->num_levels, &width,
384                               Atom);
385            if (type->level_names != NULL) {
386                for (n = 0; n < wire.nLevelNames; n++) {
387                    if ((tmp = XkmGetCountedString(file, buf, 100)) < 1)
388                        return -1;
389                    nRead += tmp;
390                    if (strlen(buf) == 0)
391                        type->level_names[n] = None;
392                    else
393                        type->level_names[n] = XkbInternAtom(buf, 0);
394                }
395            }
396        }
397        type->mods.mask = wire.realMods;
398        type->mods.real_mods = wire.realMods;
399        type->mods.vmods = wire.virtualMods;
400        type->num_levels = wire.numLevels;
401        type->map_count = wire.nMapEntries;
402    }
403    if (changes) {
404        changes->map.changed |= XkbKeyTypesMask;
405        changes->map.first_type = 0;
406        changes->map.num_types = xkb->map->num_types;
407    }
408    return nRead;
409}
410
411/***====================================================================***/
412
413static int
414ReadXkmCompatMap(FILE * file, XkbDescPtr xkb, XkbChangesPtr changes)
415{
416    register int i;
417    unsigned num_si, groups;
418    char name[100];
419    XkbSymInterpretPtr interp;
420    xkmSymInterpretDesc wire;
421    unsigned tmp;
422    int nRead = 0;
423    XkbCompatMapPtr compat;
424    XkbAction *act;
425
426    if ((tmp = XkmGetCountedString(file, name, 100)) < 1) {
427        _XkbLibError(_XkbErrBadLength, "ReadXkmCompatMap", 0);
428        return -1;
429    }
430    nRead += tmp;
431    if (name[0] != '\0') {
432        if (XkbAllocNames(xkb, XkbCompatNameMask, 0, 0) != Success) {
433            _XkbLibError(_XkbErrBadAlloc, "ReadXkmCompatMap", 0);
434            return -1;
435        }
436        xkb->names->compat = XkbInternAtom(name, FALSE);
437    }
438    num_si = XkmGetCARD16(file, &nRead);
439    groups = XkmGetCARD8(file, &nRead);
440    nRead += XkmSkipPadding(file, 1);
441    if (XkbAllocCompatMap(xkb, XkbAllCompatMask, num_si) != Success)
442        return -1;
443    compat = xkb->compat;
444    compat->num_si = 0;
445    interp = compat->sym_interpret;
446    for (i = 0; i < num_si; i++) {
447        tmp = fread(&wire, SIZEOF(xkmSymInterpretDesc), 1, file);
448        nRead += tmp * SIZEOF(xkmSymInterpretDesc);
449        interp->sym = wire.sym;
450        interp->mods = wire.mods;
451        interp->match = wire.match;
452        interp->virtual_mod = wire.virtualMod;
453        interp->flags = wire.flags;
454        interp->act.type = wire.actionType;
455        act = (XkbAction *) &interp->act;
456
457        switch (interp->act.type) {
458        case XkbSA_SetMods:
459        case XkbSA_LatchMods:
460        case XkbSA_LockMods:
461            act->mods.flags = wire.actionData[0];
462            act->mods.mask = wire.actionData[1];
463            act->mods.real_mods = wire.actionData[2];
464            act->mods.vmods1 = wire.actionData[3];
465            act->mods.vmods2 = wire.actionData[4];
466            break;
467        case XkbSA_SetGroup:
468        case XkbSA_LatchGroup:
469        case XkbSA_LockGroup:
470            act->group.flags = wire.actionData[0];
471            act->group.group_XXX = wire.actionData[1];
472            break;
473        case XkbSA_MovePtr:
474            act->ptr.flags = wire.actionData[0];
475            act->ptr.high_XXX = wire.actionData[1];
476            act->ptr.low_XXX = wire.actionData[2];
477            act->ptr.high_YYY = wire.actionData[3];
478            act->ptr.low_YYY = wire.actionData[4];
479            break;
480        case XkbSA_PtrBtn:
481        case XkbSA_LockPtrBtn:
482            act->btn.flags = wire.actionData[0];
483            act->btn.count = wire.actionData[1];
484            act->btn.button = wire.actionData[2];
485            break;
486        case XkbSA_DeviceBtn:
487        case XkbSA_LockDeviceBtn:
488            act->devbtn.flags = wire.actionData[0];
489            act->devbtn.count = wire.actionData[1];
490            act->devbtn.button = wire.actionData[2];
491            act->devbtn.device = wire.actionData[3];
492            break;
493        case XkbSA_SetPtrDflt:
494            act->dflt.flags = wire.actionData[0];
495            act->dflt.affect = wire.actionData[1];
496            act->dflt.valueXXX = wire.actionData[2];
497            break;
498        case XkbSA_ISOLock:
499            act->iso.flags = wire.actionData[0];
500            act->iso.mask = wire.actionData[1];
501            act->iso.real_mods = wire.actionData[2];
502            act->iso.group_XXX = wire.actionData[3];
503            act->iso.affect = wire.actionData[4];
504            act->iso.vmods1 = wire.actionData[5];
505            act->iso.vmods2 = wire.actionData[6];
506            break;
507        case XkbSA_SwitchScreen:
508            act->screen.flags = wire.actionData[0];
509            act->screen.screenXXX = wire.actionData[1];
510            break;
511        case XkbSA_SetControls:
512        case XkbSA_LockControls:
513            act->ctrls.flags = wire.actionData[0];
514            act->ctrls.ctrls3 = wire.actionData[1];
515            act->ctrls.ctrls2 = wire.actionData[2];
516            act->ctrls.ctrls1 = wire.actionData[3];
517            act->ctrls.ctrls0 = wire.actionData[4];
518            break;
519        case XkbSA_RedirectKey:
520            act->redirect.new_key = wire.actionData[0];
521            act->redirect.mods_mask = wire.actionData[1];
522            act->redirect.mods = wire.actionData[2];
523            act->redirect.vmods_mask0 = wire.actionData[3];
524            act->redirect.vmods_mask1 = wire.actionData[4];
525            act->redirect.vmods0 = wire.actionData[4];
526            act->redirect.vmods1 = wire.actionData[5];
527            break;
528        case XkbSA_DeviceValuator:
529            act->devval.device = wire.actionData[0];
530            act->devval.v1_what = wire.actionData[1];
531            act->devval.v1_ndx = wire.actionData[2];
532            act->devval.v1_value = wire.actionData[3];
533            act->devval.v2_what = wire.actionData[4];
534            act->devval.v2_ndx = wire.actionData[5];
535            act->devval.v2_value = wire.actionData[6];
536            break;
537
538        case XkbSA_XFree86Private:
539            /*
540             * Bugfix for broken xkbcomp: if we encounter an XFree86Private
541             * action with Any+AnyOfOrNone(All), then we skip the interp as
542             * broken.  Versions of xkbcomp below 1.2.2 had a bug where they
543             * would interpret a symbol that couldn't be found in an interpret
544             * as Any.  So, an XF86LogWindowTree+AnyOfOrNone(All) interp that
545             * triggered the PrWins action would make every key without an
546             * action trigger PrWins if libX11 didn't yet know about the
547             * XF86LogWindowTree keysym.  None too useful.
548             *
549             * We only do this for XFree86 actions, as the current XKB
550             * dataset relies on Any+AnyOfOrNone(All) -> SetMods for Ctrl in
551             * particular.
552             *
553             * See xkbcomp commits 2a473b906943ffd807ad81960c47530ee7ae9a60 and
554             * 3caab5aa37decb7b5dc1642a0452efc3e1f5100e for more details.
555             */
556            if (interp->sym == NoSymbol && interp->match == XkbSI_AnyOfOrNone &&
557                (interp->mods & 0xff) == 0xff) {
558                ErrorF("XKB: Skipping broken Any+AnyOfOrNone(All) -> Private "
559                       "action from compiled keymap\n");
560                continue;
561            }
562            /* copy the kind of action */
563            memcpy(act->any.data, wire.actionData, XkbAnyActionDataSize);
564            break;
565
566        case XkbSA_Terminate:
567            /* no args, kinda (note: untrue for xfree86). */
568            break;
569        case XkbSA_ActionMessage:
570            /* unsupported. */
571            break;
572        }
573        interp++;
574        compat->num_si++;
575    }
576    if ((num_si > 0) && (changes)) {
577        changes->compat.first_si = 0;
578        changes->compat.num_si = compat->num_si;
579    }
580    if (groups) {
581        register unsigned bit;
582
583        for (i = 0, bit = 1; i < XkbNumKbdGroups; i++, bit <<= 1) {
584            xkmModsDesc md;
585
586            if (groups & bit) {
587                tmp = fread(&md, SIZEOF(xkmModsDesc), 1, file);
588                nRead += tmp * SIZEOF(xkmModsDesc);
589                xkb->compat->groups[i].real_mods = md.realMods;
590                xkb->compat->groups[i].vmods = md.virtualMods;
591                if (md.virtualMods != 0) {
592                    unsigned mask;
593
594                    if (XkbVirtualModsToReal(xkb, md.virtualMods, &mask))
595                        xkb->compat->groups[i].mask = md.realMods | mask;
596                }
597                else
598                    xkb->compat->groups[i].mask = md.realMods;
599            }
600        }
601        if (changes)
602            changes->compat.changed_groups |= groups;
603    }
604    return nRead;
605}
606
607static int
608ReadXkmIndicators(FILE * file, XkbDescPtr xkb, XkbChangesPtr changes)
609{
610    register unsigned nLEDs;
611    xkmIndicatorMapDesc wire;
612    char buf[100];
613    unsigned tmp;
614    int nRead = 0;
615
616    if ((xkb->indicators == NULL) && (XkbAllocIndicatorMaps(xkb) != Success)) {
617        _XkbLibError(_XkbErrBadAlloc, "indicator rec", 0);
618        return -1;
619    }
620    if (XkbAllocNames(xkb, XkbIndicatorNamesMask, 0, 0) != Success) {
621        _XkbLibError(_XkbErrBadAlloc, "indicator names", 0);
622        return -1;
623    }
624    nLEDs = XkmGetCARD8(file, &nRead);
625    nRead += XkmSkipPadding(file, 3);
626    xkb->indicators->phys_indicators = XkmGetCARD32(file, &nRead);
627    while (nLEDs-- > 0) {
628        Atom name;
629        XkbIndicatorMapPtr map;
630
631        if ((tmp = XkmGetCountedString(file, buf, 100)) < 1) {
632            _XkbLibError(_XkbErrBadLength, "ReadXkmIndicators", 0);
633            return -1;
634        }
635        nRead += tmp;
636        if (buf[0] != '\0')
637            name = XkbInternAtom(buf, FALSE);
638        else
639            name = None;
640        if ((tmp = fread(&wire, SIZEOF(xkmIndicatorMapDesc), 1, file)) < 1) {
641            _XkbLibError(_XkbErrBadLength, "ReadXkmIndicators", 0);
642            return -1;
643        }
644        nRead += tmp * SIZEOF(xkmIndicatorMapDesc);
645        if (xkb->names) {
646            xkb->names->indicators[wire.indicator - 1] = name;
647            if (changes)
648                changes->names.changed_indicators |=
649                    (1 << (wire.indicator - 1));
650        }
651        map = &xkb->indicators->maps[wire.indicator - 1];
652        map->flags = wire.flags;
653        map->which_groups = wire.which_groups;
654        map->groups = wire.groups;
655        map->which_mods = wire.which_mods;
656        map->mods.mask = wire.real_mods;
657        map->mods.real_mods = wire.real_mods;
658        map->mods.vmods = wire.vmods;
659        map->ctrls = wire.ctrls;
660    }
661    return nRead;
662}
663
664static XkbKeyTypePtr
665FindTypeForKey(XkbDescPtr xkb, Atom name, unsigned width, KeySym * syms)
666{
667    if ((!xkb) || (!xkb->map))
668        return NULL;
669    if (name != None) {
670        register unsigned i;
671
672        for (i = 0; i < xkb->map->num_types; i++) {
673            if (xkb->map->types[i].name == name) {
674                if (xkb->map->types[i].num_levels != width)
675                    DebugF("Group width mismatch between key and type\n");
676                return &xkb->map->types[i];
677            }
678        }
679    }
680    if ((width < 2) || ((syms != NULL) && (syms[1] == NoSymbol)))
681        return &xkb->map->types[XkbOneLevelIndex];
682    if (syms != NULL) {
683        if (XkbKSIsLower(syms[0]) && XkbKSIsUpper(syms[1]))
684            return &xkb->map->types[XkbAlphabeticIndex];
685        else if (XkbKSIsKeypad(syms[0]) || XkbKSIsKeypad(syms[1]))
686            return &xkb->map->types[XkbKeypadIndex];
687    }
688    return &xkb->map->types[XkbTwoLevelIndex];
689}
690
691static int
692ReadXkmSymbols(FILE * file, XkbDescPtr xkb)
693{
694    register int i, g, s, totalVModMaps;
695    xkmKeySymMapDesc wireMap;
696    char buf[100];
697    unsigned minKC, maxKC, groupNames, tmp;
698    int nRead = 0;
699
700    if ((tmp = XkmGetCountedString(file, buf, 100)) < 1)
701        return -1;
702    nRead += tmp;
703    minKC = XkmGetCARD8(file, &nRead);
704    maxKC = XkmGetCARD8(file, &nRead);
705    groupNames = XkmGetCARD8(file, &nRead);
706    totalVModMaps = XkmGetCARD8(file, &nRead);
707    if (XkbAllocNames(xkb,
708                      XkbSymbolsNameMask | XkbPhysSymbolsNameMask |
709                      XkbGroupNamesMask, 0, 0) != Success) {
710        _XkbLibError(_XkbErrBadAlloc, "physical names", 0);
711        return -1;
712    }
713    if ((buf[0] != '\0') && (xkb->names)) {
714        Atom name;
715
716        name = XkbInternAtom(buf, 0);
717        xkb->names->symbols = name;
718        xkb->names->phys_symbols = name;
719    }
720    for (i = 0, g = 1; i < XkbNumKbdGroups; i++, g <<= 1) {
721        if (groupNames & g) {
722            if ((tmp = XkmGetCountedString(file, buf, 100)) < 1)
723                return -1;
724            nRead += tmp;
725
726            if (!xkb->names)
727                continue;
728
729            if (buf[0] != '\0') {
730                Atom name;
731
732                name = XkbInternAtom(buf, 0);
733                xkb->names->groups[i] = name;
734            }
735            else
736                xkb->names->groups[i] = None;
737        }
738    }
739    if (XkbAllocServerMap(xkb, XkbAllServerInfoMask, 0) != Success) {
740        _XkbLibError(_XkbErrBadAlloc, "server map", 0);
741        return -1;
742    }
743    if (XkbAllocClientMap(xkb, XkbAllClientInfoMask, 0) != Success) {
744        _XkbLibError(_XkbErrBadAlloc, "client map", 0);
745        return -1;
746    }
747    if (XkbAllocControls(xkb, XkbAllControlsMask) != Success) {
748        _XkbLibError(_XkbErrBadAlloc, "controls", 0);
749        return -1;
750    }
751    if ((xkb->map == NULL) || (xkb->server == NULL))
752        return -1;
753    if (xkb->min_key_code < 8)
754        xkb->min_key_code = minKC;
755    if (xkb->max_key_code < 8)
756        xkb->max_key_code = maxKC;
757    if ((minKC >= 8) && (minKC < xkb->min_key_code))
758        xkb->min_key_code = minKC;
759    if ((maxKC >= 8) && (maxKC > xkb->max_key_code)) {
760        _XkbLibError(_XkbErrBadValue, "keys in symbol map", maxKC);
761        return -1;
762    }
763    for (i = minKC; i <= (int) maxKC; i++) {
764        Atom typeName[XkbNumKbdGroups];
765        XkbKeyTypePtr type[XkbNumKbdGroups];
766
767        if ((tmp = fread(&wireMap, SIZEOF(xkmKeySymMapDesc), 1, file)) < 1) {
768            _XkbLibError(_XkbErrBadLength, "ReadXkmSymbols", 0);
769            return -1;
770        }
771        nRead += tmp * SIZEOF(xkmKeySymMapDesc);
772        memset((char *) typeName, 0, XkbNumKbdGroups * sizeof(Atom));
773        memset((char *) type, 0, XkbNumKbdGroups * sizeof(XkbKeyTypePtr));
774        if (wireMap.flags & XkmKeyHasTypes) {
775            for (g = 0; g < XkbNumKbdGroups; g++) {
776                if ((wireMap.flags & (1 << g)) &&
777                    ((tmp = XkmGetCountedString(file, buf, 100)) > 0)) {
778                    typeName[g] = XkbInternAtom(buf, 1);
779                    nRead += tmp;
780                }
781                type[g] = FindTypeForKey(xkb, typeName[g], wireMap.width, NULL);
782                if (type[g] == NULL) {
783                    _XkbLibError(_XkbErrMissingTypes, "ReadXkmSymbols", 0);
784                    return -1;
785                }
786                if (typeName[g] == type[g]->name)
787                    xkb->server->explicit[i] |= (1 << g);
788            }
789        }
790        if (wireMap.flags & XkmRepeatingKey) {
791            xkb->ctrls->per_key_repeat[i / 8] |= (1 << (i % 8));
792            xkb->server->explicit[i] |= XkbExplicitAutoRepeatMask;
793        }
794        else if (wireMap.flags & XkmNonRepeatingKey) {
795            xkb->ctrls->per_key_repeat[i / 8] &= ~(1 << (i % 8));
796            xkb->server->explicit[i] |= XkbExplicitAutoRepeatMask;
797        }
798        xkb->map->modmap[i] = wireMap.modifier_map;
799        if (XkbNumGroups(wireMap.num_groups) > 0) {
800            KeySym *sym;
801            int nSyms;
802
803            if (XkbNumGroups(wireMap.num_groups) > xkb->ctrls->num_groups)
804                xkb->ctrls->num_groups = wireMap.num_groups;
805            nSyms = XkbNumGroups(wireMap.num_groups) * wireMap.width;
806            sym = XkbResizeKeySyms(xkb, i, nSyms);
807            if (!sym)
808                return -1;
809            for (s = 0; s < nSyms; s++) {
810                *sym++ = XkmGetCARD32(file, &nRead);
811            }
812            if (wireMap.flags & XkmKeyHasActions) {
813                XkbAction *act;
814
815                act = XkbResizeKeyActions(xkb, i, nSyms);
816                for (s = 0; s < nSyms; s++, act++) {
817                    tmp = fread(act, SIZEOF(xkmActionDesc), 1, file);
818                    nRead += tmp * SIZEOF(xkmActionDesc);
819                }
820                xkb->server->explicit[i] |= XkbExplicitInterpretMask;
821            }
822        }
823        for (g = 0; g < XkbNumGroups(wireMap.num_groups); g++) {
824            if (((xkb->server->explicit[i] & (1 << g)) == 0) ||
825                (type[g] == NULL)) {
826                KeySym *tmpSyms;
827
828                tmpSyms = XkbKeySymsPtr(xkb, i) + (wireMap.width * g);
829                type[g] = FindTypeForKey(xkb, None, wireMap.width, tmpSyms);
830            }
831            xkb->map->key_sym_map[i].kt_index[g] =
832                type[g] - (&xkb->map->types[0]);
833        }
834        xkb->map->key_sym_map[i].group_info = wireMap.num_groups;
835        xkb->map->key_sym_map[i].width = wireMap.width;
836        if (wireMap.flags & XkmKeyHasBehavior) {
837            xkmBehaviorDesc b;
838
839            tmp = fread(&b, SIZEOF(xkmBehaviorDesc), 1, file);
840            nRead += tmp * SIZEOF(xkmBehaviorDesc);
841            xkb->server->behaviors[i].type = b.type;
842            xkb->server->behaviors[i].data = b.data;
843            xkb->server->explicit[i] |= XkbExplicitBehaviorMask;
844        }
845    }
846    if (totalVModMaps > 0) {
847        xkmVModMapDesc v;
848
849        for (i = 0; i < totalVModMaps; i++) {
850            tmp = fread(&v, SIZEOF(xkmVModMapDesc), 1, file);
851            nRead += tmp * SIZEOF(xkmVModMapDesc);
852            if (tmp > 0)
853                xkb->server->vmodmap[v.key] = v.vmods;
854        }
855    }
856    return nRead;
857}
858
859static int
860ReadXkmGeomDoodad(FILE * file, XkbGeometryPtr geom, XkbSectionPtr section)
861{
862    XkbDoodadPtr doodad;
863    xkmDoodadDesc doodadWire;
864    char buf[100];
865    unsigned tmp;
866    int nRead = 0;
867
868    nRead += XkmGetCountedString(file, buf, 100);
869    tmp = fread(&doodadWire, SIZEOF(xkmDoodadDesc), 1, file);
870    nRead += SIZEOF(xkmDoodadDesc) * tmp;
871    doodad = XkbAddGeomDoodad(geom, section, XkbInternAtom(buf, FALSE));
872    if (!doodad)
873        return nRead;
874    doodad->any.type = doodadWire.any.type;
875    doodad->any.priority = doodadWire.any.priority;
876    doodad->any.top = doodadWire.any.top;
877    doodad->any.left = doodadWire.any.left;
878    switch (doodadWire.any.type) {
879    case XkbOutlineDoodad:
880    case XkbSolidDoodad:
881        doodad->shape.angle = doodadWire.shape.angle;
882        doodad->shape.color_ndx = doodadWire.shape.color_ndx;
883        doodad->shape.shape_ndx = doodadWire.shape.shape_ndx;
884        break;
885    case XkbTextDoodad:
886        doodad->text.angle = doodadWire.text.angle;
887        doodad->text.width = doodadWire.text.width;
888        doodad->text.height = doodadWire.text.height;
889        doodad->text.color_ndx = doodadWire.text.color_ndx;
890        nRead += XkmGetCountedString(file, buf, 100);
891        doodad->text.text = Xstrdup(buf);
892        nRead += XkmGetCountedString(file, buf, 100);
893        doodad->text.font = Xstrdup(buf);
894        break;
895    case XkbIndicatorDoodad:
896        doodad->indicator.shape_ndx = doodadWire.indicator.shape_ndx;
897        doodad->indicator.on_color_ndx = doodadWire.indicator.on_color_ndx;
898        doodad->indicator.off_color_ndx = doodadWire.indicator.off_color_ndx;
899        break;
900    case XkbLogoDoodad:
901        doodad->logo.angle = doodadWire.logo.angle;
902        doodad->logo.color_ndx = doodadWire.logo.color_ndx;
903        doodad->logo.shape_ndx = doodadWire.logo.shape_ndx;
904        nRead += XkmGetCountedString(file, buf, 100);
905        doodad->logo.logo_name = Xstrdup(buf);
906        break;
907    default:
908        /* report error? */
909        return nRead;
910    }
911    return nRead;
912}
913
914static int
915ReadXkmGeomOverlay(FILE * file, XkbGeometryPtr geom, XkbSectionPtr section)
916{
917    char buf[100];
918    unsigned tmp;
919    int nRead = 0;
920    XkbOverlayPtr ol;
921    XkbOverlayRowPtr row;
922    xkmOverlayDesc olWire;
923    xkmOverlayRowDesc rowWire;
924    register int r;
925
926    nRead += XkmGetCountedString(file, buf, 100);
927    tmp = fread(&olWire, SIZEOF(xkmOverlayDesc), 1, file);
928    nRead += tmp * SIZEOF(xkmOverlayDesc);
929    ol = XkbAddGeomOverlay(section, XkbInternAtom(buf, FALSE), olWire.num_rows);
930    if (!ol)
931        return nRead;
932    for (r = 0; r < olWire.num_rows; r++) {
933        int k;
934        xkmOverlayKeyDesc keyWire;
935
936        tmp = fread(&rowWire, SIZEOF(xkmOverlayRowDesc), 1, file);
937        nRead += tmp * SIZEOF(xkmOverlayRowDesc);
938        row = XkbAddGeomOverlayRow(ol, rowWire.row_under, rowWire.num_keys);
939        if (!row) {
940            _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeomOverlay", 0);
941            return nRead;
942        }
943        for (k = 0; k < rowWire.num_keys; k++) {
944            tmp = fread(&keyWire, SIZEOF(xkmOverlayKeyDesc), 1, file);
945            nRead += tmp * SIZEOF(xkmOverlayKeyDesc);
946            memcpy(row->keys[k].over.name, keyWire.over, XkbKeyNameLength);
947            memcpy(row->keys[k].under.name, keyWire.under, XkbKeyNameLength);
948        }
949        row->num_keys = rowWire.num_keys;
950    }
951    return nRead;
952}
953
954static int
955ReadXkmGeomSection(FILE * file, XkbGeometryPtr geom)
956{
957    register int i;
958    XkbSectionPtr section;
959    xkmSectionDesc sectionWire;
960    unsigned tmp;
961    int nRead = 0;
962    char buf[100];
963    Atom nameAtom;
964
965    nRead += XkmGetCountedString(file, buf, 100);
966    nameAtom = XkbInternAtom(buf, FALSE);
967    tmp = fread(&sectionWire, SIZEOF(xkmSectionDesc), 1, file);
968    nRead += SIZEOF(xkmSectionDesc) * tmp;
969    section = XkbAddGeomSection(geom, nameAtom, sectionWire.num_rows,
970                                sectionWire.num_doodads,
971                                sectionWire.num_overlays);
972    if (!section) {
973        _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeomSection", 0);
974        return nRead;
975    }
976    section->top = sectionWire.top;
977    section->left = sectionWire.left;
978    section->width = sectionWire.width;
979    section->height = sectionWire.height;
980    section->angle = sectionWire.angle;
981    section->priority = sectionWire.priority;
982    if (sectionWire.num_rows > 0) {
983        register int k;
984        XkbRowPtr row;
985        xkmRowDesc rowWire;
986        XkbKeyPtr key;
987        xkmKeyDesc keyWire;
988
989        for (i = 0; i < sectionWire.num_rows; i++) {
990            tmp = fread(&rowWire, SIZEOF(xkmRowDesc), 1, file);
991            nRead += SIZEOF(xkmRowDesc) * tmp;
992            row = XkbAddGeomRow(section, rowWire.num_keys);
993            if (!row) {
994                _XkbLibError(_XkbErrBadAlloc, "ReadXkmKeycodes", 0);
995                return nRead;
996            }
997            row->top = rowWire.top;
998            row->left = rowWire.left;
999            row->vertical = rowWire.vertical;
1000            for (k = 0; k < rowWire.num_keys; k++) {
1001                tmp = fread(&keyWire, SIZEOF(xkmKeyDesc), 1, file);
1002                nRead += SIZEOF(xkmKeyDesc) * tmp;
1003                key = XkbAddGeomKey(row);
1004                if (!key) {
1005                    _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeomSection", 0);
1006                    return nRead;
1007                }
1008                memcpy(key->name.name, keyWire.name, XkbKeyNameLength);
1009                key->gap = keyWire.gap;
1010                key->shape_ndx = keyWire.shape_ndx;
1011                key->color_ndx = keyWire.color_ndx;
1012            }
1013        }
1014    }
1015    if (sectionWire.num_doodads > 0) {
1016        for (i = 0; i < sectionWire.num_doodads; i++) {
1017            tmp = ReadXkmGeomDoodad(file, geom, section);
1018            nRead += tmp;
1019            if (tmp < 1)
1020                return nRead;
1021        }
1022    }
1023    if (sectionWire.num_overlays > 0) {
1024        for (i = 0; i < sectionWire.num_overlays; i++) {
1025            tmp = ReadXkmGeomOverlay(file, geom, section);
1026            nRead += tmp;
1027            if (tmp < 1)
1028                return nRead;
1029        }
1030    }
1031    return nRead;
1032}
1033
1034static int
1035ReadXkmGeometry(FILE * file, XkbDescPtr xkb)
1036{
1037    register int i;
1038    char buf[100];
1039    unsigned tmp;
1040    int nRead = 0;
1041    xkmGeometryDesc wireGeom;
1042    XkbGeometryPtr geom;
1043    XkbGeometrySizesRec sizes;
1044
1045    nRead += XkmGetCountedString(file, buf, 100);
1046    tmp = fread(&wireGeom, SIZEOF(xkmGeometryDesc), 1, file);
1047    nRead += tmp * SIZEOF(xkmGeometryDesc);
1048    sizes.which = XkbGeomAllMask;
1049    sizes.num_properties = wireGeom.num_properties;
1050    sizes.num_colors = wireGeom.num_colors;
1051    sizes.num_shapes = wireGeom.num_shapes;
1052    sizes.num_sections = wireGeom.num_sections;
1053    sizes.num_doodads = wireGeom.num_doodads;
1054    sizes.num_key_aliases = wireGeom.num_key_aliases;
1055    if (XkbAllocGeometry(xkb, &sizes) != Success) {
1056        _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeometry", 0);
1057        return nRead;
1058    }
1059    geom = xkb->geom;
1060    geom->name = XkbInternAtom(buf, FALSE);
1061    geom->width_mm = wireGeom.width_mm;
1062    geom->height_mm = wireGeom.height_mm;
1063    nRead += XkmGetCountedString(file, buf, 100);
1064    geom->label_font = Xstrdup(buf);
1065    if (wireGeom.num_properties > 0) {
1066        char val[1024];
1067
1068        for (i = 0; i < wireGeom.num_properties; i++) {
1069            nRead += XkmGetCountedString(file, buf, 100);
1070            nRead += XkmGetCountedString(file, val, 1024);
1071            if (XkbAddGeomProperty(geom, buf, val) == NULL) {
1072                _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeometry", 0);
1073                return nRead;
1074            }
1075        }
1076    }
1077    if (wireGeom.num_colors > 0) {
1078        for (i = 0; i < wireGeom.num_colors; i++) {
1079            nRead += XkmGetCountedString(file, buf, 100);
1080            if (XkbAddGeomColor(geom, buf, i) == NULL) {
1081                _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeometry", 0);
1082                return nRead;
1083            }
1084        }
1085    }
1086    geom->base_color = &geom->colors[wireGeom.base_color_ndx];
1087    geom->label_color = &geom->colors[wireGeom.label_color_ndx];
1088    if (wireGeom.num_shapes > 0) {
1089        XkbShapePtr shape;
1090        xkmShapeDesc shapeWire;
1091        Atom nameAtom;
1092
1093        for (i = 0; i < wireGeom.num_shapes; i++) {
1094            register int n;
1095            XkbOutlinePtr ol;
1096            xkmOutlineDesc olWire;
1097
1098            nRead += XkmGetCountedString(file, buf, 100);
1099            nameAtom = XkbInternAtom(buf, FALSE);
1100            tmp = fread(&shapeWire, SIZEOF(xkmShapeDesc), 1, file);
1101            nRead += tmp * SIZEOF(xkmShapeDesc);
1102            shape = XkbAddGeomShape(geom, nameAtom, shapeWire.num_outlines);
1103            if (!shape) {
1104                _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeometry", 0);
1105                return nRead;
1106            }
1107            for (n = 0; n < shapeWire.num_outlines; n++) {
1108                register int p;
1109                xkmPointDesc ptWire;
1110
1111                tmp = fread(&olWire, SIZEOF(xkmOutlineDesc), 1, file);
1112                nRead += tmp * SIZEOF(xkmOutlineDesc);
1113                ol = XkbAddGeomOutline(shape, olWire.num_points);
1114                if (!ol) {
1115                    _XkbLibError(_XkbErrBadAlloc, "ReadXkmGeometry", 0);
1116                    return nRead;
1117                }
1118                ol->num_points = olWire.num_points;
1119                ol->corner_radius = olWire.corner_radius;
1120                for (p = 0; p < olWire.num_points; p++) {
1121                    tmp = fread(&ptWire, SIZEOF(xkmPointDesc), 1, file);
1122                    nRead += tmp * SIZEOF(xkmPointDesc);
1123                    ol->points[p].x = ptWire.x;
1124                    ol->points[p].y = ptWire.y;
1125                    if (ptWire.x < shape->bounds.x1)
1126                        shape->bounds.x1 = ptWire.x;
1127                    if (ptWire.x > shape->bounds.x2)
1128                        shape->bounds.x2 = ptWire.x;
1129                    if (ptWire.y < shape->bounds.y1)
1130                        shape->bounds.y1 = ptWire.y;
1131                    if (ptWire.y > shape->bounds.y2)
1132                        shape->bounds.y2 = ptWire.y;
1133                }
1134            }
1135            if (shapeWire.primary_ndx != XkbNoShape)
1136                shape->primary = &shape->outlines[shapeWire.primary_ndx];
1137            if (shapeWire.approx_ndx != XkbNoShape)
1138                shape->approx = &shape->outlines[shapeWire.approx_ndx];
1139        }
1140    }
1141    if (wireGeom.num_sections > 0) {
1142        for (i = 0; i < wireGeom.num_sections; i++) {
1143            tmp = ReadXkmGeomSection(file, geom);
1144            nRead += tmp;
1145            if (tmp == 0)
1146                return nRead;
1147        }
1148    }
1149    if (wireGeom.num_doodads > 0) {
1150        for (i = 0; i < wireGeom.num_doodads; i++) {
1151            tmp = ReadXkmGeomDoodad(file, geom, NULL);
1152            nRead += tmp;
1153            if (tmp == 0)
1154                return nRead;
1155        }
1156    }
1157    if ((wireGeom.num_key_aliases > 0) && (geom->key_aliases)) {
1158        int sz = XkbKeyNameLength * 2;
1159        int num = wireGeom.num_key_aliases;
1160
1161        if (fread(geom->key_aliases, sz, num, file) != num) {
1162            _XkbLibError(_XkbErrBadLength, "ReadXkmGeometry", 0);
1163            return -1;
1164        }
1165        nRead += (num * sz);
1166        geom->num_key_aliases = num;
1167    }
1168    return nRead;
1169}
1170
1171Bool
1172XkmProbe(FILE * file)
1173{
1174    unsigned hdr, tmp;
1175    int nRead = 0;
1176
1177    hdr = (('x' << 24) | ('k' << 16) | ('m' << 8) | XkmFileVersion);
1178    tmp = XkmGetCARD32(file, &nRead);
1179    if (tmp != hdr) {
1180        if ((tmp & (~0xff)) == (hdr & (~0xff))) {
1181            _XkbLibError(_XkbErrBadFileVersion, "XkmProbe", tmp & 0xff);
1182        }
1183        return 0;
1184    }
1185    return 1;
1186}
1187
1188static Bool
1189XkmReadTOC(FILE * file, xkmFileInfo * file_info, int max_toc,
1190           xkmSectionInfo * toc)
1191{
1192    unsigned hdr, tmp;
1193    int nRead = 0;
1194    unsigned i, size_toc;
1195
1196    hdr = (('x' << 24) | ('k' << 16) | ('m' << 8) | XkmFileVersion);
1197    tmp = XkmGetCARD32(file, &nRead);
1198    if (tmp != hdr) {
1199        if ((tmp & (~0xff)) == (hdr & (~0xff))) {
1200            _XkbLibError(_XkbErrBadFileVersion, "XkmReadTOC", tmp & 0xff);
1201        }
1202        else {
1203            _XkbLibError(_XkbErrBadFileType, "XkmReadTOC", tmp);
1204        }
1205        return 0;
1206    }
1207    if (fread(file_info, SIZEOF(xkmFileInfo), 1, file) != 1)
1208        return 0;
1209    size_toc = file_info->num_toc;
1210    if (size_toc > max_toc) {
1211        DebugF("Warning! Too many TOC entries; last %d ignored\n",
1212               size_toc - max_toc);
1213        size_toc = max_toc;
1214    }
1215    for (i = 0; i < size_toc; i++) {
1216        if (fread(&toc[i], SIZEOF(xkmSectionInfo), 1, file) != 1)
1217            return 0;
1218    }
1219    return 1;
1220}
1221
1222/***====================================================================***/
1223
1224#define	MAX_TOC	16
1225unsigned
1226XkmReadFile(FILE * file, unsigned need, unsigned want, XkbDescPtr *xkb)
1227{
1228    register unsigned i;
1229    xkmSectionInfo toc[MAX_TOC], tmpTOC;
1230    xkmFileInfo fileInfo;
1231    unsigned tmp, nRead = 0;
1232    unsigned which = need | want;
1233
1234    if (!XkmReadTOC(file, &fileInfo, MAX_TOC, toc))
1235        return which;
1236    if ((fileInfo.present & need) != need) {
1237        _XkbLibError(_XkbErrIllegalContents, "XkmReadFile",
1238                     need & (~fileInfo.present));
1239        return which;
1240    }
1241    if (*xkb == NULL)
1242        *xkb = XkbAllocKeyboard();
1243    for (i = 0; i < fileInfo.num_toc; i++) {
1244        fseek(file, toc[i].offset, SEEK_SET);
1245        tmp = fread(&tmpTOC, SIZEOF(xkmSectionInfo), 1, file);
1246        nRead = tmp * SIZEOF(xkmSectionInfo);
1247        if ((tmpTOC.type != toc[i].type) || (tmpTOC.format != toc[i].format) ||
1248            (tmpTOC.size != toc[i].size) || (tmpTOC.offset != toc[i].offset)) {
1249            return which;
1250        }
1251        if ((which & (1 << tmpTOC.type)) == 0) {
1252            continue;
1253        }
1254        switch (tmpTOC.type) {
1255        case XkmVirtualModsIndex:
1256            tmp = ReadXkmVirtualMods(file, *xkb, NULL);
1257            break;
1258        case XkmTypesIndex:
1259            tmp = ReadXkmKeyTypes(file, *xkb, NULL);
1260            break;
1261        case XkmCompatMapIndex:
1262            tmp = ReadXkmCompatMap(file, *xkb, NULL);
1263            break;
1264        case XkmKeyNamesIndex:
1265            tmp = ReadXkmKeycodes(file, *xkb, NULL);
1266            break;
1267        case XkmIndicatorsIndex:
1268            tmp = ReadXkmIndicators(file, *xkb, NULL);
1269            break;
1270        case XkmSymbolsIndex:
1271            tmp = ReadXkmSymbols(file, *xkb);
1272            break;
1273        case XkmGeometryIndex:
1274            tmp = ReadXkmGeometry(file, *xkb);
1275            break;
1276        default:
1277            _XkbLibError(_XkbErrBadImplementation,
1278                         XkbConfigText(tmpTOC.type, XkbMessage), 0);
1279            tmp = 0;
1280            break;
1281        }
1282        if (tmp > 0) {
1283            nRead += tmp;
1284            which &= ~(1 << toc[i].type);
1285            (*xkb)->defined |= (1 << toc[i].type);
1286        }
1287        if (nRead != tmpTOC.size) {
1288            _XkbLibError(_XkbErrBadLength,
1289                         XkbConfigText(tmpTOC.type, XkbMessage),
1290                         nRead - tmpTOC.size);
1291        }
1292    }
1293    return which;
1294}
1295