xkbconfig.c revision 70728a38
1/************************************************************
2 Copyright (c) 1995 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#elif defined(HAVE_CONFIG_H)
30#include <config.h>
31#endif
32
33#include <stdio.h>
34#include <ctype.h>
35#include <stdlib.h>
36
37#include <X11/Xfuncs.h>
38
39#include <X11/Xfuncs.h>
40
41
42#include <X11/Xos.h>
43#include <X11/Xlib.h>
44#include <X11/keysym.h>
45#include <X11/XKBlib.h>
46#include "XKBfileInt.h"
47
48
49#include <X11/extensions/XKBconfig.h>
50
51/***====================================================================***/
52
53#define	XKBCF_MAX_STR_LEN	100
54static char _XkbCF_rtrn[XKBCF_MAX_STR_LEN + 1];
55
56static int
57ScanIdent(FILE *file, int ch, XkbCFScanResultPtr val_rtrn)
58{
59    register int i;
60    char *str;
61
62    val_rtrn->str = str = _XkbCF_rtrn;
63    for (i = 0; (isalpha(ch) || isdigit(ch) || (ch == '_')); ch = getc(file)) {
64        if (i < XKBCF_MAX_STR_LEN)
65            str[i++] = ch;
66    }
67    if ((ch != EOF) && (ch != ' ') && (ch != '\t'))
68        ungetc(ch, file);
69    str[i] = '\0';
70    return XkbCF_Ident;
71}
72
73static int
74ScanString(FILE *file, int quote, XkbCFScanResultPtr val_rtrn)
75{
76    int ch, nInBuf;
77
78    nInBuf = 0;
79    while (((ch = getc(file)) != EOF) && (ch != '\n') && (ch != quote)) {
80        if (ch == '\\') {
81            if ((ch = getc(file)) != EOF) {
82                if (ch == 'n')
83                    ch = '\n';
84                else if (ch == 't')
85                    ch = '\t';
86                else if (ch == 'v')
87                    ch = '\v';
88                else if (ch == 'b')
89                    ch = '\b';
90                else if (ch == 'r')
91                    ch = '\r';
92                else if (ch == 'f')
93                    ch = '\f';
94                else if (ch == 'e')
95                    ch = '\033';
96                else if (ch == '0') {
97                    int tmp, stop;
98
99                    ch = stop = 0;
100                    if (((tmp = getc(file)) != EOF) && (isdigit(tmp)) &&
101                        (tmp != '8') && (tmp != '9')) {
102                        ch = (ch * 8) + (tmp - '0');
103                    }
104                    else {
105                        stop = 1;
106                        ungetc(tmp, file);
107                    }
108                    if ((!stop) && ((tmp = getc(file)) != EOF) && (isdigit(tmp))
109                        && (tmp != '8') && (tmp != '9')) {
110                        ch = (ch * 8) + (tmp - '0');
111                    }
112                    else {
113                        stop = 1;
114                        ungetc(tmp, file);
115                    }
116                    if ((!stop) && ((tmp = getc(file)) != EOF) && (isdigit(tmp))
117                        && (tmp != '8') && (tmp != '9')) {
118                        ch = (ch * 8) + (tmp - '0');
119                    }
120                    else {
121                        stop = 1;
122                        ungetc(tmp, file);
123                    }
124                }
125            }
126            else
127                return XkbCF_EOF;
128        }
129
130        if (nInBuf < XKBCF_MAX_STR_LEN - 1)
131            _XkbCF_rtrn[nInBuf++] = ch;
132    }
133    if (ch == quote) {
134        _XkbCF_rtrn[nInBuf++] = '\0';
135        val_rtrn->str = _XkbCF_rtrn;
136        return XkbCF_String;
137    }
138    return XkbCF_UnterminatedString;
139}
140
141static int
142ScanInteger(FILE *file, int ch, XkbCFScanResultPtr val_rtrn)
143{
144    int i;
145
146    if (isdigit(ch))
147        ungetc(ch, file);
148    if (fscanf(file, "%i", &i) == 1) {
149        val_rtrn->ival = i;
150        return XkbCF_Integer;
151    }
152    return XkbCF_Unknown;
153}
154
155int
156XkbCFScan(FILE *file, XkbCFScanResultPtr val_rtrn, XkbConfigRtrnPtr rtrn)
157{
158    int ch;
159
160    do {
161        ch = getc(file);
162    } while ((ch == '\t') || (ch == ' '));
163    if (isalpha(ch))
164        return ScanIdent(file, ch, val_rtrn);
165    else if (isdigit(ch))
166        return ScanInteger(file, ch, val_rtrn);
167    else if (ch == '"')
168        return ScanString(file, ch, val_rtrn);
169    else if (ch == '\n') {
170        rtrn->line++;
171        return XkbCF_EOL;
172    }
173    else if (ch == ';')
174        return XkbCF_Semi;
175    else if (ch == '=')
176        return XkbCF_Equals;
177    else if (ch == '+') {
178        ch = getc(file);
179        if (ch == '=')
180            return XkbCF_PlusEquals;
181        if ((ch != EOF) && (ch != ' ') && (ch != '\t'))
182            ungetc(ch, file);
183        return XkbCF_Plus;
184    }
185    else if (ch == '-') {
186        ch = getc(file);
187        if (ch == '=')
188            return XkbCF_MinusEquals;
189        if ((ch != EOF) && (ch != ' ') && (ch != '\t'))
190            ungetc(ch, file);
191        return XkbCF_Minus;
192    }
193    else if (ch == EOF)
194        return XkbCF_EOF;
195    else if ((ch == '#') || ((ch == '/') && (getc(file) == '/'))) {
196        while ((ch != '\n') && (ch != EOF))
197            ch = getc(file);
198        rtrn->line++;
199        return XkbCF_EOL;
200    }
201    return XkbCF_Unknown;
202}
203
204/***====================================================================***/
205
206#define	_XkbCF_Illegal			0
207#define	_XkbCF_Keymap		 	1
208#define	_XkbCF_Keycodes		 	2
209#define	_XkbCF_Geometry		 	3
210#define	_XkbCF_PhysSymbols	 	4
211#define _XkbCF_Symbols		 	5
212#define	_XkbCF_Types		 	6
213#define	_XkbCF_CompatMap	 	7
214
215#define	_XkbCF_RulesFile		8
216#define	_XkbCF_Model			9
217#define	_XkbCF_Layout			10
218#define	_XkbCF_Variant			11
219#define	_XkbCF_Options			12
220
221#define	_XkbCF_InitialMods	 	13
222#define	_XkbCF_InitialCtrls	 	14
223
224#define	_XkbCF_ClickVolume	 	15
225#define	_XkbCF_BellVolume	 	16
226#define	_XkbCF_BellPitch	 	17
227#define	_XkbCF_BellDuration	 	18
228#define	_XkbCF_RepeatDelay	 	19
229#define	_XkbCF_RepeatInterval	 	20
230#define	_XkbCF_SlowKeysDelay	 	21
231#define	_XkbCF_DebounceDelay		22
232#define	_XkbCF_MouseKeysDelay		23
233#define	_XkbCF_MouseKeysInterval	24
234#define	_XkbCF_MouseKeysTimeToMax	25
235#define	_XkbCF_MouseKeysMaxSpeed	26
236#define	_XkbCF_MouseKeysCurve		27
237#define	_XkbCF_AccessXTimeout		28
238#define	_XkbCF_AccessXTimeoutCtrlsOn	29
239#define	_XkbCF_AccessXTimeoutCtrlsOff	30
240#define	_XkbCF_AccessXTimeoutOptsOn	31
241#define	_XkbCF_AccessXTimeoutOptsOff	32
242
243#define	_XkbCF_IgnoreLockMods		33
244#define	_XkbCF_IgnoreGroupLock		34
245#define	_XkbCF_InternalMods		35
246
247#define	_XkbCF_GroupsWrap		36
248#define	_XkbCF_InitialFeedback		37
249
250static Bool
251AddCtrlByName(XkbConfigRtrnPtr rtrn, char *name, unsigned long *ctrls_rtrn)
252{
253    if ((_XkbStrCaseCmp(name, "repeat") == 0) ||
254        (_XkbStrCaseCmp(name, "repeatkeys") == 0))
255        *ctrls_rtrn = XkbRepeatKeysMask;
256    else if (_XkbStrCaseCmp(name, "slowkeys") == 0)
257        *ctrls_rtrn = XkbSlowKeysMask;
258    else if (_XkbStrCaseCmp(name, "bouncekeys") == 0)
259        *ctrls_rtrn = XkbBounceKeysMask;
260    else if (_XkbStrCaseCmp(name, "stickykeys") == 0)
261        *ctrls_rtrn = XkbStickyKeysMask;
262    else if (_XkbStrCaseCmp(name, "mousekeys") == 0)
263        *ctrls_rtrn = XkbMouseKeysMask;
264    else if (_XkbStrCaseCmp(name, "mousekeysaccel") == 0)
265        *ctrls_rtrn = XkbMouseKeysAccelMask;
266    else if (_XkbStrCaseCmp(name, "accessxkeys") == 0)
267        *ctrls_rtrn = XkbAccessXKeysMask;
268    else if (_XkbStrCaseCmp(name, "accessxtimeout") == 0)
269        *ctrls_rtrn = XkbAccessXTimeoutMask;
270    else if (_XkbStrCaseCmp(name, "accessxfeedback") == 0)
271        *ctrls_rtrn = XkbAccessXFeedbackMask;
272    else if (_XkbStrCaseCmp(name, "audiblebell") == 0)
273        *ctrls_rtrn = XkbAudibleBellMask;
274    else if (_XkbStrCaseCmp(name, "overlay1") == 0)
275        *ctrls_rtrn = XkbOverlay1Mask;
276    else if (_XkbStrCaseCmp(name, "overlay2") == 0)
277        *ctrls_rtrn = XkbOverlay2Mask;
278    else if (_XkbStrCaseCmp(name, "ignoregrouplock") == 0)
279        *ctrls_rtrn = XkbIgnoreGroupLockMask;
280    else {
281        rtrn->error = XkbCF_ExpectedControl;
282        return False;
283    }
284    return True;
285}
286
287static Bool
288AddAXTimeoutOptByName(XkbConfigRtrnPtr rtrn,
289                      char *name,
290                      unsigned short *opts_rtrn)
291{
292    if (_XkbStrCaseCmp(name, "slowkeyspress") == 0)
293        *opts_rtrn = XkbAX_SKPressFBMask;
294    else if (_XkbStrCaseCmp(name, "slowkeysaccept") == 0)
295        *opts_rtrn = XkbAX_SKAcceptFBMask;
296    else if (_XkbStrCaseCmp(name, "feature") == 0)
297        *opts_rtrn = XkbAX_FeatureFBMask;
298    else if (_XkbStrCaseCmp(name, "slowwarn") == 0)
299        *opts_rtrn = XkbAX_SlowWarnFBMask;
300    else if (_XkbStrCaseCmp(name, "indicator") == 0)
301        *opts_rtrn = XkbAX_IndicatorFBMask;
302    else if (_XkbStrCaseCmp(name, "stickykeys") == 0)
303        *opts_rtrn = XkbAX_StickyKeysFBMask;
304    else if (_XkbStrCaseCmp(name, "twokeys") == 0)
305        *opts_rtrn = XkbAX_TwoKeysMask;
306    else if (_XkbStrCaseCmp(name, "latchtolock") == 0)
307        *opts_rtrn = XkbAX_LatchToLockMask;
308    else if (_XkbStrCaseCmp(name, "slowkeysrelease") == 0)
309        *opts_rtrn = XkbAX_SKReleaseFBMask;
310    else if (_XkbStrCaseCmp(name, "slowkeysreject") == 0)
311        *opts_rtrn = XkbAX_SKRejectFBMask;
312    else if (_XkbStrCaseCmp(name, "bouncekeysreject") == 0)
313        *opts_rtrn = XkbAX_BKRejectFBMask;
314    else if (_XkbStrCaseCmp(name, "dumbbell") == 0)
315        *opts_rtrn = XkbAX_DumbBellFBMask;
316    else {
317        rtrn->error = XkbCF_ExpectedControl;
318        return False;
319    }
320    return True;
321}
322
323XkbConfigUnboundModPtr
324XkbCFAddModByName(XkbConfigRtrnPtr rtrn, int what, char *name, Bool merge,
325                  XkbConfigUnboundModPtr last)
326{
327    if (rtrn->num_unbound_mods >= rtrn->sz_unbound_mods) {
328        rtrn->sz_unbound_mods += 5;
329        rtrn->unbound_mods = _XkbTypedRealloc(rtrn->unbound_mods,
330                                              rtrn->sz_unbound_mods,
331                                              XkbConfigUnboundModRec);
332        if (rtrn->unbound_mods == NULL) {
333            rtrn->error = XkbCF_BadAlloc;
334            return NULL;
335        }
336    }
337    if (last == NULL) {
338        last = &rtrn->unbound_mods[rtrn->num_unbound_mods++];
339        last->what = what;
340        last->mods = 0;
341        last->vmods = 0;
342        last->merge = merge;
343        last->name = NULL;
344    }
345    if (_XkbStrCaseCmp(name, "shift") == 0)
346        last->mods |= ShiftMask;
347    else if (_XkbStrCaseCmp(name, "lock") == 0)
348        last->mods |= LockMask;
349    else if ((_XkbStrCaseCmp(name, "control") == 0) ||
350             (_XkbStrCaseCmp(name, "ctrl") == 0))
351        last->mods |= ControlMask;
352    else if (_XkbStrCaseCmp(name, "mod1") == 0)
353        last->mods |= Mod1Mask;
354    else if (_XkbStrCaseCmp(name, "mod2") == 0)
355        last->mods |= Mod2Mask;
356    else if (_XkbStrCaseCmp(name, "mod3") == 0)
357        last->mods |= Mod3Mask;
358    else if (_XkbStrCaseCmp(name, "mod4") == 0)
359        last->mods |= Mod4Mask;
360    else if (_XkbStrCaseCmp(name, "mod5") == 0)
361        last->mods |= Mod5Mask;
362    else {
363        if (last->name != NULL) {
364            last = &rtrn->unbound_mods[rtrn->num_unbound_mods++];
365            last->what = what;
366            last->mods = 0;
367            last->vmods = 0;
368            last->merge = merge;
369            last->name = NULL;
370        }
371        last->name = _XkbDupString(name);
372    }
373    return last;
374}
375
376int
377XkbCFBindMods(XkbConfigRtrnPtr rtrn, XkbDescPtr xkb)
378{
379    register int n, v;
380    Atom name;
381    XkbConfigUnboundModPtr mod;
382    int missing;
383
384    if (rtrn->num_unbound_mods < 1)
385        return 0;
386    if ((xkb == NULL) || (xkb->names == NULL))
387        return -1;
388
389    missing = 0;
390    for (n = 0, mod = rtrn->unbound_mods; n < rtrn->num_unbound_mods;
391         n++, mod++) {
392        if (mod->name != NULL) {
393            name = XkbInternAtom(xkb->dpy, mod->name, True);
394            if (name == None)
395                continue;
396            for (v = 0; v < XkbNumVirtualMods; v++) {
397                if (xkb->names->vmods[v] == name) {
398                    mod->vmods = (1 << v);
399                    _XkbFree(mod->name);
400                    mod->name = NULL;
401                    break;
402                }
403            }
404            if (mod->name != NULL)
405                missing++;
406        }
407    }
408    return missing;
409}
410
411Bool
412XkbCFApplyMods(XkbConfigRtrnPtr rtrn, int what, XkbConfigModInfoPtr info)
413{
414    register int n;
415    XkbConfigUnboundModPtr mod;
416
417    if (rtrn->num_unbound_mods < 1)
418        return True;
419
420    for (n = 0, mod = rtrn->unbound_mods; n < rtrn->num_unbound_mods;
421         n++, mod++) {
422        if (mod->what != what)
423            continue;
424        if (mod->merge == XkbCF_MergeRemove) {
425            info->mods_clear |= mod->mods;
426            info->vmods_clear |= mod->vmods;
427        }
428        else {
429            if (mod->merge == XkbCF_MergeSet)
430                info->replace = True;
431            info->mods |= mod->mods;
432            info->vmods |= mod->vmods;
433        }
434        if (mod->name == NULL) {
435            mod->what = _XkbCF_Illegal;
436        }
437        else {
438            mod->mods = 0;
439            mod->vmods = 0;
440        }
441    }
442    return True;
443}
444
445/*ARGSUSED*/
446static Bool
447DefaultParser(FILE *             file,
448              XkbConfigFieldsPtr fields,
449              XkbConfigFieldPtr  field,
450              XkbDescPtr         xkb,
451              XkbConfigRtrnPtr   rtrn)
452{
453    int tok;
454    XkbCFScanResultRec val;
455    char **str;
456    int merge;
457    unsigned long *ctrls, ctrls_mask;
458    unsigned short *opts, opts_mask;
459    int *pival, sign;
460    int onoff;
461    XkbConfigUnboundModPtr last;
462    unsigned what;
463
464    tok = XkbCFScan(file, &val, rtrn);
465    str = NULL;
466    onoff = 0;
467    pival = NULL;
468    switch (field->field_id) {
469    case _XkbCF_RulesFile:      if (!str) str = &rtrn->rules_file;
470    case _XkbCF_Model:          if (!str) str = &rtrn->model;
471    case _XkbCF_Layout:         if (!str) str = &rtrn->layout;
472    case _XkbCF_Variant:        if (!str) str = &rtrn->variant;
473    case _XkbCF_Options:        if (!str) str = &rtrn->options;
474    case _XkbCF_Keymap:         if (!str) str = &rtrn->keymap;
475    case _XkbCF_Keycodes:       if (!str) str = &rtrn->keycodes;
476    case _XkbCF_Geometry:       if (!str) str = &rtrn->geometry;
477    case _XkbCF_PhysSymbols:    if (!str) str = &rtrn->phys_symbols;
478    case _XkbCF_Symbols:        if (!str) str = &rtrn->symbols;
479    case _XkbCF_Types:          if (!str) str = &rtrn->types;
480    case _XkbCF_CompatMap:      if (!str) str = &rtrn->compat;
481        if (tok != XkbCF_Equals) {
482            rtrn->error = XkbCF_MissingEquals;
483            goto BAILOUT;
484        }
485        tok = XkbCFScan(file, &val, rtrn);
486        if ((tok != XkbCF_String) && (tok != XkbCF_Ident)) {
487            rtrn->error = XkbCF_ExpectedString;
488            return False;
489        }
490        tok = XkbCFScan(file, &val, rtrn);
491        if ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
492            rtrn->error = XkbCF_ExpectedEOS;
493            return False;
494        }
495        if (*str != NULL)
496            _XkbFree(*str);
497        *str = _XkbDupString(val.str);
498        break;
499    case _XkbCF_InitialMods:
500    case _XkbCF_IgnoreLockMods:
501    case _XkbCF_InternalMods:
502        what = XkbCF_InitialMods;
503        if (field->field_id == _XkbCF_InitialMods)
504            rtrn->defined |= (what = XkbCF_InitialMods);
505        else if (field->field_id == _XkbCF_InternalMods)
506            rtrn->defined |= (what = XkbCF_InternalMods);
507        else if (field->field_id == _XkbCF_IgnoreLockMods)
508            rtrn->defined |= (what = XkbCF_IgnoreLockMods);
509        if (tok == XkbCF_Equals)
510            merge = XkbCF_MergeSet;
511        else if (tok == XkbCF_MinusEquals)
512            merge = XkbCF_MergeRemove;
513        else if (tok == XkbCF_PlusEquals)
514            merge = XkbCF_MergeAdd;
515        else {
516            rtrn->error = XkbCF_MissingEquals;
517            goto BAILOUT;
518        }
519        tok = XkbCFScan(file, &val, rtrn);
520        if ((tok == XkbCF_EOL) || (tok == XkbCF_Semi) || (tok == XkbCF_EOF)) {
521            rtrn->error = XkbCF_ExpectedModifier;
522            return False;
523        }
524        last = NULL;
525        while ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
526            if ((tok != XkbCF_Ident) && (tok != XkbCF_String)) {
527                rtrn->error = XkbCF_ExpectedModifier;
528                return False;
529            }
530            last = XkbCFAddModByName(rtrn, what, val.str, merge, last);
531            if (last == NULL)
532                return False;
533            if (merge == XkbCF_MergeSet)
534                merge = XkbCF_MergeAdd;
535            tok = XkbCFScan(file, &val, rtrn);
536            if ((tok != XkbCF_EOL) && (tok != XkbCF_EOF) && (tok != XkbCF_Semi)) {
537                if (tok != XkbCF_Plus) {
538                    rtrn->error = XkbCF_ExpectedOperator;
539                    return False;
540                }
541                tok = XkbCFScan(file, &val, rtrn);
542            }
543        }
544        break;
545    case _XkbCF_InitialCtrls:
546        rtrn->defined |= XkbCF_InitialCtrls;
547        ctrls = NULL;
548        if (tok == XkbCF_PlusEquals)
549            ctrls = &rtrn->initial_ctrls;
550        else if (tok == XkbCF_MinusEquals)
551            ctrls = &rtrn->initial_ctrls_clear;
552        else if (tok == XkbCF_Equals) {
553            ctrls = &rtrn->initial_ctrls;
554            rtrn->replace_initial_ctrls = True;
555            *ctrls = 0;
556        }
557        else {
558            rtrn->error = XkbCF_MissingEquals;
559            goto BAILOUT;
560        }
561        tok = XkbCFScan(file, &val, rtrn);
562        if ((tok == XkbCF_EOL) || (tok == XkbCF_Semi) || (tok == XkbCF_EOF)) {
563            rtrn->error = XkbCF_ExpectedControl;
564            return False;
565        }
566        while ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
567            if ((tok != XkbCF_Ident) && (tok != XkbCF_String)) {
568                rtrn->error = XkbCF_ExpectedControl;
569                return False;
570            }
571            if (!AddCtrlByName(rtrn, val.str, &ctrls_mask)) {
572                return False;
573            }
574            *ctrls |= ctrls_mask;
575            tok = XkbCFScan(file, &val, rtrn);
576            if ((tok != XkbCF_EOL) && (tok != XkbCF_EOF) && (tok != XkbCF_Semi)) {
577                if (tok != XkbCF_Plus) {
578                    rtrn->error = XkbCF_ExpectedOperator;
579                    return False;
580                }
581                tok = XkbCFScan(file, &val, rtrn);
582            }
583        }
584        break;
585    case _XkbCF_AccessXTimeoutCtrlsOn:
586    case _XkbCF_AccessXTimeoutCtrlsOff:
587        opts = NULL;
588        if (tok == XkbCF_MinusEquals) {
589            ctrls = &rtrn->axt_ctrls_ignore;
590            opts = &rtrn->axt_opts_ignore;
591        }
592        else if ((tok == XkbCF_PlusEquals) || (tok == XkbCF_Equals)) {
593            if (field->field_id == _XkbCF_AccessXTimeoutCtrlsOff) {
594                ctrls = &rtrn->axt_ctrls_off;
595                opts = &rtrn->axt_opts_off;
596                if (tok == XkbCF_Equals)
597                    rtrn->replace_axt_ctrls_off = True;
598            }
599            else {
600                ctrls = &rtrn->axt_ctrls_on;
601                opts = &rtrn->axt_opts_on;
602                if (tok == XkbCF_Equals)
603                    rtrn->replace_axt_ctrls_on = True;
604            }
605            *ctrls = 0;
606        }
607        else {
608            rtrn->error = XkbCF_MissingEquals;
609            goto BAILOUT;
610        }
611        tok = XkbCFScan(file, &val, rtrn);
612        if ((tok == XkbCF_EOL) || (tok == XkbCF_Semi) || (tok == XkbCF_EOF)) {
613            rtrn->error = XkbCF_ExpectedControl;
614            return False;
615        }
616        while ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
617            if ((tok != XkbCF_Ident) && (tok != XkbCF_String)) {
618                rtrn->error = XkbCF_ExpectedControl;
619                return False;
620            }
621            if (!AddCtrlByName(rtrn, val.str, &ctrls_mask)) {
622                if (!AddAXTimeoutOptByName(rtrn, val.str, &opts_mask))
623                    return False;
624                *opts |= opts_mask;
625                if (field->field_id == _XkbCF_AccessXTimeoutCtrlsOff) {
626                    rtrn->defined |= XkbCF_AccessXTimeoutOptsOff;
627                    if (rtrn->replace_axt_ctrls_off)
628                        rtrn->replace_axt_opts_off = True;
629                }
630                else {
631                    rtrn->defined |= XkbCF_AccessXTimeoutOptsOn;
632                    if (rtrn->replace_axt_ctrls_on)
633                        rtrn->replace_axt_opts_on = True;
634                }
635            }
636            else
637                *ctrls |= ctrls_mask;
638            tok = XkbCFScan(file, &val, rtrn);
639            if ((tok != XkbCF_EOL) && (tok != XkbCF_EOF) && (tok != XkbCF_Semi)) {
640                if (tok != XkbCF_Plus) {
641                    rtrn->error = XkbCF_ExpectedOperator;
642                    return False;
643                }
644                tok = XkbCFScan(file, &val, rtrn);
645            }
646        }
647        break;
648    case _XkbCF_InitialFeedback:
649        rtrn->defined |= XkbCF_InitialOpts;
650        opts = NULL;
651        if (tok == XkbCF_PlusEquals)
652            opts = &rtrn->initial_opts;
653        else if (tok == XkbCF_MinusEquals)
654            opts = &rtrn->initial_opts_clear;
655        else if (tok == XkbCF_Equals) {
656            opts = &rtrn->initial_opts;
657            rtrn->replace_initial_opts = True;
658            *opts = 0;
659        }
660        else {
661            rtrn->error = XkbCF_MissingEquals;
662            goto BAILOUT;
663        }
664        tok = XkbCFScan(file, &val, rtrn);
665        if ((tok == XkbCF_EOL) || (tok == XkbCF_Semi) || (tok == XkbCF_EOF)) {
666            rtrn->error = XkbCF_ExpectedAXOption;
667            return False;
668        }
669        while ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
670            if ((tok != XkbCF_Ident) && (tok != XkbCF_String)) {
671                rtrn->error = XkbCF_ExpectedAXOption;
672                return False;
673            }
674            if (!AddAXTimeoutOptByName(rtrn, val.str, &opts_mask)) {
675                return False;
676            }
677            *opts |= opts_mask;
678            tok = XkbCFScan(file, &val, rtrn);
679            if ((tok != XkbCF_EOL) && (tok != XkbCF_EOF) && (tok != XkbCF_Semi)) {
680                if (tok != XkbCF_Plus) {
681                    rtrn->error = XkbCF_ExpectedOperator;
682                    return False;
683                }
684                tok = XkbCFScan(file, &val, rtrn);
685            }
686        }
687        break;
688    case _XkbCF_AccessXTimeoutOptsOff:
689    case _XkbCF_AccessXTimeoutOptsOn:
690        opts = NULL;
691        if (tok == XkbCF_MinusEquals)
692            opts = &rtrn->axt_opts_ignore;
693        else if ((tok == XkbCF_PlusEquals) || (tok == XkbCF_Equals)) {
694            if (field->field_id == _XkbCF_AccessXTimeoutOptsOff) {
695                opts = &rtrn->axt_opts_off;
696                if (tok == XkbCF_Equals)
697                    rtrn->replace_axt_opts_off = True;
698            }
699            else {
700                opts = &rtrn->axt_opts_on;
701                if (tok == XkbCF_Equals)
702                    rtrn->replace_axt_opts_on = True;
703            }
704            *opts = 0;
705        }
706        else {
707            rtrn->error = XkbCF_MissingEquals;
708            goto BAILOUT;
709        }
710        tok = XkbCFScan(file, &val, rtrn);
711        if ((tok == XkbCF_EOL) || (tok == XkbCF_Semi) || (tok == XkbCF_EOF)) {
712            rtrn->error = XkbCF_ExpectedControl;
713            return False;
714        }
715        while ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
716            if ((tok != XkbCF_Ident) && (tok != XkbCF_String)) {
717                rtrn->error = XkbCF_ExpectedControl;
718                return False;
719            }
720            if (!AddAXTimeoutOptByName(rtrn, val.str, &opts_mask))
721                return False;
722            *opts |= opts_mask;
723
724            tok = XkbCFScan(file, &val, rtrn);
725            if ((tok != XkbCF_EOL) && (tok != XkbCF_EOF) && (tok != XkbCF_Semi)) {
726                if (tok != XkbCF_Plus) {
727                    rtrn->error = XkbCF_ExpectedOperator;
728                    return False;
729                }
730                tok = XkbCFScan(file, &val, rtrn);
731            }
732        }
733        break;
734    case _XkbCF_ClickVolume:
735        if (!pival) {
736            pival = &rtrn->click_volume;
737            onoff = 100;
738        }
739    case _XkbCF_BellVolume:
740        if (!pival) {
741            pival = &rtrn->bell_volume;
742            onoff = 100;
743        }
744    case _XkbCF_BellPitch:          if (!pival) pival = &rtrn->bell_pitch;
745    case _XkbCF_BellDuration:       if (!pival) pival = &rtrn->bell_duration;
746    case _XkbCF_RepeatDelay:        if (!pival) pival = &rtrn->repeat_delay;
747    case _XkbCF_RepeatInterval:     if (!pival) pival = &rtrn->repeat_interval;
748    case _XkbCF_SlowKeysDelay:      if (!pival) pival = &rtrn->slow_keys_delay;
749    case _XkbCF_DebounceDelay:      if (!pival) pival = &rtrn->debounce_delay;
750    case _XkbCF_MouseKeysDelay:     if (!pival) pival = &rtrn->mk_delay;
751    case _XkbCF_MouseKeysInterval:  if (!pival) pival = &rtrn->mk_interval;
752    case _XkbCF_MouseKeysTimeToMax: if (!pival) pival = &rtrn->mk_time_to_max;
753    case _XkbCF_MouseKeysMaxSpeed:  if (!pival) pival = &rtrn->mk_max_speed;
754    case _XkbCF_MouseKeysCurve:     if (!pival) pival = &rtrn->mk_curve;
755    case _XkbCF_AccessXTimeout:     if (!pival) pival = &rtrn->ax_timeout;
756        if (tok != XkbCF_Equals) {
757            rtrn->error = XkbCF_MissingEquals;
758            goto BAILOUT;
759        }
760        tok = XkbCFScan(file, &val, rtrn);
761        if (tok == XkbCF_Minus && field->field_id == _XkbCF_MouseKeysCurve) {
762            /* This can be a negative value */
763            tok = XkbCFScan(file, &val, rtrn);
764            sign = -1;
765        }
766        else
767            sign = 1;
768        if (tok != XkbCF_Integer) {
769            Bool ok = False;
770
771            if ((onoff) && (tok == XkbCF_Ident) && (val.str != NULL)) {
772                if (_XkbStrCaseCmp(val.str, "on")) {
773                    val.ival = onoff;
774                    ok = True;
775                }
776                else if (_XkbStrCaseCmp(val.str, "off")) {
777                    val.ival = 0;
778                    ok = True;
779                }
780            }
781            if (!ok) {
782                rtrn->error = XkbCF_ExpectedInteger;
783                goto BAILOUT;
784            }
785        }
786        *pival = val.ival * sign;
787        if (field->field_id == _XkbCF_AccessXTimeout)
788            rtrn->defined |= XkbCF_AccessXTimeout;
789        tok = XkbCFScan(file, &val, rtrn);
790        if ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
791            rtrn->error = XkbCF_ExpectedEOS;
792            return False;
793        }
794        break;
795    case _XkbCF_GroupsWrap:
796        if (tok != XkbCF_Equals) {
797            rtrn->error = XkbCF_MissingEquals;
798            goto BAILOUT;
799        }
800        tok = XkbCFScan(file, &val, rtrn);
801        if (tok == XkbCF_Ident) {
802            if (_XkbStrCaseCmp(val.str, "wrap") == 0) {
803                rtrn->groups_wrap = XkbSetGroupInfo(0, XkbWrapIntoRange, 0);
804            }
805            else if (_XkbStrCaseCmp(val.str, "clamp") == 0) {
806                rtrn->groups_wrap = XkbSetGroupInfo(0, XkbClampIntoRange, 0);
807            }
808            else {
809                rtrn->error = XkbCF_ExpectedOORGroupBehavior;
810                return False;
811            }
812        }
813        else if ((tok == XkbCF_Integer) && (XkbIsLegalGroup(val.ival - 1))) {
814            rtrn->groups_wrap = XkbSetGroupInfo(0, XkbRedirectIntoRange,
815                                                val.ival - 1);
816        }
817        else {
818            rtrn->error = XkbCF_ExpectedOORGroupBehavior;
819            return False;
820        }
821        rtrn->defined |= XkbCF_GroupsWrap;
822        tok = XkbCFScan(file, &val, rtrn);
823        if ((tok != XkbCF_EOL) && (tok != XkbCF_Semi) && (tok != XkbCF_EOF)) {
824            rtrn->error = XkbCF_ExpectedEOS;
825            return False;
826        }
827        break;
828    default:
829        rtrn->error = XkbCF_ExpectedInteger;
830        goto BAILOUT;
831
832    }
833    return True;
834 BAILOUT:
835    return False;
836}
837
838static Bool
839DefaultCleanUp(XkbConfigRtrnPtr rtrn)
840{
841    if (rtrn->keymap)
842        _XkbFree(rtrn->keymap);
843    if (rtrn->keycodes)
844        _XkbFree(rtrn->keycodes);
845    if (rtrn->geometry)
846        _XkbFree(rtrn->geometry);
847    if (rtrn->phys_symbols)
848        _XkbFree(rtrn->phys_symbols);
849    if (rtrn->symbols)
850        _XkbFree(rtrn->symbols);
851    if (rtrn->types)
852        _XkbFree(rtrn->types);
853    if (rtrn->compat)
854        _XkbFree(rtrn->compat);
855    rtrn->keycodes = rtrn->geometry = NULL;
856    rtrn->symbols = rtrn->phys_symbols = NULL;
857    rtrn->types = rtrn->compat = NULL;
858    if ((rtrn->unbound_mods != NULL) && (rtrn->num_unbound_mods > 0)) {
859        register int i;
860
861        for (i = 0; i < rtrn->num_unbound_mods; i++) {
862            if (rtrn->unbound_mods[i].name != NULL) {
863                _XkbFree(rtrn->unbound_mods[i].name);
864                rtrn->unbound_mods[i].name = NULL;
865            }
866        }
867        _XkbFree(rtrn->unbound_mods);
868        rtrn->sz_unbound_mods = 0;
869        rtrn->num_unbound_mods = 0;
870        rtrn->unbound_mods = NULL;
871    }
872    return True;
873}
874
875static Bool
876DefaultApplyNames(XkbConfigRtrnPtr rtrn, XkbDescPtr xkb)
877{
878    char *str;
879
880    if (XkbAllocNames(xkb, XkbComponentNamesMask, 0, 0) != Success)
881        return False;
882    if ((str = rtrn->keycodes) != NULL) {
883        xkb->names->keycodes = XkbInternAtom(xkb->dpy, str, False);
884        _XkbFree(str);
885        rtrn->keycodes = NULL;
886    }
887    if ((str = rtrn->geometry) != NULL) {
888        xkb->names->geometry = XkbInternAtom(xkb->dpy, str, False);
889        _XkbFree(str);
890        rtrn->geometry = NULL;
891    }
892    if ((str = rtrn->symbols) != NULL) {
893        xkb->names->symbols = XkbInternAtom(xkb->dpy, str, False);
894        _XkbFree(str);
895        rtrn->symbols = NULL;
896    }
897    if ((str = rtrn->phys_symbols) != NULL) {
898        xkb->names->phys_symbols = XkbInternAtom(xkb->dpy, str, False);
899        _XkbFree(str);
900        rtrn->phys_symbols = NULL;
901    }
902    if ((str = rtrn->types) != NULL) {
903        xkb->names->types = XkbInternAtom(xkb->dpy, str, False);
904        _XkbFree(str);
905        rtrn->types = NULL;
906    }
907    if ((str = rtrn->compat) != NULL) {
908        xkb->names->compat = XkbInternAtom(xkb->dpy, str, False);
909        _XkbFree(str);
910        rtrn->compat = NULL;
911    }
912    return True;
913}
914
915static Bool
916DefaultApplyControls(XkbConfigRtrnPtr rtrn, XkbDescPtr xkb)
917{
918    unsigned on, off;
919    XkbControlsPtr ctrls;
920    unsigned int mask;
921
922    if (XkbAllocControls(xkb, XkbAllControlsMask) != Success)
923        return False;
924    ctrls = xkb->ctrls;
925    if (rtrn->replace_initial_ctrls)
926        ctrls->enabled_ctrls = rtrn->initial_ctrls;
927    else
928        ctrls->enabled_ctrls |= rtrn->initial_ctrls;
929    ctrls->enabled_ctrls &= ~rtrn->initial_ctrls_clear;
930    if (rtrn->internal_mods.replace) {
931        ctrls->internal.real_mods = rtrn->internal_mods.mods;
932        ctrls->internal.vmods = rtrn->internal_mods.vmods;
933    }
934    else {
935        ctrls->internal.real_mods &= ~rtrn->internal_mods.mods_clear;
936        ctrls->internal.vmods &= ~rtrn->internal_mods.vmods_clear;
937        ctrls->internal.real_mods |= rtrn->internal_mods.mods;
938        ctrls->internal.vmods |= rtrn->internal_mods.vmods;
939    }
940    mask = 0;
941    (void) XkbVirtualModsToReal(xkb, ctrls->internal.vmods, &mask);
942    ctrls->internal.mask = (ctrls->internal.real_mods | mask);
943
944    if (rtrn->ignore_lock_mods.replace) {
945        ctrls->ignore_lock.real_mods = rtrn->ignore_lock_mods.mods;
946        ctrls->ignore_lock.vmods = rtrn->ignore_lock_mods.vmods;
947    }
948    else {
949        ctrls->ignore_lock.real_mods &= ~rtrn->ignore_lock_mods.mods_clear;
950        ctrls->ignore_lock.vmods &= ~rtrn->ignore_lock_mods.vmods_clear;
951        ctrls->ignore_lock.real_mods |= rtrn->ignore_lock_mods.mods;
952        ctrls->ignore_lock.vmods |= rtrn->ignore_lock_mods.vmods;
953    }
954    mask = 0;
955    (void) XkbVirtualModsToReal(xkb, ctrls->ignore_lock.vmods, &mask);
956    ctrls->ignore_lock.mask = (ctrls->ignore_lock.real_mods | mask);
957
958    if (rtrn->repeat_delay > 0)
959        ctrls->repeat_delay = rtrn->repeat_delay;
960    if (rtrn->repeat_interval > 0)
961        ctrls->repeat_interval = rtrn->repeat_interval;
962    if (rtrn->slow_keys_delay > 0)
963        ctrls->slow_keys_delay = rtrn->slow_keys_delay;
964    if (rtrn->debounce_delay > 0)
965        ctrls->debounce_delay = rtrn->debounce_delay;
966    if (rtrn->mk_delay > 0)
967        ctrls->mk_delay = rtrn->mk_delay;
968    if (rtrn->mk_interval > 0)
969        ctrls->mk_interval = rtrn->mk_interval;
970    if (rtrn->mk_time_to_max > 0)
971        ctrls->mk_time_to_max = rtrn->mk_time_to_max;
972    if (rtrn->mk_max_speed > 0)
973        ctrls->mk_max_speed = rtrn->mk_max_speed;
974    if (rtrn->mk_curve > 0)
975        ctrls->mk_curve = rtrn->mk_curve;
976    if (rtrn->defined & XkbCF_AccessXTimeout && rtrn->ax_timeout > 0)
977        ctrls->ax_timeout = rtrn->ax_timeout;
978
979    /* any value set to both off and on is reset to ignore */
980    if ((off = (rtrn->axt_ctrls_on & rtrn->axt_ctrls_off)) != 0)
981        rtrn->axt_ctrls_ignore |= off;
982
983    /* ignore takes priority over on and off */
984    rtrn->axt_ctrls_on &= ~rtrn->axt_ctrls_ignore;
985    rtrn->axt_ctrls_off &= ~rtrn->axt_ctrls_ignore;
986
987    if (!rtrn->replace_axt_ctrls_off) {
988        off = (ctrls->axt_ctrls_mask & (~ctrls->axt_ctrls_values));
989        off &= ~rtrn->axt_ctrls_on;
990        off |= rtrn->axt_ctrls_off;
991    }
992    else
993        off = rtrn->axt_ctrls_off;
994    if (!rtrn->replace_axt_ctrls_on) {
995        on = (ctrls->axt_ctrls_mask & ctrls->axt_ctrls_values);
996        on &= ~rtrn->axt_ctrls_off;
997        on |= rtrn->axt_ctrls_on;
998    }
999    else
1000        on = rtrn->axt_ctrls_on;
1001    ctrls->axt_ctrls_mask = (on | off) & ~rtrn->axt_ctrls_ignore;
1002    ctrls->axt_ctrls_values = on & ~rtrn->axt_ctrls_ignore;
1003
1004    /* any value set to both off and on is reset to ignore */
1005    if ((off = (rtrn->axt_opts_on & rtrn->axt_opts_off)) != 0)
1006        rtrn->axt_opts_ignore |= off;
1007
1008    /* ignore takes priority over on and off */
1009    rtrn->axt_opts_on &= ~rtrn->axt_opts_ignore;
1010    rtrn->axt_opts_off &= ~rtrn->axt_opts_ignore;
1011
1012    if (rtrn->replace_axt_opts_off) {
1013        off = (ctrls->axt_opts_mask & (~ctrls->axt_opts_values));
1014        off &= ~rtrn->axt_opts_on;
1015        off |= rtrn->axt_opts_off;
1016    }
1017    else
1018        off = rtrn->axt_opts_off;
1019    if (!rtrn->replace_axt_opts_on) {
1020        on = (ctrls->axt_opts_mask & ctrls->axt_opts_values);
1021        on &= ~rtrn->axt_opts_off;
1022        on |= rtrn->axt_opts_on;
1023    }
1024    else
1025        on = rtrn->axt_opts_on;
1026    ctrls->axt_opts_mask =
1027        (unsigned short) ((on | off) & ~rtrn->axt_ctrls_ignore);
1028    ctrls->axt_opts_values = (unsigned short) (on & ~rtrn->axt_ctrls_ignore);
1029
1030    if (rtrn->defined & XkbCF_GroupsWrap) {
1031        int n;
1032
1033        n = XkbNumGroups(ctrls->groups_wrap);
1034        rtrn->groups_wrap = XkbSetNumGroups(rtrn->groups_wrap, n);
1035        ctrls->groups_wrap = rtrn->groups_wrap;
1036    }
1037    return True;
1038}
1039
1040/*ARGSUSED*/
1041static Bool
1042DefaultFinish(XkbConfigFieldsPtr fields, XkbDescPtr xkb,
1043              XkbConfigRtrnPtr rtrn, int what)
1044{
1045    if ((what == XkbCF_Destroy) || (what == XkbCF_CleanUp))
1046        return DefaultCleanUp(rtrn);
1047    if (what == XkbCF_Check) {
1048        if ((rtrn->symbols == NULL) && (rtrn->phys_symbols != NULL))
1049            rtrn->symbols = _XkbDupString(rtrn->phys_symbols);
1050    }
1051    if ((what == XkbCF_Apply) || (what == XkbCF_Check)) {
1052        if (xkb && xkb->names && (rtrn->num_unbound_mods > 0))
1053            XkbCFBindMods(rtrn, xkb);
1054        XkbCFApplyMods(rtrn, XkbCF_InitialMods, &rtrn->initial_mods);
1055        XkbCFApplyMods(rtrn, XkbCF_InternalMods, &rtrn->internal_mods);
1056        XkbCFApplyMods(rtrn, XkbCF_IgnoreLockMods, &rtrn->ignore_lock_mods);
1057    }
1058    if (what == XkbCF_Apply) {
1059        if (xkb != NULL) {
1060            DefaultApplyNames(rtrn, xkb);
1061            DefaultApplyControls(rtrn, xkb);
1062            XkbCFBindMods(rtrn, xkb);
1063        }
1064    }
1065    return True;
1066}
1067
1068static XkbConfigFieldRec _XkbCFDfltFields[] = {
1069    {"rules",                   _XkbCF_RulesFile},
1070    {"model",                   _XkbCF_Model},
1071    {"layout",                  _XkbCF_Layout},
1072    {"variant",                 _XkbCF_Variant},
1073    {"options",                 _XkbCF_Options},
1074    {"keymap",                  _XkbCF_Keymap},
1075    {"keycodes",                _XkbCF_Keycodes},
1076    {"geometry",                _XkbCF_Geometry},
1077    {"realsymbols",             _XkbCF_PhysSymbols},
1078    {"actualsymbols",           _XkbCF_PhysSymbols},
1079    {"symbols",                 _XkbCF_Symbols},
1080    {"symbolstouse",            _XkbCF_Symbols},
1081    {"types",                   _XkbCF_Types},
1082    {"compat",                  _XkbCF_CompatMap},
1083    {"modifiers",               _XkbCF_InitialMods},
1084    {"controls",                _XkbCF_InitialCtrls},
1085    {"click",                   _XkbCF_ClickVolume},
1086    {"clickvolume",             _XkbCF_ClickVolume},
1087    {"bell",                    _XkbCF_BellVolume},
1088    {"bellvolume",              _XkbCF_BellVolume},
1089    {"bellpitch",               _XkbCF_BellPitch},
1090    {"bellduration",            _XkbCF_BellDuration},
1091    {"repeatdelay",             _XkbCF_RepeatDelay},
1092    {"repeatinterval",          _XkbCF_RepeatInterval},
1093    {"slowkeysdelay",           _XkbCF_SlowKeysDelay},
1094    {"debouncedelay",           _XkbCF_DebounceDelay},
1095    {"mousekeysdelay",          _XkbCF_MouseKeysDelay},
1096    {"mousekeysinterval",       _XkbCF_MouseKeysInterval},
1097    {"mousekeystimetomax",      _XkbCF_MouseKeysTimeToMax},
1098    {"mousekeysmaxspeed",       _XkbCF_MouseKeysMaxSpeed},
1099    {"mousekeyscurve",          _XkbCF_MouseKeysCurve},
1100    {"accessxtimeout",          _XkbCF_AccessXTimeout},
1101    {"axtimeout",               _XkbCF_AccessXTimeout},
1102    {"accessxtimeoutctrlson",   _XkbCF_AccessXTimeoutCtrlsOn},
1103    {"axtctrlson",              _XkbCF_AccessXTimeoutCtrlsOn},
1104    {"accessxtimeoutctrlsoff",  _XkbCF_AccessXTimeoutCtrlsOff},
1105    {"axtctrlsoff",             _XkbCF_AccessXTimeoutCtrlsOff},
1106    {"accessxtimeoutfeedbackon",_XkbCF_AccessXTimeoutOptsOn},
1107    {"axtfeedbackon",           _XkbCF_AccessXTimeoutOptsOn},
1108    {"accessxtimeoutfeedbackoff",_XkbCF_AccessXTimeoutOptsOff},
1109    {"axtfeedbackoff",          _XkbCF_AccessXTimeoutOptsOff},
1110    {"ignorelockmods",          _XkbCF_IgnoreLockMods},
1111    {"ignorelockmodifiers",     _XkbCF_IgnoreLockMods},
1112    {"ignoregrouplock",         _XkbCF_IgnoreGroupLock},
1113    {"internalmods",            _XkbCF_InternalMods},
1114    {"internalmodifiers",       _XkbCF_InternalMods},
1115    {"outofrangegroups",        _XkbCF_GroupsWrap},
1116    {"groups",                  _XkbCF_GroupsWrap},
1117    {"feedback",                _XkbCF_InitialFeedback},
1118};
1119#define	_XkbCFNumDfltFields (sizeof(_XkbCFDfltFields)/sizeof(XkbConfigFieldRec))
1120
1121static XkbConfigFieldsRec _XkbCFDflts = {
1122    0,                          /* cfg_id */
1123    _XkbCFNumDfltFields,        /* num_fields */
1124    _XkbCFDfltFields,           /* fields */
1125    DefaultParser,              /* parser */
1126    DefaultFinish,              /* finish */
1127    NULL,                       /* priv */
1128    NULL                        /* next */
1129};
1130
1131XkbConfigFieldsPtr XkbCFDflts = &_XkbCFDflts;
1132
1133/***====================================================================***/
1134
1135XkbConfigFieldsPtr
1136XkbCFDup(XkbConfigFieldsPtr fields)
1137{
1138    XkbConfigFieldsPtr pNew;
1139
1140    pNew = _XkbTypedAlloc(XkbConfigFieldsRec);
1141    if (pNew != NULL) {
1142        memcpy(pNew, fields, sizeof(XkbConfigFieldsRec));
1143        if ((pNew->fields != NULL) && (pNew->num_fields > 0)) {
1144            pNew->fields = _XkbTypedCalloc(pNew->num_fields, XkbConfigFieldRec);
1145            if (pNew->fields) {
1146                memcpy(fields->fields, pNew->fields,
1147                       (pNew->num_fields * sizeof(XkbConfigFieldRec)));
1148            }
1149            else {
1150                _XkbFree(pNew);
1151                return NULL;
1152            }
1153        }
1154        else {
1155            pNew->num_fields = 0;
1156            pNew->fields = NULL;
1157        }
1158        pNew->next = NULL;
1159    }
1160    return pNew;
1161}
1162
1163XkbConfigFieldsPtr
1164XkbCFFree(XkbConfigFieldsPtr fields, Bool all)
1165{
1166    XkbConfigFieldsPtr next;
1167
1168    next = NULL;
1169    while (fields != NULL) {
1170        next = fields->next;
1171        if (fields != XkbCFDflts) {
1172            if (fields->fields) {
1173                _XkbFree(fields->fields);
1174                fields->fields = NULL;
1175                fields->num_fields = 0;
1176            }
1177            _XkbFree(fields);
1178        }
1179        fields = (all ? next : NULL);
1180    }
1181    return next;
1182}
1183
1184Bool
1185XkbCFApplyRtrnValues(XkbConfigRtrnPtr rtrn,
1186                     XkbConfigFieldsPtr fields,
1187                     XkbDescPtr xkb)
1188{
1189    Bool ok;
1190
1191    if ((fields == NULL) || (rtrn == NULL) || (xkb == NULL))
1192        return False;
1193    for (ok = True; fields != NULL; fields = fields->next) {
1194        if (fields->finish != NULL)
1195            ok = (*fields->finish) (fields, xkb, rtrn, XkbCF_Apply) && ok;
1196    }
1197    return ok;
1198}
1199
1200XkbConfigRtrnPrivPtr
1201XkbCFAddPrivate(XkbConfigRtrnPtr rtrn, XkbConfigFieldsPtr fields, XPointer ptr)
1202{
1203    XkbConfigRtrnPrivPtr priv;
1204
1205    if ((rtrn == NULL) || (fields == NULL))
1206        return NULL;
1207    priv = _XkbTypedAlloc(XkbConfigRtrnPrivRec);
1208    if (priv != NULL) {
1209        priv->cfg_id = fields->cfg_id;
1210        priv->priv = ptr;
1211        priv->next = rtrn->priv;
1212        rtrn->priv = priv;
1213    }
1214    return priv;
1215}
1216
1217void
1218XkbCFFreeRtrn(XkbConfigRtrnPtr rtrn, XkbConfigFieldsPtr fields, XkbDescPtr xkb)
1219{
1220    XkbConfigRtrnPrivPtr tmp, next;
1221
1222    if ((fields == NULL) || (rtrn == NULL))
1223        return;
1224    while (fields != NULL) {
1225        if (fields->finish != NULL)
1226            (*fields->finish) (fields, xkb, rtrn, XkbCF_Destroy);
1227        fields = fields->next;
1228    }
1229    for (tmp = rtrn->priv; tmp != NULL; tmp = next) {
1230        next = tmp->next;
1231        bzero((char *) tmp, sizeof(XkbConfigRtrnPrivRec));
1232        _XkbFree(tmp);
1233    }
1234    bzero((char *) rtrn, sizeof(XkbConfigRtrnRec));
1235    return;
1236}
1237
1238Bool
1239XkbCFParse(FILE *file, XkbConfigFieldsPtr fields,
1240           XkbDescPtr xkb, XkbConfigRtrnPtr rtrn)
1241{
1242    int tok;
1243    XkbCFScanResultRec val;
1244    XkbConfigFieldsPtr tmp;
1245
1246    if ((file == NULL) || (fields == NULL) || (rtrn == NULL))
1247        return False;
1248    for (tok = 0, tmp = fields; tmp != NULL; tmp = tmp->next, tok++) {
1249        fields->cfg_id = tok;
1250    }
1251    bzero((char *) rtrn, sizeof(XkbConfigRtrnRec));
1252    rtrn->line = 1;
1253    rtrn->click_volume = -1;
1254    rtrn->bell_volume = -1;
1255    while ((tok = XkbCFScan(file, &val, rtrn)) != XkbCF_EOF) {
1256        if (tok == XkbCF_Ident) {
1257            Bool done;
1258
1259            for (tmp = fields, done = False; (tmp != NULL) && (!done);
1260                 tmp = tmp->next) {
1261                register int i;
1262
1263                XkbConfigFieldPtr f;
1264
1265                for (i = 0, f = tmp->fields; (i < tmp->num_fields) && (!done);
1266                     i++, f++) {
1267                    if (_XkbStrCaseCmp(val.str, f->field) != 0)
1268                        continue;
1269                    if ((*tmp->parser) (file, tmp, f, xkb, rtrn))
1270                        done = True;
1271                    else
1272                        goto BAILOUT;
1273                }
1274            }
1275        }
1276        else if ((tok != XkbCF_EOL) && (tok != XkbCF_Semi)) {
1277            rtrn->error = XkbCF_MissingIdent;
1278            goto BAILOUT;
1279        }
1280    }
1281    for (tmp = fields; tmp != NULL; tmp = tmp->next) {
1282        if ((tmp->finish) && (!(*tmp->finish) (tmp, xkb, rtrn, XkbCF_Check)))
1283            goto BAILOUT;
1284    }
1285    return True;
1286 BAILOUT:
1287    for (tmp = fields; tmp != NULL; tmp = tmp->next) {
1288        if (tmp->finish)
1289            (*tmp->finish) (tmp, xkb, rtrn, XkbCF_CleanUp);
1290    }
1291    return False;
1292}
1293
1294/*ARGSUSED*/
1295void
1296XkbCFReportError(FILE *file, char *name, int error, int line)
1297{
1298    const char *msg;
1299
1300    switch (error) {
1301    case XkbCF_BadAlloc:
1302        msg = "allocation failed\n";
1303        break;
1304    case XkbCF_UnterminatedString:
1305        msg = "unterminated string on line %d";
1306        break;
1307    case XkbCF_MissingIdent:
1308        msg = "expected identifier on line %d";
1309        break;
1310    case XkbCF_MissingEquals:
1311        msg = "expected '=' on line %d";
1312        break;
1313    case XkbCF_ExpectedEOS:
1314        msg = "expected ';' or newline on line %d";
1315        break;
1316    case XkbCF_ExpectedBoolean:
1317        msg = "expected a boolean value on line %d";
1318        break;
1319    case XkbCF_ExpectedInteger:
1320        msg = "expected a numeric value on line %d";
1321        break;
1322    case XkbCF_ExpectedString:
1323        msg = "expected a string on line %d";
1324        break;
1325    case XkbCF_ExpectedModifier:
1326        msg = "expected a modifier name on line %d";
1327        break;
1328    case XkbCF_ExpectedControl:
1329        msg = "expected a control name on line %d";
1330        break;
1331    case XkbCF_ExpectedAXOption:
1332        msg = "expected an AccessX option on line %d";
1333        break;
1334    case XkbCF_ExpectedOperator:
1335        msg = "expected '+' or '-' on line %d";
1336        break;
1337    case XkbCF_ExpectedOORGroupBehavior:
1338        msg = "expected wrap, clamp or group number on line %d";
1339        break;
1340    default:
1341        msg = "unknown error on line %d";
1342        break;
1343    }
1344    fprintf(file, msg, line);
1345    if (name)
1346        fprintf(file, " of %s\n", name);
1347    else
1348        fprintf(file, "\n");
1349    return;
1350}
1351