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