setxkbmap.c revision b8414663
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 return False; 746 ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult); 747 fclose(fp); 748 if (!ok) 749 { 750 ERR1("Couldn't find configuration file \"%s\"\n", name); 751 return False; 752 } 753 if (cfgResult.rules_file) 754 { 755 trySetString(&settings.rules, cfgResult.rules_file, FROM_CONFIG); 756 cfgResult.rules_file = NULL; 757 } 758 if (cfgResult.model) 759 { 760 trySetString(&settings.model, cfgResult.model, FROM_CONFIG); 761 cfgResult.model = NULL; 762 } 763 if (cfgResult.layout) 764 { 765 trySetString(&settings.layout, cfgResult.layout, FROM_CONFIG); 766 cfgResult.layout = NULL; 767 } 768 if (cfgResult.variant) 769 { 770 trySetString(&settings.variant, cfgResult.variant, FROM_CONFIG); 771 cfgResult.variant = NULL; 772 } 773 if (cfgResult.options) 774 { 775 addStringToOptions(cfgResult.options, &options); 776 cfgResult.options = NULL; 777 } 778 if (cfgResult.keymap) 779 { 780 trySetString(&settings.keymap, cfgResult.keymap, FROM_CONFIG); 781 cfgResult.keymap = NULL; 782 } 783 if (cfgResult.keycodes) 784 { 785 trySetString(&settings.keycodes, cfgResult.keycodes, FROM_CONFIG); 786 cfgResult.keycodes = NULL; 787 } 788 if (cfgResult.geometry) 789 { 790 trySetString(&settings.geometry, cfgResult.geometry, FROM_CONFIG); 791 cfgResult.geometry = NULL; 792 } 793 if (cfgResult.symbols) 794 { 795 trySetString(&settings.symbols, cfgResult.symbols, FROM_CONFIG); 796 cfgResult.symbols = NULL; 797 } 798 if (cfgResult.types) 799 { 800 trySetString(&settings.types, cfgResult.types, FROM_CONFIG); 801 cfgResult.types = NULL; 802 } 803 if (cfgResult.compat) 804 { 805 trySetString(&settings.compat, cfgResult.compat, FROM_CONFIG); 806 cfgResult.compat = NULL; 807 } 808 if (verbose > 5) 809 { 810 MSG("After config file:\n"); 811 dumpNames(True, True); 812 } 813 return True; 814} 815 816XkbRF_RulesPtr 817tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules) 818{ 819 XkbRF_RulesPtr rules = NULL; 820 VMSG1(7, "Trying to load rules file %s...\n", name); 821 rules = XkbRF_Load(name, locale, wantDesc, wantRules); 822 if (rules) 823 { 824 VMSG(7, "Success.\n"); 825 } 826 return rules; 827} 828 829/** 830 * If any of model, layout, variant or options is specified, then compile the 831 * options into the 832 * 833 * @return True on success or false otherwise. 834 */ 835Bool 836applyRules(void) 837{ 838 int i; 839 char *rfName; 840 XkbRF_RulesPtr rules = NULL; 841 842 if (settings.model.src || settings.layout.src || settings.variant.src 843 || options.item) 844 { 845 char buf[PATH_MAX]; 846 XkbComponentNamesRec rnames; 847 848 if (settings.variant.src < settings.layout.src) 849 settings.variant.value = NULL; 850 851 rdefs.model = settings.model.value; 852 rdefs.layout = settings.layout.value; 853 rdefs.variant = settings.variant.value; 854 if (options.item) 855 rdefs.options = 856 stringFromOptions(rdefs.options, &options); 857 858 if (settings.rules.src) 859 rfName = settings.rules.value; 860 else 861 rfName = DFLT_XKB_RULES_FILE; 862 863 if (rfName[0] == '/') 864 { 865 rules = tryLoadRules(rfName, settings.locale.value, True, True); 866 } 867 else 868 { 869 /* try to load rules files from all include paths until the first 870 * we succeed with */ 871 for (i = 0; (i < inclPath.num) && (!rules); i++) 872 { 873 if (snprintf(buf, PATH_MAX, "%s/rules/%s", 874 inclPath.item[i], rfName) >= PATH_MAX) 875 { 876 VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n", 877 inclPath.item[i], rfName); 878 continue; 879 } 880 rules = tryLoadRules(buf, settings.locale.value, True, True); 881 } 882 } 883 if (!rules) 884 { 885 ERR1("Couldn't find rules file (%s) \n", rfName); 886 return False; 887 } 888 /* Let the rules file to the magic, then update the svValues with 889 * those returned after processing the rules */ 890 XkbRF_GetComponents(rules, &rdefs, &rnames); 891 if (rnames.keycodes) 892 { 893 trySetString(&settings.keycodes, rnames.keycodes, FROM_RULES); 894 rnames.keycodes = NULL; 895 } 896 if (rnames.symbols) 897 { 898 trySetString(&settings.symbols, rnames.symbols, FROM_RULES); 899 rnames.symbols = NULL; 900 } 901 if (rnames.types) 902 { 903 trySetString(&settings.types, rnames.types, FROM_RULES); 904 rnames.types = NULL; 905 } 906 if (rnames.compat) 907 { 908 trySetString(&settings.compat, rnames.compat, FROM_RULES); 909 rnames.compat = NULL; 910 } 911 if (rnames.geometry) 912 { 913 trySetString(&settings.geometry, rnames.geometry, FROM_RULES); 914 rnames.geometry = NULL; 915 } 916 if (rnames.keymap) 917 { 918 trySetString(&settings.keymap, rnames.keymap, FROM_RULES); 919 rnames.keymap = NULL; 920 } 921 if (verbose > 6) 922 { 923 MSG1("Applied rules from %s:\n", rfName); 924 dumpNames(True, False); 925 } 926 } 927 else if (verbose > 6) 928 { 929 MSG("No rules variables specified. Rules file ignored\n"); 930 } 931 return True; 932} 933 934/* Primitive sanity check - filter out 'map names' (inside parenthesis) */ 935/* that can confuse xkbcomp parser */ 936static Bool 937checkName(char *name, const char *string) 938{ 939 char *i = name, *opar = NULL; 940 Bool ret = True; 941 942 if (!name) 943 return True; 944 945 while (*i) 946 { 947 if (opar == NULL) 948 { 949 if (*i == '(') 950 opar = i; 951 } 952 else 953 { 954 if ((*i == '(') || (*i == '|') || (*i == '+')) 955 { 956 ret = False; 957 break; 958 } 959 if (*i == ')') 960 opar = NULL; 961 } 962 i++; 963 } 964 if (opar) 965 ret = False; 966 if (!ret) 967 { 968 char c; 969 int n = 1; 970 for (i = opar + 1; *i && n; i++) 971 { 972 if (*i == '(') 973 n++; 974 if (*i == ')') 975 n--; 976 } 977 if (*i) 978 i++; 979 c = *i; 980 *i = '\0'; 981 ERR1("Illegal map name '%s' ", opar); 982 *i = c; 983 ERR2("in %s name '%s'\n", string, name); 984 } 985 return ret; 986} 987 988void 989printKeymap(void) 990{ 991 MSG("xkb_keymap {\n"); 992 if (settings.keycodes.value) 993 MSG1("\txkb_keycodes { include \"%s\"\t};\n", settings.keycodes.value); 994 if (settings.types.value) 995 MSG1("\txkb_types { include \"%s\"\t};\n", settings.types.value); 996 if (settings.compat.value) 997 MSG1("\txkb_compat { include \"%s\"\t};\n", settings.compat.value); 998 if (settings.symbols.value) 999 MSG1("\txkb_symbols { include \"%s\"\t};\n", settings.symbols.value); 1000 if (settings.geometry.value) 1001 MSG1("\txkb_geometry { include \"%s\"\t};\n", settings.geometry.value); 1002 MSG("};\n"); 1003} 1004 1005Bool 1006applyComponentNames(void) 1007{ 1008 if (!checkName(settings.types.value, "types")) 1009 return False; 1010 if (!checkName(settings.compat.value, "compat")) 1011 return False; 1012 if (!checkName(settings.symbols.value, "symbols")) 1013 return False; 1014 if (!checkName(settings.keycodes.value, "keycodes")) 1015 return False; 1016 if (!checkName(settings.geometry.value, "geometry")) 1017 return False; 1018 if (!checkName(settings.keymap.value, "keymap")) 1019 return False; 1020 1021 if (verbose > 5) 1022 { 1023 MSG("Trying to build keymap using the following components:\n"); 1024 dumpNames(False, True); 1025 } 1026 /* Upload the new description to the server. */ 1027 if (dpy && !print && !query) 1028 { 1029 XkbComponentNamesRec cmdNames = { 1030 .keymap = settings.keymap.value, 1031 .keycodes = settings.keycodes.value, 1032 .types = settings.types.value, 1033 .compat = settings.compat.value, 1034 .symbols = settings.symbols.value, 1035 .geometry = settings.geometry.value 1036 }; 1037 1038 xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames, 1039 XkbGBN_AllComponentsMask, 1040 XkbGBN_AllComponentsMask & 1041 (~XkbGBN_GeometryMask), True); 1042 if (!xkb) 1043 { 1044 ERR("Error loading new keyboard description\n"); 1045 return False; 1046 } 1047 /* update the XKB root property */ 1048 if (settings.rules.value && (rdefs.model || rdefs.layout)) 1049 { 1050 if (!XkbRF_SetNamesProp(dpy, settings.rules.value, &rdefs)) 1051 { 1052 VMSG(0, "Error updating the XKB names property\n"); 1053 } 1054 } 1055 } 1056 if (print) 1057 { 1058 printKeymap(); 1059 } 1060 if (query) 1061 { 1062 dumpNames(True, False); 1063 } 1064 return True; 1065} 1066 1067 1068int 1069main(int argc, char **argv) 1070{ 1071 if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv))) 1072 exit(-1); 1073 settings.locale.value = setlocale(LC_ALL, settings.locale.value); 1074 settings.locale.src = FROM_SERVER; 1075 VMSG1(7, "locale is %s\n", settings.locale.value); 1076 if (dpy) 1077 getServerValues(); 1078 if (settings.config.value && (!applyConfig(settings.config.value))) 1079 exit(-3); 1080 if (!applyRules()) 1081 exit(-4); 1082 if (!applyComponentNames()) 1083 exit(-5); 1084 if (dpy) 1085 XCloseDisplay(dpy); 1086 exit(0); 1087} 1088