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