xkbcomp.c revision b7d0e977
1/************************************************************ 2 Copyright (c) 1994 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 <ctype.h> 29#include <X11/keysym.h> 30 31/* for symlink attack security fix -- Branden Robinson */ 32#include <sys/stat.h> 33#include <sys/types.h> 34#include <unistd.h> 35/* end BR */ 36 37#define DEBUG_VAR debugFlags 38#include "xkbcomp.h" 39#include <stdlib.h> 40#include "xkbpath.h" 41#include "parseutils.h" 42#include "misc.h" 43#include "tokens.h" 44#include <X11/extensions/XKBgeom.h> 45 46 47#ifdef WIN32 48#define S_IRGRP 0 49#define S_IWGRP 0 50#define S_IROTH 0 51#define S_IWOTH 0 52#endif 53 54#define lowbit(x) ((x) & (-(x))) 55 56/***====================================================================***/ 57 58#define WANT_DEFAULT 0 59#define WANT_XKM_FILE 1 60#define WANT_C_HDR 2 61#define WANT_XKB_FILE 3 62#define WANT_X_SERVER 4 63#define WANT_LISTING 5 64 65#define INPUT_UNKNOWN 0 66#define INPUT_XKB 1 67#define INPUT_XKM 2 68 69#ifdef DEBUG 70unsigned int debugFlags; 71#endif 72 73static const char *fileTypeExt[] = { 74 "XXX", 75 "xkm", 76 "h", 77 "xkb", 78 "dir" 79}; 80 81static unsigned inputFormat, outputFormat; 82static const char *rootDir; 83static char *inputFile; 84static const char *inputMap; 85static char *outputFile; 86static const char *inDpyName; 87static const char *outDpyName; 88static Display *inDpy; 89static Display *outDpy; 90static Bool showImplicit = False; 91static Bool synch = False; 92static Bool computeDflts = False; 93static Bool xkblist = False; 94unsigned warningLevel = 5; 95unsigned verboseLevel = 0; 96unsigned dirsToStrip = 0; 97static unsigned optionalParts = 0; 98static const char *preErrorMsg = NULL; 99static const char *postErrorMsg = NULL; 100static const char *errorPrefix = NULL; 101static unsigned int device_id = XkbUseCoreKbd; 102 103/***====================================================================***/ 104 105#define M(m) fprintf(stderr,(m)) 106#define M1(m,a) fprintf(stderr,(m),(a)) 107 108static void 109Usage(int argc, char *argv[]) 110{ 111 if (!xkblist) 112 M1("Usage: %s [options] input-file [ output-file ]\n", argv[0]); 113 else 114 M1("Usage: %s [options] file[(map)] ...\n", argv[0]); 115 M("Legal options:\n"); 116 M("-?,-help Print this message\n"); 117 M("-version Print the version number\n"); 118 if (!xkblist) 119 { 120 M("-a Show all actions\n"); 121 M("-C Create a C header file\n"); 122 } 123#ifdef DEBUG 124 M("-d [flags] Report debugging information\n"); 125#endif 126 M("-em1 <msg> Print <msg> before printing first error message\n"); 127 M("-emp <msg> Print <msg> at the start of each message line\n"); 128 M("-eml <msg> If there were any errors, print <msg> before exiting\n"); 129 if (!xkblist) 130 { 131 M("-dflts Compute defaults for missing parts\n"); 132 M("-I[<dir>] Specifies a top level directory for include\n"); 133 M(" directives. Multiple directories are legal.\n"); 134 M("-l [flags] List matching maps in the specified files\n"); 135 M(" f: list fully specified names\n"); 136 M(" h: also list hidden maps\n"); 137 M(" l: long listing (show flags)\n"); 138 M(" p: also list partial maps\n"); 139 M(" R: recursively list subdirectories\n"); 140 M(" default is all options off\n"); 141 } 142 M("-i <deviceid> Specifies device ID (not name) to compile for\n"); 143 M("-m[ap] <map> Specifies map to compile\n"); 144 M("-o <file> Specifies output file name\n"); 145 if (!xkblist) 146 { 147 M("-opt[ional] <parts> Specifies optional components of keymap\n"); 148 M(" Errors in optional parts are not fatal\n"); 149 M(" <parts> can be any combination of:\n"); 150 M(" c: compat map g: geometry\n"); 151 M(" k: keycodes s: symbols\n"); 152 M(" t: types\n"); 153 } 154 if (xkblist) 155 { 156 M("-p <count> Specifies the number of slashes to be stripped\n"); 157 M(" from the front of the map name on output\n"); 158 } 159 M("-R[<DIR>] Specifies the root directory for\n"); 160 M(" relative path names\n"); 161 M("-synch Force synchronization\n"); 162 if (xkblist) 163 { 164 M("-v [<flags>] Set level of detail for listing.\n"); 165 M(" flags are as for the -l option\n"); 166 } 167 M("-w [<lvl>] Set warning level (0=none, 10=all)\n"); 168 if (!xkblist) 169 { 170 M("-xkb Create an XKB source (.xkb) file\n"); 171 M("-xkm Create a compiled key map (.xkm) file\n"); 172 } 173 return; 174} 175 176/***====================================================================***/ 177 178static void 179setVerboseFlags(const char *str) 180{ 181 for (; *str; str++) 182 { 183 switch (*str) 184 { 185 case 'f': 186 verboseLevel |= WantFullNames; 187 break; 188 case 'h': 189 verboseLevel |= WantHiddenMaps; 190 break; 191 case 'l': 192 verboseLevel |= WantLongListing; 193 break; 194 case 'p': 195 verboseLevel |= WantPartialMaps; 196 break; 197 case 'R': 198 verboseLevel |= ListRecursive; 199 break; 200 default: 201 if (warningLevel > 4) 202 { 203 WARN("Unknown verbose option \"%c\"\n", (unsigned int) *str); 204 ACTION("Ignored\n"); 205 } 206 break; 207 } 208 } 209 return; 210} 211 212static Bool 213parseArgs(int argc, char *argv[]) 214{ 215 int i, tmp; 216 217 i = strlen(argv[0]); 218 tmp = strlen("xkblist"); 219 if ((i >= tmp) && (strcmp(&argv[0][i - tmp], "xkblist") == 0)) 220 { 221 xkblist = True; 222 } 223 for (i = 1; i < argc; i++) 224 { 225 int itmp; 226 if ((argv[i][0] != '-') || (strcmp(argv[i], "-") == 0)) 227 { 228 if (!xkblist) 229 { 230 if (inputFile == NULL) 231 inputFile = argv[i]; 232 else if (outputFile == NULL) 233 outputFile = argv[i]; 234 else if (warningLevel > 0) 235 { 236 WARN("Too many file names on command line\n"); 237 ACTION 238 ("Compiling %s, writing to %s, ignoring %s\n", 239 inputFile, outputFile, argv[i]); 240 } 241 } 242 else if (!AddMatchingFiles(argv[i])) 243 return False; 244 } 245 else if ((strcmp(argv[i], "-?") == 0) 246 || (strcmp(argv[i], "-help") == 0)) 247 { 248 Usage(argc, argv); 249 exit(0); 250 } 251 else if (strcmp(argv[i], "-version") == 0) 252 { 253 printf("xkbcomp %s\n", PACKAGE_VERSION); 254 exit(0); 255 } else if ((strcmp(argv[i], "-a") == 0) && (!xkblist)) 256 { 257 showImplicit = True; 258 } 259 else if ((strcmp(argv[i], "-C") == 0) && (!xkblist)) 260 { 261 if ((outputFormat != WANT_DEFAULT) 262 && (outputFormat != WANT_C_HDR)) 263 { 264 if (warningLevel > 0) 265 { 266 WARN("Multiple output file formats specified\n"); 267 ACTION("\"%s\" flag ignored\n", argv[i]); 268 } 269 } 270 else 271 outputFormat = WANT_C_HDR; 272 } 273#ifdef DEBUG 274 else if (strcmp(argv[i], "-d") == 0) 275 { 276 if ((i >= (argc - 1)) || (!isdigit(argv[i + 1][0]))) 277 { 278 debugFlags = 1; 279 } 280 else 281 { 282 if (sscanf(argv[++i], "%i", &itmp) == 1) 283 debugFlags = itmp; 284 } 285 INFO("Setting debug flags to %d\n", debugFlags); 286 } 287#endif 288 else if ((strcmp(argv[i], "-dflts") == 0) && (!xkblist)) 289 { 290 computeDflts = True; 291 } 292 else if (strcmp(argv[i], "-em1") == 0) 293 { 294 if (++i >= argc) 295 { 296 if (warningLevel > 0) 297 { 298 WARN("No pre-error message specified\n"); 299 ACTION("Trailing \"-em1\" option ignored\n"); 300 } 301 } 302 else if (preErrorMsg != NULL) 303 { 304 if (warningLevel > 0) 305 { 306 WARN("Multiple pre-error messages specified\n"); 307 ACTION("Compiling %s, ignoring %s\n", 308 preErrorMsg, argv[i]); 309 } 310 } 311 else 312 preErrorMsg = argv[i]; 313 } 314 else if (strcmp(argv[i], "-emp") == 0) 315 { 316 if (++i >= argc) 317 { 318 if (warningLevel > 0) 319 { 320 WARN("No error prefix specified\n"); 321 ACTION("Trailing \"-emp\" option ignored\n"); 322 } 323 } 324 else if (errorPrefix != NULL) 325 { 326 if (warningLevel > 0) 327 { 328 WARN("Multiple error prefixes specified\n"); 329 ACTION("Compiling %s, ignoring %s\n", 330 errorPrefix, argv[i]); 331 } 332 } 333 else 334 errorPrefix = argv[i]; 335 } 336 else if (strcmp(argv[i], "-eml") == 0) 337 { 338 if (++i >= argc) 339 { 340 if (warningLevel > 0) 341 { 342 WARN("No post-error message specified\n"); 343 ACTION("Trailing \"-eml\" option ignored\n"); 344 } 345 } 346 else if (postErrorMsg != NULL) 347 { 348 if (warningLevel > 0) 349 { 350 WARN("Multiple post-error messages specified\n"); 351 ACTION("Compiling %s, ignoring %s\n", 352 postErrorMsg, argv[i]); 353 } 354 } 355 else 356 postErrorMsg = argv[i]; 357 } 358 else if ((strncmp(argv[i], "-I", 2) == 0) && (!xkblist)) 359 { 360 if (!XkbAddDirectoryToPath(&argv[i][2])) 361 { 362 ACTION("Exiting\n"); 363 exit(1); 364 } 365 } 366 else if ((strncmp(argv[i], "-i", 2) == 0) && (!xkblist)) 367 { 368 if (++i >= argc) 369 { 370 if (warningLevel > 0) 371 WARN("No device ID specified\n"); 372 } 373 device_id = atoi(argv[i]); 374 } 375 else if ((strncmp(argv[i], "-l", 2) == 0) && (!xkblist)) 376 { 377 if (outputFormat != WANT_DEFAULT) 378 { 379 if (warningLevel > 0) 380 { 381 WARN("Multiple output file formats specified\n"); 382 ACTION("\"%s\" flag ignored\n", argv[i]); 383 } 384 } 385 else 386 { 387 if (argv[i][2] != '\0') 388 setVerboseFlags(&argv[i][2]); 389 xkblist = True; 390 if ((inputFile) && (!AddMatchingFiles(inputFile))) 391 return False; 392 else 393 inputFile = NULL; 394 if ((outputFile) && (!AddMatchingFiles(outputFile))) 395 return False; 396 else 397 outputFile = NULL; 398 } 399 } 400 else if ((strcmp(argv[i], "-m") == 0) 401 || (strcmp(argv[i], "-map") == 0)) 402 { 403 if (++i >= argc) 404 { 405 if (warningLevel > 0) 406 { 407 WARN("No map name specified\n"); 408 ACTION("Trailing \"%s\" option ignored\n", argv[i - 1]); 409 } 410 } 411 else if (xkblist) 412 { 413 if (!AddMapOnly(argv[i])) 414 return False; 415 } 416 else if (inputMap != NULL) 417 { 418 if (warningLevel > 0) 419 { 420 WARN("Multiple map names specified\n"); 421 ACTION("Compiling %s, ignoring %s\n", inputMap, argv[i]); 422 } 423 } 424 else 425 inputMap = argv[i]; 426 } 427 else if ((strcmp(argv[i], "-merge") == 0) && (!xkblist)) 428 { 429 /* Ignored */ 430 } 431 else if (strcmp(argv[i], "-o") == 0) 432 { 433 if (++i >= argc) 434 { 435 if (warningLevel > 0) 436 { 437 WARN("No output file specified\n"); 438 ACTION("Trailing \"-o\" option ignored\n"); 439 } 440 } 441 else if (outputFile != NULL) 442 { 443 if (warningLevel > 0) 444 { 445 WARN("Multiple output files specified\n"); 446 ACTION("Compiling %s, ignoring %s\n", outputFile, 447 argv[i]); 448 } 449 } 450 else 451 outputFile = argv[i]; 452 } 453 else if (((strcmp(argv[i], "-opt") == 0) 454 || (strcmp(argv[i], "optional") == 0)) && (!xkblist)) 455 { 456 if (++i >= argc) 457 { 458 if (warningLevel > 0) 459 { 460 WARN("No optional components specified\n"); 461 ACTION("Trailing \"%s\" option ignored\n", argv[i - 1]); 462 } 463 } 464 else 465 { 466 for (const char *tmp2 = argv[i]; (*tmp2 != '\0'); tmp2++) 467 { 468 switch (*tmp2) 469 { 470 case 'c': 471 case 'C': 472 optionalParts |= XkmCompatMapMask; 473 break; 474 case 'g': 475 case 'G': 476 optionalParts |= XkmGeometryMask; 477 break; 478 case 'k': 479 case 'K': 480 optionalParts |= XkmKeyNamesMask; 481 break; 482 case 's': 483 case 'S': 484 optionalParts |= XkmSymbolsMask; 485 break; 486 case 't': 487 case 'T': 488 optionalParts |= XkmTypesMask; 489 break; 490 default: 491 if (warningLevel > 0) 492 { 493 WARN 494 ("Illegal component for %s option\n", 495 argv[i - 1]); 496 ACTION 497 ("Ignoring unknown specifier \"%c\"\n", 498 (unsigned int) *tmp2); 499 } 500 break; 501 } 502 } 503 } 504 } 505 else if (strncmp(argv[i], "-p", 2) == 0) 506 { 507 if (isdigit(argv[i][2])) 508 { 509 if (sscanf(&argv[i][2], "%i", &itmp) == 1) 510 dirsToStrip = itmp; 511 } 512 else if ((i < (argc - 1)) && (isdigit(argv[i + 1][0]))) 513 { 514 if (sscanf(argv[++i], "%i", &itmp) == 1) 515 dirsToStrip = itmp; 516 } 517 else 518 { 519 dirsToStrip = 0; 520 } 521 if (warningLevel > 5) 522 INFO("Setting path count to %d\n", dirsToStrip); 523 } 524 else if (strncmp(argv[i], "-R", 2) == 0) 525 { 526 if (argv[i][2] == '\0') 527 { 528 if (warningLevel > 0) 529 { 530 WARN("No root directory specified\n"); 531 ACTION("Ignoring -R option\n"); 532 } 533 } 534 else if (rootDir != NULL) 535 { 536 if (warningLevel > 0) 537 { 538 WARN("Multiple root directories specified\n"); 539 ACTION("Using %s, ignoring %s\n", rootDir, argv[i]); 540 } 541 } 542 else 543 { 544 rootDir = &argv[i][2]; 545 if (warningLevel > 8) 546 { 547 WARN("Changing root directory to \"%s\"\n", rootDir); 548 } 549 if (chdir(rootDir) == 0) 550 { 551 XkbAddDirectoryToPath("."); 552 } else if (warningLevel > 0) 553 { 554 WARN("Couldn't change directory to \"%s\"\n", rootDir); 555 ACTION("Root directory (-R) option ignored\n"); 556 rootDir = NULL; 557 } 558 } 559 } 560 else if ((strcmp(argv[i], "-synch") == 0) 561 || (strcmp(argv[i], "-s") == 0)) 562 { 563 synch = True; 564 } 565 else if (strncmp(argv[i], "-v", 2) == 0) 566 { 567 const char *str; 568 if (argv[i][2] != '\0') 569 str = &argv[i][2]; 570 else if ((i < (argc - 1)) && (argv[i + 1][0] != '-')) 571 str = argv[++i]; 572 else 573 str = NULL; 574 if (str) 575 setVerboseFlags(str); 576 } 577 else if (strncmp(argv[i], "-w", 2) == 0) 578 { 579 unsigned long utmp; 580 char *tmp2; 581 /* If text is just after "-w" in the same word, then it must 582 * be a number and it is the warning level. Otherwise, if the 583 * next argument is a number, then it is the warning level, 584 * else the warning level is assumed to be 0. 585 */ 586 if (argv[i][2] == '\0') 587 { 588 warningLevel = 0; 589 if (i < argc - 1) 590 { 591 utmp = strtoul(argv[i+1], &tmp2, 10); 592 if (argv[i+1][0] != '\0' && *tmp2 == '\0') 593 { 594 warningLevel = utmp > 10 ? 10 : utmp; 595 i++; 596 } 597 } 598 } 599 else 600 { 601 utmp = strtoul(&argv[i][2], &tmp2, 10); 602 if (*tmp2 == '\0') 603 warningLevel = utmp > 10 ? 10 : utmp; 604 else 605 { 606 ERROR("Unknown flag \"%s\" on command line\n", argv[i]); 607 Usage(argc, argv); 608 return False; 609 } 610 } 611 } 612 else if ((strcmp(argv[i], "-xkb") == 0) && (!xkblist)) 613 { 614 if ((outputFormat != WANT_DEFAULT) 615 && (outputFormat != WANT_XKB_FILE)) 616 { 617 if (warningLevel > 0) 618 { 619 WARN("Multiple output file formats specified\n"); 620 ACTION("\"%s\" flag ignored\n", argv[i]); 621 } 622 } 623 else 624 outputFormat = WANT_XKB_FILE; 625 } 626 else if ((strcmp(argv[i], "-xkm") == 0) && (!xkblist)) 627 { 628 if ((outputFormat != WANT_DEFAULT) 629 && (outputFormat != WANT_XKM_FILE)) 630 { 631 if (warningLevel > 0) 632 { 633 WARN("Multiple output file formats specified\n"); 634 ACTION("\"%s\" flag ignored\n", argv[i]); 635 } 636 } 637 else 638 outputFormat = WANT_XKM_FILE; 639 } 640 else 641 { 642 ERROR("Unknown flag \"%s\" on command line\n", argv[i]); 643 Usage(argc, argv); 644 return False; 645 } 646 } 647 if (xkblist) 648 inputFormat = INPUT_XKB; 649 else if (inputFile == NULL) 650 { 651 ERROR("No input file specified\n"); 652 return False; 653 } 654 else if (strcmp(inputFile, "-") == 0) 655 { 656 inputFormat = INPUT_XKB; 657 } 658#ifndef WIN32 659 else if (strchr(inputFile, ':') == NULL) 660 { 661#else 662 else if ((strchr(inputFile, ':') == NULL) || (strlen(inputFile) > 2 && 663 isalpha(inputFile[0]) && 664 inputFile[1] == ':' 665 && strchr(inputFile + 2, 666 ':') == NULL)) 667 { 668#endif 669 int len; 670 len = strlen(inputFile); 671 if (inputFile[len - 1] == ')') 672 { 673 char *tmpstr; 674 if ((tmpstr = strchr(inputFile, '(')) != NULL) 675 { 676 *tmpstr = '\0'; 677 inputFile[len - 1] = '\0'; 678 tmpstr++; 679 if (*tmpstr == '\0') 680 { 681 WARN("Empty map in filename\n"); 682 ACTION("Ignored\n"); 683 } 684 else if (inputMap == NULL) 685 { 686 inputMap = uStringDup(tmpstr); 687 } 688 else 689 { 690 WARN("Map specified in filename and with -m flag\n"); 691 ACTION("map from name (\"%s\") ignored\n", tmpstr); 692 } 693 } 694 else 695 { 696 ERROR("Illegal name \"%s\" for input file\n", inputFile); 697 return False; 698 } 699 } 700 if ((len > 4) && (strcmp(&inputFile[len - 4], ".xkm") == 0)) 701 { 702 inputFormat = INPUT_XKM; 703 } 704 else 705 { 706 FILE *file; 707 file = fopen(inputFile, "r"); 708 if (file) 709 { 710 if (XkmProbe(file)) 711 inputFormat = INPUT_XKM; 712 else 713 inputFormat = INPUT_XKB; 714 fclose(file); 715 } 716 else 717 { 718 fprintf(stderr, "Cannot open \"%s\" for reading\n", 719 inputFile); 720 return False; 721 } 722 } 723 } 724 else 725 { 726 inDpyName = inputFile; 727 inputFile = NULL; 728 inputFormat = INPUT_XKM; 729 } 730 731 if (outputFormat == WANT_DEFAULT) 732 { 733 if (xkblist) 734 outputFormat = WANT_LISTING; 735 else if (inputFormat == INPUT_XKB) 736 outputFormat = WANT_XKM_FILE; 737 else 738 outputFormat = WANT_XKB_FILE; 739 } 740 if ((outputFormat == WANT_LISTING) && (inputFormat != INPUT_XKB)) 741 { 742 if (inputFile) 743 ERROR("Cannot generate a listing from a .xkm file (yet)\n"); 744 else 745 ERROR("Cannot generate a listing from an X connection (yet)\n"); 746 return False; 747 } 748 if (xkblist) 749 { 750 if (outputFile == NULL) 751 outputFile = uStringDup("-"); 752 else if (strchr(outputFile, ':') != NULL) 753 { 754 ERROR("Cannot write a listing to an X connection\n"); 755 return False; 756 } 757 } 758 else if ((!outputFile) && (inputFile) && (strcmp(inputFile, "-") == 0)) 759 { 760#ifdef HAVE_ASPRINTF 761 if (asprintf(&outputFile, "stdin.%s", fileTypeExt[outputFormat]) < 0) 762#else 763 size_t len = strlen("stdin") + strlen(fileTypeExt[outputFormat]) + 2; 764 outputFile = calloc(len, sizeof(char)); 765 if (outputFile != NULL) 766 snprintf(outputFile, len, "stdin.%s", fileTypeExt[outputFormat]); 767 else 768#endif 769 { 770 WSGO("Cannot allocate space for output file name\n"); 771 ACTION("Exiting\n"); 772 exit(1); 773 } 774 } 775 else if ((outputFile == NULL) && (inputFile != NULL)) 776 { 777 int len; 778 const char *base, *ext; 779 780 if (inputMap == NULL) 781 { 782 base = strrchr(inputFile, '/'); 783 if (base == NULL) 784 base = inputFile; 785 else 786 base++; 787 } 788 else 789 base = inputMap; 790 791 len = strlen(base) + strlen(fileTypeExt[outputFormat]) + 2; 792 outputFile = calloc(len, sizeof(char)); 793 if (outputFile == NULL) 794 { 795 WSGO("Cannot allocate space for output file name\n"); 796 ACTION("Exiting\n"); 797 exit(1); 798 } 799 ext = strrchr(base, '.'); 800 if (ext == NULL) 801 snprintf(outputFile, len, "%s.%s", base, fileTypeExt[outputFormat]); 802 else 803 { 804 strcpy(outputFile, base); 805 strcpy(&outputFile[ext - base + 1], fileTypeExt[outputFormat]); 806 } 807 } 808 else if (outputFile == NULL) 809 { 810 int len; 811 char *ch, buf[128]; 812 const char *name = buf; 813 if (inDpyName[0] == ':') 814 snprintf(buf, sizeof(buf), "server%s", inDpyName); 815 else 816 name = inDpyName; 817 818 len = strlen(name) + strlen(fileTypeExt[outputFormat]) + 2; 819 outputFile = calloc(len, sizeof(char)); 820 if (outputFile == NULL) 821 { 822 WSGO("Cannot allocate space for output file name\n"); 823 ACTION("Exiting\n"); 824 exit(1); 825 } 826 strcpy(outputFile, name); 827 for (ch = outputFile; (*ch) != '\0'; ch++) 828 { 829 if (*ch == ':') 830 *ch = '-'; 831 else if (*ch == '.') 832 *ch = '_'; 833 } 834 *ch++ = '.'; 835 strcpy(ch, fileTypeExt[outputFormat]); 836 } 837#ifdef WIN32 838 else if (strlen(outputFile) > 2 && 839 isalpha(outputFile[0]) && 840 outputFile[1] == ':' && strchr(outputFile + 2, ':') == NULL) 841 { 842 } 843#endif 844 else if (strchr(outputFile, ':') != NULL) 845 { 846 outDpyName = outputFile; 847 outputFile = NULL; 848 outputFormat = WANT_X_SERVER; 849 } 850 return True; 851} 852 853static Display * 854GetDisplay(const char *program, const char *dpyName) 855{ 856 int mjr, mnr, error; 857 Display *dpy; 858 859 mjr = XkbMajorVersion; 860 mnr = XkbMinorVersion; 861 dpy = XkbOpenDisplay(dpyName, NULL, NULL, &mjr, &mnr, &error); 862 if (dpy == NULL) 863 { 864 switch (error) 865 { 866 case XkbOD_BadLibraryVersion: 867 INFO("%s was compiled with XKB version %d.%02d\n", 868 program, XkbMajorVersion, XkbMinorVersion); 869 ERROR("X library supports incompatible version %d.%02d\n", 870 mjr, mnr); 871 break; 872 case XkbOD_ConnectionRefused: 873 ERROR("Cannot open display \"%s\"\n", dpyName); 874 break; 875 case XkbOD_NonXkbServer: 876 ERROR("XKB extension not present on %s\n", dpyName); 877 break; 878 case XkbOD_BadServerVersion: 879 INFO("%s was compiled with XKB version %d.%02d\n", 880 program, XkbMajorVersion, XkbMinorVersion); 881 ERROR("Server %s uses incompatible version %d.%02d\n", 882 dpyName, mjr, mnr); 883 break; 884 default: 885 WSGO("Unknown error %d from XkbOpenDisplay\n", error); 886 } 887 } 888 else if (synch) 889 XSynchronize(dpy, True); 890 return dpy; 891} 892 893/***====================================================================***/ 894 895#ifdef DEBUG 896extern int yydebug; 897#endif 898 899int 900main(int argc, char *argv[]) 901{ 902 FILE *file; /* input file (or stdin) */ 903 XkbFile *rtrn; 904 XkbFile *mapToUse; 905 int ok; 906 XkbFileInfo result; 907 Status status; 908 909 scan_set_file(stdin); 910#ifdef DEBUG 911 uSetDebugFile(NullString); 912#endif 913 uSetErrorFile(NullString); 914 915 XkbInitIncludePath(); 916 if (!parseArgs(argc, argv)) 917 exit(1); 918#ifdef DEBUG 919 if (debugFlags & 0x2) 920 yydebug = 1; 921#endif 922 if (preErrorMsg) 923 uSetPreErrorMessage(preErrorMsg); 924 if (errorPrefix) 925 uSetErrorPrefix(errorPrefix); 926 if (postErrorMsg) 927 uSetPostErrorMessage(postErrorMsg); 928 file = NULL; 929 XkbInitAtoms(NULL); 930 XkbAddDefaultDirectoriesToPath(); 931 if (xkblist) 932 { 933 Bool gotSome; 934 gotSome = GenerateListing(outputFile); 935 if ((warningLevel > 7) && (!gotSome)) 936 return -1; 937 return 0; 938 } 939 if (inputFile != NULL) 940 { 941 if (strcmp(inputFile, "-") == 0) 942 { 943 file = stdin; 944 inputFile = "stdin"; 945 } 946 else 947 { 948 file = fopen(inputFile, "r"); 949 } 950 } 951 else if (inDpyName != NULL) 952 { 953 inDpy = GetDisplay(argv[0], inDpyName); 954 if (!inDpy) 955 { 956 ACTION("Exiting\n"); 957 exit(1); 958 } 959 } 960 if (outDpyName != NULL) 961 { 962 outDpy = GetDisplay(argv[0], outDpyName); 963 if (!outDpy) 964 { 965 ACTION("Exiting\n"); 966 exit(1); 967 } 968 } 969 if ((inDpy == NULL) && (outDpy == NULL)) 970 { 971 int mjr, mnr; 972 mjr = XkbMajorVersion; 973 mnr = XkbMinorVersion; 974 if (!XkbLibraryVersion(&mjr, &mnr)) 975 { 976 INFO("%s was compiled with XKB version %d.%02d\n", 977 argv[0], XkbMajorVersion, XkbMinorVersion); 978 ERROR("X library supports incompatible version %d.%02d\n", 979 mjr, mnr); 980 ACTION("Exiting\n"); 981 exit(1); 982 } 983 } 984 if (file) 985 { 986 ok = True; 987 setScanState(inputFile, 1); 988 if ((inputFormat == INPUT_XKB) /* parse .xkb file */ 989 && (XKBParseFile(file, &rtrn) && (rtrn != NULL))) 990 { 991 fclose(file); 992 mapToUse = rtrn; 993 if (inputMap != NULL) /* map specified on cmdline? */ 994 { 995 while ((mapToUse) 996 && (!uStringEqual(mapToUse->name, inputMap))) 997 { 998 mapToUse = (XkbFile *) mapToUse->common.next; 999 } 1000 if (!mapToUse) 1001 { 1002 FATAL("No map named \"%s\" in \"%s\"\n", 1003 inputMap, inputFile); 1004 /* NOTREACHED */ 1005 } 1006 } 1007 else if (rtrn->common.next != NULL) 1008 { 1009 /* look for map with XkbLC_Default flag. */ 1010 mapToUse = rtrn; 1011 for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next) 1012 { 1013 if (mapToUse->flags & XkbLC_Default) 1014 break; 1015 } 1016 if (!mapToUse) 1017 { 1018 mapToUse = rtrn; 1019 if (warningLevel > 4) 1020 { 1021 WARN 1022 ("No map specified, but \"%s\" has several\n", 1023 inputFile); 1024 ACTION 1025 ("Using the first defined map, \"%s\"\n", 1026 mapToUse->name); 1027 } 1028 } 1029 } 1030 bzero(&result, sizeof(result)); 1031 result.type = mapToUse->type; 1032 if ((result.xkb = XkbAllocKeyboard()) == NULL) 1033 { 1034 WSGO("Cannot allocate keyboard description\n"); 1035 /* NOTREACHED */ 1036 } 1037 switch (mapToUse->type) 1038 { 1039 case XkmSemanticsFile: 1040 case XkmLayoutFile: 1041 case XkmKeymapFile: 1042 ok = CompileKeymap(mapToUse, &result, MergeReplace); 1043 break; 1044 case XkmKeyNamesIndex: 1045 ok = CompileKeycodes(mapToUse, &result, MergeReplace); 1046 break; 1047 case XkmTypesIndex: 1048 ok = CompileKeyTypes(mapToUse, &result, MergeReplace); 1049 break; 1050 case XkmSymbolsIndex: 1051 /* if it's just symbols, invent key names */ 1052 result.xkb->flags |= AutoKeyNames; 1053 ok = False; 1054 break; 1055 case XkmCompatMapIndex: 1056 ok = CompileCompatMap(mapToUse, &result, MergeReplace, NULL); 1057 break; 1058 case XkmGeometryFile: 1059 case XkmGeometryIndex: 1060 /* if it's just a geometry, invent key names */ 1061 result.xkb->flags |= AutoKeyNames; 1062 ok = CompileGeometry(mapToUse, &result, MergeReplace); 1063 break; 1064 default: 1065 WSGO("Unknown file type %d\n", mapToUse->type); 1066 ok = False; 1067 break; 1068 } 1069 result.xkb->device_spec = device_id; 1070 } 1071 else if (inputFormat == INPUT_XKM) /* parse xkm file */ 1072 { 1073 unsigned tmp; 1074 bzero(&result, sizeof(result)); 1075 if ((result.xkb = XkbAllocKeyboard()) == NULL) 1076 { 1077 WSGO("Cannot allocate keyboard description\n"); 1078 /* NOTREACHED */ 1079 } 1080 tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result); 1081 if (tmp == XkmKeymapLegal) 1082 { 1083 ERROR("Cannot read XKM file \"%s\"\n", inputFile); 1084 ok = False; 1085 } 1086 result.xkb->device_spec = device_id; 1087 } 1088 else 1089 { 1090 INFO("Errors encountered in %s; not compiled.\n", inputFile); 1091 ok = False; 1092 } 1093 } 1094 else if (inDpy != NULL) 1095 { 1096 bzero(&result, sizeof(result)); 1097 result.type = XkmKeymapFile; 1098 result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id); 1099 if (result.xkb == NULL) 1100 WSGO("Cannot load keyboard description\n"); 1101 if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success) 1102 WSGO("Could not load indicator map\n"); 1103 if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success) 1104 WSGO("Could not load keyboard controls\n"); 1105 if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success) 1106 WSGO("Could not load compatibility map\n"); 1107 if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success) 1108 WSGO("Could not load names\n"); 1109 if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success) 1110 { 1111 if (warningLevel > 3) 1112 { 1113 char buf[100]; 1114 buf[0] = '\0'; 1115 XGetErrorText(inDpy, status, buf, 100); 1116 WARN("Could not load keyboard geometry for %s\n", inDpyName); 1117 ACTION("%s\n", buf); 1118 ACTION("Resulting keymap file will not describe geometry\n"); 1119 } 1120 } 1121 if (computeDflts) 1122 ok = (ComputeKbdDefaults(result.xkb) == Success); 1123 else 1124 ok = True; 1125 } 1126 else 1127 { 1128 fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile); 1129 ok = 0; 1130 } 1131 if (ok) 1132 { 1133 FILE *out = stdout; 1134 if ((inDpy != outDpy) && 1135 (XkbChangeKbdDisplay(outDpy, &result) != Success)) 1136 { 1137 WSGO("Error converting keyboard display from %s to %s\n", 1138 inDpyName, outDpyName); 1139 exit(1); 1140 } 1141 if (outputFile != NULL) 1142 { 1143 if (strcmp(outputFile, "-") == 0) 1144 outputFile = "stdout"; 1145 else 1146 { 1147 /* 1148 * fix to prevent symlink attack (e.g., 1149 * ln -s /etc/passwd /var/tmp/server-0.xkm) 1150 */ 1151 /* 1152 * this patch may have POSIX, Linux, or GNU libc bias 1153 * -- Branden Robinson 1154 */ 1155 int outputFileFd; 1156 int binMode = 0; 1157 const char *openMode = "w"; 1158 unlink(outputFile); 1159#ifdef O_BINARY 1160 switch (outputFormat) 1161 { 1162 case WANT_XKM_FILE: 1163 binMode = O_BINARY; 1164 openMode = "wb"; 1165 break; 1166 default: 1167 binMode = 0; 1168 break; 1169 } 1170#endif 1171 outputFileFd = 1172 open(outputFile, O_WRONLY | O_CREAT | O_EXCL, 1173 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH 1174 | S_IWOTH | binMode); 1175 if (outputFileFd < 0) 1176 { 1177 ERROR 1178 ("Cannot open \"%s\" to write keyboard description\n", 1179 outputFile); 1180 ACTION("Exiting\n"); 1181 exit(1); 1182 } 1183#ifndef WIN32 1184 out = fdopen(outputFileFd, openMode); 1185#else 1186 close(outputFileFd); 1187 out = fopen(outputFile, "wb"); 1188#endif 1189 /* end BR */ 1190 if (out == NULL) 1191 { 1192 ERROR 1193 ("Cannot open \"%s\" to write keyboard description\n", 1194 outputFile); 1195 ACTION("Exiting\n"); 1196 exit(1); 1197 } 1198 } 1199 } 1200 switch (outputFormat) 1201 { 1202 case WANT_XKM_FILE: 1203 ok = XkbWriteXKMFile(out, &result); 1204 break; 1205 case WANT_XKB_FILE: 1206 ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL); 1207 break; 1208 case WANT_C_HDR: 1209 ok = XkbWriteCFile(out, outputFile, &result); 1210 break; 1211 case WANT_X_SERVER: 1212 if (!(ok = XkbWriteToServer(&result))) 1213 { 1214 ERROR("%s in %s\n", _XkbErrMessages[_XkbErrCode], 1215 _XkbErrLocation ? _XkbErrLocation : "unknown"); 1216 ACTION("Couldn't write keyboard description to %s\n", 1217 outDpyName); 1218 } 1219 break; 1220 default: 1221 WSGO("Unknown output format %d\n", outputFormat); 1222 ACTION("No output file created\n"); 1223 ok = False; 1224 break; 1225 } 1226 if (outputFormat != WANT_X_SERVER) 1227 { 1228 if (fclose(out)) 1229 { 1230 ERROR("Cannot close \"%s\" properly (not enough space?)\n", 1231 outputFile); 1232 ok= False; 1233 } 1234 else if (!ok) 1235 { 1236 ERROR("%s in %s\n", _XkbErrMessages[_XkbErrCode], 1237 _XkbErrLocation ? _XkbErrLocation : "unknown"); 1238 } 1239 if (!ok) 1240 { 1241 ACTION("Output file \"%s\" removed\n", outputFile); 1242 unlink(outputFile); 1243 } 1244 } 1245 } 1246 if (inDpy) 1247 XCloseDisplay(inDpy); 1248 inDpy = NULL; 1249 if (outDpy) 1250 XCloseDisplay(outDpy); 1251 uFinishUp(); 1252 return (ok == 0); 1253} 1254