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