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