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