Git init
[framework/uifw/xorg/util/x11-xkb-utils.git] / setxkbmap / setxkbmap.c
1 /************************************************************
2  Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
3
4  Permission to use, copy, modify, and distribute this
5  software and its documentation for any purpose and without
6  fee is hereby granted, provided that the above copyright
7  notice appear in all copies and that both that copyright
8  notice and this permission notice appear in supporting
9  documentation, and that the name of Silicon Graphics not be 
10  used in advertising or publicity pertaining to distribution 
11  of the software without specific prior written permission.
12  Silicon Graphics makes no representation about the suitability 
13  of this software for any purpose. It is provided "as is"
14  without any express or implied warranty.
15  
16  SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
17  SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
18  AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19  GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
20  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
21  DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
22  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23  THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25  ********************************************************/
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <locale.h>
30 #include <limits.h>
31 #include <ctype.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xos.h>
34 #include <X11/XKBlib.h>
35 #include <X11/extensions/XKBfile.h>
36 #include <X11/extensions/XKBconfig.h>
37 #include <X11/extensions/XKBrules.h>
38
39 #ifndef PATH_MAX
40 #ifdef MAXPATHLEN
41 #define PATH_MAX MAXPATHLEN
42 #else
43 #define PATH_MAX 1024
44 #endif
45 #endif
46
47 #ifndef DFLT_XKB_CONFIG_ROOT
48 #define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
49 #endif
50 #ifndef DFLT_XKB_RULES_FILE
51 #define DFLT_XKB_RULES_FILE "base"
52 #endif
53 #ifndef DFLT_XKB_LAYOUT
54 #define DFLT_XKB_LAYOUT "us"
55 #endif
56 #ifndef DFLT_XKB_MODEL
57 #define DFLT_XKB_MODEL "pc105"
58 #endif
59
60 /* Values used in svSrc to state how a value was obtained. The order of these
61  * is important, the bigger the higher the priority.
62  * e.g. FROM_CONFIG overrides FROM_SERVER */
63 #define UNDEFINED       0
64 #define FROM_SERVER     1       /* retrieved from server at runtime */
65 #define FROM_RULES      2       /* xkb rules file */
66 #define FROM_CONFIG     3       /* command-line specified config file */
67 #define FROM_CMD_LINE   4       /* specified at the cmdline */
68 #define NUM_SOURCES     5
69
70 /* Indices used into svSrc, svNValue */
71 #define RULES_NDX       0       /* rules file */
72 #define CONFIG_NDX      1       /* config file (if used) */
73 #define DISPLAY_NDX     2       /* X display name */
74 #define LOCALE_NDX      3       /* machine's locale */
75 #define MODEL_NDX       4
76 #define LAYOUT_NDX      5
77 #define VARIANT_NDX     6
78 #define KEYCODES_NDX    7
79 #define TYPES_NDX       8
80 #define COMPAT_NDX      9
81 #define SYMBOLS_NDX     10
82 #define GEOMETRY_NDX    11
83 #define KEYMAP_NDX      12
84 #define NUM_STRING_VALS 13
85
86 /***====================================================================***/
87 static Bool print = False;
88 static Bool query = False;
89 static Bool synch = False;
90 static int verbose = 5;
91
92 static Display *dpy;
93
94 /**
95  * human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error
96  * reporting.
97  */
98 static char *srcName[NUM_SOURCES] = {
99     "undefined", "X server", "rules file", "config file", "command line"
100 };
101
102 /**
103  * human-readable versions for RULES_NDX, CONFIG_NDX, etc. Used for error
104  * reporting.
105  */
106 static char *svName[NUM_STRING_VALS] = {
107     "rules file", "config file", "X display", "locale",
108     "keyboard model", "keyboard layout", "layout variant",
109     "keycodes", "types", "compatibility map", "symbols", "geometry",
110     "keymap"
111 };
112 /**
113  * Holds the source for each of RULES, CONFIG, DISPLAY, etc.
114  * i.e. if svSrc[LAYOUT_NDX] == FROM_SERVER, then the layout has been fetched
115  * from the server.
116  */
117 static int svSrc[NUM_STRING_VALS];
118 /**
119  * Holds the value for each of RULES, CONFIG, DISPLAY, etc.
120  */
121 static char *svValue[NUM_STRING_VALS];
122
123 static XkbConfigRtrnRec cfgResult;
124
125 static XkbRF_RulesPtr rules = NULL;
126 static XkbRF_VarDefsRec rdefs;
127
128 static Bool clearOptions = False;
129 static int szOptions = 0;
130 static int numOptions = 0;
131 static char **options = NULL;
132
133 static int szInclPath = 0;
134 static int numInclPath = 0;
135 static char **inclPath = NULL;
136
137 static XkbDescPtr xkb = NULL;
138
139 static int deviceSpec = XkbUseCoreKbd;
140
141 /***====================================================================***/
142
143 #define streq(s1,s2)    (strcmp(s1,s2)==0)
144 #define strpfx(s1,s2)   (strncmp(s1,s2,strlen(s2))==0)
145
146 #define MSG(s)          printf(s)
147 #define MSG1(s,a)       printf(s,a)
148 #define MSG2(s,a,b)     printf(s,a,b)
149 #define MSG3(s,a,b,c)   printf(s,a,b,c)
150
151 #define VMSG(l,s)       if (verbose>(l)) printf(s)
152 #define VMSG1(l,s,a)    if (verbose>(l)) printf(s,a)
153 #define VMSG2(l,s,a,b)  if (verbose>(l)) printf(s,a,b)
154 #define VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c)
155
156 #define ERR(s)          fprintf(stderr,s)
157 #define ERR1(s,a)       fprintf(stderr,s,a)
158 #define ERR2(s,a,b)     fprintf(stderr,s,a,b)
159 #define ERR3(s,a,b,c)   fprintf(stderr,s,a,b,c)
160
161 /***====================================================================***/
162
163 Bool addToList(int *sz, int *num, char ***listIn, char *newVal);
164 void usage(int argc, char **argv);
165 void dumpNames(Bool wantRules, Bool wantCNames);
166 void trySetString(int which, char *newVal, int src);
167 Bool setOptString(int *arg, int argc, char **argv, int which, int src);
168 int parseArgs(int argc, char **argv);
169 Bool getDisplay(int argc, char **argv);
170 Bool getServerValues(void);
171 FILE *findFileInPath(char *name, char *subdir);
172 Bool addStringToOptions(char *opt_str, int *sz_opts, int *num_opts,
173                         char ***opts);
174 char *stringFromOptions(char *orig, int numNew, char **newOpts);
175 Bool applyConfig(char *name);
176 Bool applyRules(void);
177 Bool applyComponentNames(void);
178 void printKeymap(void);
179
180 /***====================================================================***/
181
182 Bool
183 addToList(int *sz, int *num, char ***listIn, char *newVal)
184 {
185     register int i;
186     char **list;
187
188     if ((!newVal) || (!newVal[0]))
189     {
190         *num = 0;
191         return True;
192     }
193     list = *listIn;
194     for (i = 0; i < *num; i++)
195     {
196         if (streq(list[i], newVal))
197             return True;
198     }
199     if ((list == NULL) || (*sz < 1))
200     {
201         *num = 0;
202         *sz = 4;
203         list = (char **) calloc(*sz, sizeof(char *));
204         *listIn = list;
205     }
206     else if (*num >= *sz)
207     {
208         *sz *= 2;
209         list = (char **) realloc(list, (*sz) * sizeof(char *));
210         *listIn = list;
211     }
212     if (!list)
213     {
214         ERR("Internal Error! Allocation failure in add to list!\n");
215         ERR("                Exiting.\n");
216         exit(-1);
217     }
218     list[*num] = strdup(newVal);
219     (*num) = (*num) + 1;
220     return True;
221 }
222
223 /***====================================================================***/
224
225 void
226 usage(int argc, char **argv)
227 {
228     MSG1("Usage: %s [args] [<layout> [<variant> [<option> ... ]]]\n",
229          argv[0]);
230     MSG("Where legal args are:\n");
231     MSG("-?,-help            Print this message\n");
232     MSG("-compat <name>      Specifies compatibility map component name\n");
233     MSG("-config <file>      Specifies configuration file to use\n");
234     MSG("-device <deviceid>  Specifies the device ID to use\n");
235     MSG("-display <dpy>      Specifies display to use\n");
236     MSG("-geometry <name>    Specifies geometry component name\n");
237     MSG("-I[<dir>]           Add <dir> to list of directories to be used\n");
238     MSG("-keycodes <name>    Specifies keycodes component name\n");
239     MSG("-keymap <name>      Specifies name of keymap to load\n");
240     MSG("-layout <name>      Specifies layout used to choose component names\n");
241     MSG("-model <name>       Specifies model used to choose component names\n");
242     MSG("-option <name>      Adds an option used to choose component names\n");
243     MSG("-print              Print a complete xkb_keymap description and exit\n");
244     MSG("-query              Print the current layout settings and exit\n");
245     MSG("-rules <name>       Name of rules file to use\n");
246     MSG("-symbols <name>     Specifies symbols component name\n");
247     MSG("-synch              Synchronize request w/X server\n");
248     MSG("-types <name>       Specifies types component name\n");
249     MSG("-v[erbose] [<lvl>]  Sets verbosity (1..10).  Higher values yield\n");
250     MSG("                    more messages\n");
251     MSG("-variant <name>     Specifies layout variant used to choose component names\n");
252 }
253
254 void
255 dumpNames(Bool wantRules, Bool wantCNames)
256 {
257     if (wantRules)
258     {
259         if (svValue[RULES_NDX])
260             MSG1("rules:      %s\n", svValue[RULES_NDX]);
261         if (svValue[MODEL_NDX])
262             MSG1("model:      %s\n", svValue[MODEL_NDX]);
263         if (svValue[LAYOUT_NDX])
264             MSG1("layout:     %s\n", svValue[LAYOUT_NDX]);
265         if (svValue[VARIANT_NDX])
266             MSG1("variant:    %s\n", svValue[VARIANT_NDX]);
267         if (options)
268         {
269             char *opt_str = stringFromOptions(NULL, numOptions, options);
270             MSG1("options:    %s\n", opt_str);
271             free(opt_str);
272         }
273     }
274     if (wantCNames)
275     {
276         if (svValue[KEYMAP_NDX])
277             MSG1("keymap:     %s\n", svValue[KEYMAP_NDX]);
278         if (svValue[KEYCODES_NDX])
279             MSG1("keycodes:   %s\n", svValue[KEYCODES_NDX]);
280         if (svValue[TYPES_NDX])
281             MSG1("types:      %s\n", svValue[TYPES_NDX]);
282         if (svValue[COMPAT_NDX])
283             MSG1("compat:     %s\n", svValue[COMPAT_NDX]);
284         if (svValue[SYMBOLS_NDX])
285             MSG1("symbols:    %s\n", svValue[SYMBOLS_NDX]);
286         if (svValue[GEOMETRY_NDX])
287             MSG1("geometry:   %s\n", svValue[GEOMETRY_NDX]);
288     }
289     return;
290 }
291
292 /***====================================================================***/
293
294 /**
295  * Set the given string (obtained from src) in the svValue/svSrc globals.
296  * If the given item is already set, it is overridden if the original source
297  * is less significant than the given one.
298  *
299  * @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...)
300  */
301 void
302 trySetString(int which, char *newVal, int src)
303 {
304     if (svValue[which] != NULL)
305     {
306         if (svSrc[which] == src)
307         {
308             VMSG2(0, "Warning! More than one %s from %s\n",
309                   svName[which], srcName[src]);
310             VMSG2(0, "         Using \"%s\", ignoring \"%s\"\n",
311                   svValue[which], newVal);
312             return;
313         }
314         else if (svSrc[which] > src)
315         {
316             VMSG1(5, "Warning! Multiple definitions of %s\n", svName[which]);
317             VMSG2(5, "         Using %s, ignoring %s\n",
318                   srcName[svSrc[which]], srcName[src]);
319             return;
320         }
321     }
322     svSrc[which] = src;
323     svValue[which] = newVal;
324     return;
325 }
326
327 Bool
328 setOptString(int *arg, int argc, char **argv, int which, int src)
329 {
330     int ndx;
331     char *opt;
332
333     ndx = *arg;
334     opt = argv[ndx];
335     if (ndx >= argc - 1)
336     {
337         VMSG1(0, "No %s specified on the command line\n", svName[which]);
338         VMSG1(0, "Trailing %s option ignored\n", opt);
339         return True;
340     }
341     ndx++;
342     *arg = ndx;
343     if (svValue[which] != NULL)
344     {
345         if (svSrc[which] == src)
346         {
347             VMSG2(0, "More than one %s on %s\n", svName[which], srcName[src]);
348             VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", svValue[which],
349                   argv[ndx]);
350             return True;
351         }
352         else if (svSrc[which] > src)
353         {
354             VMSG1(5, "Multiple definitions of %s\n", svName[which]);
355             VMSG2(5, "Using %s, ignoring %s\n", srcName[svSrc[which]],
356                   srcName[src]);
357             return True;
358         }
359     }
360     svSrc[which] = src;
361     svValue[which] = argv[ndx];
362     return True;
363 }
364
365 /***====================================================================***/
366
367 /**
368  * Parse commandline arguments.
369  * Return True on success or False if an unrecognized option has been
370  * specified.
371  */
372 int
373 parseArgs(int argc, char **argv)
374 {
375     int i;
376     Bool ok;
377     unsigned present;
378
379     ok = True;
380     addToList(&szInclPath, &numInclPath, &inclPath, ".");
381     addToList(&szInclPath, &numInclPath, &inclPath, DFLT_XKB_CONFIG_ROOT);
382     for (i = 1; (i < argc) && ok; i++)
383     {
384         if (argv[i][0] != '-')
385         {
386             /* Allow a call like "setxkbmap us" to work. Layout is default,
387                if -layout is given, then try parsing variant, then options */
388             if (!svSrc[LAYOUT_NDX])
389                 trySetString(LAYOUT_NDX, argv[i], FROM_CMD_LINE);
390             else if (!svSrc[VARIANT_NDX])
391                 trySetString(VARIANT_NDX, argv[i], FROM_CMD_LINE);
392             else
393                 ok = addToList(&szOptions, &numOptions, &options, argv[i]);
394         }
395         else if (streq(argv[i], "-compat"))
396             ok = setOptString(&i, argc, argv, COMPAT_NDX, FROM_CMD_LINE);
397         else if (streq(argv[i], "-config"))
398             ok = setOptString(&i, argc, argv, CONFIG_NDX, FROM_CMD_LINE);
399         else if (streq(argv[i], "-device"))
400             deviceSpec = atoi(argv[++i]); /* only allow device IDs, not names */
401         else if (streq(argv[i], "-display"))
402             ok = setOptString(&i, argc, argv, DISPLAY_NDX, FROM_CMD_LINE);
403         else if (streq(argv[i], "-geometry"))
404             ok = setOptString(&i, argc, argv, GEOMETRY_NDX, FROM_CMD_LINE);
405         else if (streq(argv[i], "-help") || streq(argv[i], "-?"))
406         {
407             usage(argc, argv);
408             exit(0);
409         }
410         else if (strpfx(argv[i], "-I"))
411             ok = addToList(&szInclPath, &numInclPath, &inclPath, &argv[i][2]);
412         else if (streq(argv[i], "-keycodes"))
413             ok = setOptString(&i, argc, argv, KEYCODES_NDX, FROM_CMD_LINE);
414         else if (streq(argv[i], "-keymap"))
415             ok = setOptString(&i, argc, argv, KEYMAP_NDX, FROM_CMD_LINE);
416         else if (streq(argv[i], "-layout"))
417             ok = setOptString(&i, argc, argv, LAYOUT_NDX, FROM_CMD_LINE);
418         else if (streq(argv[i], "-model"))
419             ok = setOptString(&i, argc, argv, MODEL_NDX, FROM_CMD_LINE);
420         else if (streq(argv[i], "-option"))
421         {
422             if ((i == argc - 1) || (argv[i + 1][0] == '\0')
423                 || (argv[i + 1][0] == '-'))
424             {
425                 clearOptions = True;
426                 ok = addToList(&szOptions, &numOptions, &options, "");
427                 if (i < argc - 1 && argv[i + 1][0] == '\0')
428                     i++;
429             }
430             else
431             {
432                 ok = addToList(&szOptions, &numOptions, &options, argv[++i]);
433             }
434         }
435         else if (streq(argv[i], "-print"))
436             print = True;
437         else if (streq(argv[i], "-query"))
438             query = True;
439         else if (streq(argv[i], "-rules"))
440             ok = setOptString(&i, argc, argv, RULES_NDX, FROM_CMD_LINE);
441         else if (streq(argv[i], "-symbols"))
442             ok = setOptString(&i, argc, argv, SYMBOLS_NDX, FROM_CMD_LINE);
443         else if (streq(argv[i], "-synch"))
444             synch = True;
445         else if (streq(argv[i], "-types"))
446             ok = setOptString(&i, argc, argv, TYPES_NDX, FROM_CMD_LINE);
447         else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v")))
448         {
449             if ((i < argc - 1) && (isdigit(argv[i + 1][0])))
450                 verbose = atoi(argv[++i]);
451             else
452                 verbose++;
453             if (verbose < 0)
454             {
455                 ERR1("Illegal verbose level %d.  Reset to 0\n", verbose);
456                 verbose = 0;
457             }
458             else if (verbose > 10)
459             {
460                 ERR1("Illegal verbose level %d.  Reset to 10\n", verbose);
461                 verbose = 10;
462             }
463             VMSG1(7, "Setting verbose level to %d\n", verbose);
464         }
465         else if (streq(argv[i], "-variant"))
466             ok = setOptString(&i, argc, argv, VARIANT_NDX, FROM_CMD_LINE);
467         else
468         {
469             ERR1("Error!   Option \"%s\" not recognized\n", argv[i]);
470             ok = False;
471         }
472     }
473
474     present = 0;
475     if (svValue[TYPES_NDX])
476         present++;
477     if (svValue[COMPAT_NDX])
478         present++;
479     if (svValue[SYMBOLS_NDX])
480         present++;
481     if (svValue[KEYCODES_NDX])
482         present++;
483     if (svValue[GEOMETRY_NDX])
484         present++;
485     if (svValue[CONFIG_NDX])
486         present++;
487     if (svValue[MODEL_NDX])
488         present++;
489     if (svValue[LAYOUT_NDX])
490         present++;
491     if (svValue[VARIANT_NDX])
492         present++;
493     if (svValue[KEYMAP_NDX] && present)
494     {
495         ERR("No other components can be specified when a keymap is present\n");
496         return False;
497     }
498     return ok;
499 }
500
501 /**
502  * Open a connection to the display and print error if it fails.
503  *
504  * @return True on success or False otherwise.
505  */
506 Bool
507 getDisplay(int argc, char **argv)
508 {
509     int major, minor, why;
510
511     major = XkbMajorVersion;
512     minor = XkbMinorVersion;
513     dpy =
514         XkbOpenDisplay(svValue[DISPLAY_NDX], NULL, NULL, &major, &minor,
515                        &why);
516     if (!dpy)
517     {
518         if (svValue[DISPLAY_NDX] == NULL)
519             svValue[DISPLAY_NDX] = getenv("DISPLAY");
520         if (svValue[DISPLAY_NDX] == NULL)
521             svValue[DISPLAY_NDX] = "default display";
522         switch (why)
523         {
524         case XkbOD_BadLibraryVersion:
525             ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
526                  XkbMajorVersion, XkbMinorVersion);
527             ERR2("Xlib supports incompatible version %d.%02d\n",
528                  major, minor);
529             break;
530         case XkbOD_ConnectionRefused:
531             ERR1("Cannot open display \"%s\"\n", svValue[DISPLAY_NDX]);
532             break;
533         case XkbOD_NonXkbServer:
534             ERR1("XKB extension not present on %s\n", svValue[DISPLAY_NDX]);
535             break;
536         case XkbOD_BadServerVersion:
537             ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
538                  XkbMajorVersion, XkbMinorVersion);
539             ERR3("Server %s uses incompatible version %d.%02d\n",
540                  svValue[DISPLAY_NDX], major, minor);
541             break;
542         default:
543             ERR1("Unknown error %d from XkbOpenDisplay\n", why);
544             break;
545         }
546         return False;
547     }
548     if (synch)
549         XSynchronize(dpy, True);
550     return True;
551 }
552
553 /***====================================================================***/
554
555 /**
556  * Retrieve xkb values from the XKB_RULES_NAMES property and store their
557  * contents in svValues.
558  * If the property cannot be read, the built-in defaults are used.
559  *
560  * @return True.
561  */
562 Bool
563 getServerValues(void)
564 {
565     XkbRF_VarDefsRec vd;
566     char *tmp = NULL;
567
568     if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp)
569     {
570         VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM);
571         tmp = DFLT_XKB_RULES_FILE;
572         vd.model = DFLT_XKB_MODEL;
573         vd.layout = DFLT_XKB_LAYOUT;
574         vd.variant = NULL;
575         vd.options = NULL;
576         VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n",
577               tmp, vd.model, vd.layout);
578     }
579     if (tmp)
580         trySetString(RULES_NDX, tmp, FROM_SERVER);
581     if (vd.model)
582         trySetString(MODEL_NDX, vd.model, FROM_SERVER);
583     if (vd.layout)
584         trySetString(LAYOUT_NDX, vd.layout, FROM_SERVER);
585     if (vd.variant)
586         trySetString(VARIANT_NDX, vd.variant, FROM_SERVER);
587     if ((vd.options) && (!clearOptions))
588     {
589         addStringToOptions(vd.options, &szOptions, &numOptions, &options);
590         XFree(vd.options);
591     }
592     return True;
593 }
594
595 /***====================================================================***/
596
597 FILE *
598 findFileInPath(char *name, char *subdir)
599 {
600     register int i;
601     char buf[PATH_MAX];
602     FILE *fp;
603
604     if (name[0] == '/')
605     {
606         fp = fopen(name, "r");
607         if ((verbose > 7) || ((!fp) && (verbose > 0)))
608             MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name);
609         return fp;
610     }
611     for (i = 0; (i < numInclPath); i++)
612     {
613         if ((strlen(inclPath[i]) + strlen(subdir) + strlen(name) + 2) >
614             PATH_MAX)
615         {
616             VMSG3(0, "Path too long (%s/%s%s). Ignored.\n", inclPath[i],
617                   subdir, name);
618             continue;
619         }
620         sprintf(buf, "%s/%s%s", inclPath[i], subdir, name);
621         fp = fopen(name, "r");
622         if ((verbose > 7) || ((!fp) && (verbose > 5)))
623             MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf);
624         if (fp != NULL)
625             return fp;
626     }
627     return NULL;
628 }
629
630 /***====================================================================***/
631
632 Bool
633 addStringToOptions(char *opt_str, int *sz_opts, int *num_opts, char ***opts)
634 {
635     char *tmp, *str, *next;
636     Bool ok = True;
637
638     if ((str = strdup(opt_str)) == NULL)
639         return False;
640     for (tmp = str, next = NULL; (tmp && *tmp != '\0') && ok; tmp = next)
641     {
642         next = strchr(str, ',');
643         if (next)
644         {
645             *next = '\0';
646             next++;
647         }
648         ok = addToList(sz_opts, num_opts, opts, tmp) && ok;
649     }
650     free(str);
651     return ok;
652 }
653
654 /***====================================================================***/
655
656 char *
657 stringFromOptions(char *orig, int numNew, char **newOpts)
658 {
659     int len, i, nOut;
660
661     if (orig)
662         len = strlen(orig) + 1;
663     else
664         len = 0;
665     for (i = 0; i < numNew; i++)
666     {
667         if (newOpts[i])
668             len += strlen(newOpts[i]) + 1;
669     }
670     if (len < 1)
671         return NULL;
672     if (orig)
673     {
674         orig = (char *) realloc(orig, len);
675         if (!orig)
676         {
677             ERR("OOM in stringFromOptions\n");
678             return NULL;
679         }
680         nOut = 1;
681     }
682     else
683     {
684         orig = (char *) calloc(len, 1);
685         if (!orig)
686         {
687             ERR("OOM in stringFromOptions\n");
688             return NULL;
689         }
690         nOut = 0;
691     }
692     for (i = 0; i < numNew; i++)
693     {
694         if (!newOpts[i])
695             continue;
696         if (nOut > 0)
697         {
698             strcat(orig, ",");
699             strcat(orig, newOpts[i]);
700         }
701         else
702             strcpy(orig, newOpts[i]);
703         nOut++;
704     }
705     return orig;
706 }
707
708 /***====================================================================***/
709
710 Bool
711 applyConfig(char *name)
712 {
713     FILE *fp;
714     Bool ok;
715
716     if ((fp = findFileInPath(name, "")) == NULL)
717         return False;
718     ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
719     fclose(fp);
720     if (!ok)
721     {
722         ERR1("Couldn't find configuration file \"%s\"\n", name);
723         return False;
724     }
725     if (cfgResult.rules_file)
726     {
727         trySetString(RULES_NDX, cfgResult.rules_file, FROM_CONFIG);
728         cfgResult.rules_file = NULL;
729     }
730     if (cfgResult.model)
731     {
732         trySetString(MODEL_NDX, cfgResult.model, FROM_CONFIG);
733         cfgResult.model = NULL;
734     }
735     if (cfgResult.layout)
736     {
737         trySetString(LAYOUT_NDX, cfgResult.layout, FROM_CONFIG);
738         cfgResult.layout = NULL;
739     }
740     if (cfgResult.variant)
741     {
742         trySetString(VARIANT_NDX, cfgResult.variant, FROM_CONFIG);
743         cfgResult.variant = NULL;
744     }
745     if (cfgResult.options)
746     {
747         addStringToOptions(cfgResult.options, &szOptions, &numOptions,
748                            &options);
749         cfgResult.options = NULL;
750     }
751     if (cfgResult.keymap)
752     {
753         trySetString(KEYMAP_NDX, cfgResult.keymap, FROM_CONFIG);
754         cfgResult.keymap = NULL;
755     }
756     if (cfgResult.keycodes)
757     {
758         trySetString(KEYCODES_NDX, cfgResult.keycodes, FROM_CONFIG);
759         cfgResult.keycodes = NULL;
760     }
761     if (cfgResult.geometry)
762     {
763         trySetString(GEOMETRY_NDX, cfgResult.geometry, FROM_CONFIG);
764         cfgResult.geometry = NULL;
765     }
766     if (cfgResult.symbols)
767     {
768         trySetString(SYMBOLS_NDX, cfgResult.symbols, FROM_CONFIG);
769         cfgResult.symbols = NULL;
770     }
771     if (cfgResult.types)
772     {
773         trySetString(TYPES_NDX, cfgResult.types, FROM_CONFIG);
774         cfgResult.types = NULL;
775     }
776     if (cfgResult.compat)
777     {
778         trySetString(COMPAT_NDX, cfgResult.compat, FROM_CONFIG);
779         cfgResult.compat = NULL;
780     }
781     if (verbose > 5)
782     {
783         MSG("After config file:\n");
784         dumpNames(True, True);
785     }
786     return True;
787 }
788
789 /**
790  * If any of model, layout, variant or options is specified, then compile the
791  * options into the
792  *
793  * @return True on success or false otherwise.
794  */
795 Bool
796 applyRules(void)
797 {
798     int i;
799     char *rfName;
800
801     if (svSrc[MODEL_NDX] || svSrc[LAYOUT_NDX] || svSrc[VARIANT_NDX]
802         || options)
803     {
804         char buf[PATH_MAX];
805         XkbComponentNamesRec rnames;
806
807         if (svSrc[VARIANT_NDX] < svSrc[LAYOUT_NDX])
808             svValue[VARIANT_NDX] = NULL;
809
810         rdefs.model = svValue[MODEL_NDX];
811         rdefs.layout = svValue[LAYOUT_NDX];
812         rdefs.variant = svValue[VARIANT_NDX];
813         if (options)
814             rdefs.options =
815                 stringFromOptions(rdefs.options, numOptions, options);
816
817         if (svSrc[RULES_NDX])
818             rfName = svValue[RULES_NDX];
819         else
820             rfName = DFLT_XKB_RULES_FILE;
821
822         if (rfName[0] == '/')
823         {
824             rules = XkbRF_Load(rfName, svValue[LOCALE_NDX], True, True);
825         }
826         else
827         {
828             /* try to load rules files from all include paths until the first
829              * we succeed with */
830             for (i = 0; (i < numInclPath) && (!rules); i++)
831             {
832                 if ((strlen(inclPath[i]) + strlen(rfName) + 8) > PATH_MAX)
833                 {
834                     VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
835                           inclPath[i], rfName);
836                     continue;
837                 }
838                 sprintf(buf, "%s/rules/%s", inclPath[i], svValue[RULES_NDX]);
839                 rules = XkbRF_Load(buf, svValue[LOCALE_NDX], True, True);
840             }
841         }
842         if (!rules)
843         {
844             ERR1("Couldn't find rules file (%s) \n", svValue[RULES_NDX]);
845             return False;
846         }
847         /* Let the rules file to the magic, then update the svValues with
848          * those returned after processing the rules */
849         XkbRF_GetComponents(rules, &rdefs, &rnames);
850         if (rnames.keycodes)
851         {
852             trySetString(KEYCODES_NDX, rnames.keycodes, FROM_RULES);
853             rnames.keycodes = NULL;
854         }
855         if (rnames.symbols)
856         {
857             trySetString(SYMBOLS_NDX, rnames.symbols, FROM_RULES);
858             rnames.symbols = NULL;
859         }
860         if (rnames.types)
861         {
862             trySetString(TYPES_NDX, rnames.types, FROM_RULES);
863             rnames.types = NULL;
864         }
865         if (rnames.compat)
866         {
867             trySetString(COMPAT_NDX, rnames.compat, FROM_RULES);
868             rnames.compat = NULL;
869         }
870         if (rnames.geometry)
871         {
872             trySetString(GEOMETRY_NDX, rnames.geometry, FROM_RULES);
873             rnames.geometry = NULL;
874         }
875         if (rnames.keymap)
876         {
877             trySetString(KEYMAP_NDX, rnames.keymap, FROM_RULES);
878             rnames.keymap = NULL;
879         }
880         if (verbose > 6)
881         {
882             MSG1("Applied rules from %s:\n", svValue[RULES_NDX]);
883             dumpNames(True, False);
884         }
885     }
886     else if (verbose > 6)
887     {
888         MSG("No rules variables specified.  Rules file ignored\n");
889     }
890     return True;
891 }
892
893 /* Primitive sanity check - filter out 'map names' (inside parenthesis) */
894 /* that can confuse xkbcomp parser */
895 static Bool
896 checkName(char *name, char *string)
897 {
898     char *i = name, *opar = NULL;
899     Bool ret = True;
900
901     if (!name)
902         return True;
903
904     while (*i)
905     {
906         if (opar == NULL)
907         {
908             if (*i == '(')
909                 opar = i;
910         }
911         else
912         {
913             if ((*i == '(') || (*i == '|') || (*i == '+'))
914             {
915                 ret = False;
916                 break;
917             }
918             if (*i == ')')
919                 opar = NULL;
920         }
921         i++;
922     }
923     if (opar)
924         ret = False;
925     if (!ret)
926     {
927         char c;
928         int n = 1;
929         for (i = opar + 1; *i && n; i++)
930         {
931             if (*i == '(')
932                 n++;
933             if (*i == ')')
934                 n--;
935         }
936         if (*i)
937             i++;
938         c = *i;
939         *i = '\0';
940         ERR1("Illegal map name '%s' ", opar);
941         *i = c;
942         ERR2("in %s name '%s'\n", string, name);
943     }
944     return ret;
945 }
946
947 void
948 printKeymap(void)
949 {
950     MSG("xkb_keymap {\n");
951     if (svValue[KEYCODES_NDX])
952         MSG1("\txkb_keycodes  { include \"%s\"\t};\n", svValue[KEYCODES_NDX]);
953     if (svValue[TYPES_NDX])
954         MSG1("\txkb_types     { include \"%s\"\t};\n", svValue[TYPES_NDX]);
955     if (svValue[COMPAT_NDX])
956         MSG1("\txkb_compat    { include \"%s\"\t};\n", svValue[COMPAT_NDX]);
957     if (svValue[SYMBOLS_NDX])
958         MSG1("\txkb_symbols   { include \"%s\"\t};\n", svValue[SYMBOLS_NDX]);
959     if (svValue[GEOMETRY_NDX])
960         MSG1("\txkb_geometry  { include \"%s\"\t};\n", svValue[GEOMETRY_NDX]);
961     MSG("};\n");
962 }
963
964 Bool
965 applyComponentNames(void)
966 {
967     if (!checkName(svValue[TYPES_NDX], "types"))
968         return False;
969     if (!checkName(svValue[COMPAT_NDX], "compat"))
970         return False;
971     if (!checkName(svValue[SYMBOLS_NDX], "symbols"))
972         return False;
973     if (!checkName(svValue[KEYCODES_NDX], "keycodes"))
974         return False;
975     if (!checkName(svValue[GEOMETRY_NDX], "geometry"))
976         return False;
977     if (!checkName(svValue[KEYMAP_NDX], "keymap"))
978         return False;
979
980     if (verbose > 5)
981     {
982         MSG("Trying to build keymap using the following components:\n");
983         dumpNames(False, True);
984     }
985     /* Upload the new description to the server. */
986     if (dpy && !print && !query)
987     {
988         XkbComponentNamesRec cmdNames;
989         cmdNames.types = svValue[TYPES_NDX];
990         cmdNames.compat = svValue[COMPAT_NDX];
991         cmdNames.symbols = svValue[SYMBOLS_NDX];
992         cmdNames.keycodes = svValue[KEYCODES_NDX];
993         cmdNames.geometry = svValue[GEOMETRY_NDX];
994         cmdNames.keymap = svValue[KEYMAP_NDX];
995         xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
996                                    XkbGBN_AllComponentsMask,
997                                    XkbGBN_AllComponentsMask &
998                                    (~XkbGBN_GeometryMask), True);
999         if (!xkb)
1000         {
1001             ERR("Error loading new keyboard description\n");
1002             return False;
1003         }
1004         /* update the XKB root property */
1005         if (svValue[RULES_NDX] && (rdefs.model || rdefs.layout))
1006         {
1007             if (!XkbRF_SetNamesProp(dpy, svValue[RULES_NDX], &rdefs))
1008             {
1009                 VMSG(0, "Error updating the XKB names property\n");
1010             }
1011         }
1012     }
1013     if (print)
1014     {
1015         printKeymap();
1016     }
1017     if (query)
1018     {
1019         dumpNames(True, False);
1020     }
1021     return True;
1022 }
1023
1024
1025 int
1026 main(int argc, char **argv)
1027 {
1028     if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
1029         exit(-1);
1030     svValue[LOCALE_NDX] = setlocale(LC_ALL, svValue[LOCALE_NDX]);
1031     svSrc[LOCALE_NDX] = FROM_SERVER;
1032     VMSG1(7, "locale is %s\n", svValue[LOCALE_NDX]);
1033     if (dpy)
1034         getServerValues();
1035     if (svValue[CONFIG_NDX] && (!applyConfig(svValue[CONFIG_NDX])))
1036         exit(-3);
1037     if (!applyRules())
1038         exit(-4);
1039     if (!applyComponentNames())
1040         exit(-5);
1041     if (dpy)
1042         XCloseDisplay(dpy);
1043     exit(0);
1044 }