xkbcomp.c revision 07d2e718
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#if defined(sgi) 38#include <malloc.h> 39#endif 40 41#define DEBUG_VAR debugFlags 42#include "xkbcomp.h" 43#include <stdlib.h> 44#include "xkbpath.h" 45#include "parseutils.h" 46#include "misc.h" 47#include "tokens.h" 48#include <X11/extensions/XKBgeom.h> 49 50#ifdef __UNIXOS2__ 51#define chdir _chdir2 52#endif 53 54#ifdef WIN32 55#define S_IRGRP 0 56#define S_IWGRP 0 57#define S_IROTH 0 58#define S_IWOTH 0 59#endif 60 61#define lowbit(x) ((x) & (-(x))) 62 63/***====================================================================***/ 64 65#define WANT_DEFAULT 0 66#define WANT_XKM_FILE 1 67#define WANT_C_HDR 2 68#define WANT_XKB_FILE 3 69#define WANT_X_SERVER 4 70#define WANT_LISTING 5 71 72#define INPUT_UNKNOWN 0 73#define INPUT_XKB 1 74#define INPUT_XKM 2 75 76unsigned int debugFlags; 77 78static const char *fileTypeExt[] = { 79 "XXX", 80 "xkm", 81 "h", 82 "xkb", 83 "dir" 84}; 85 86static unsigned inputFormat, outputFormat; 87char *rootDir; 88static char *inputFile; 89static char *inputMap; 90static char *outputFile; 91static char *inDpyName; 92static char *outDpyName; 93static Display *inDpy; 94static Display *outDpy; 95static Bool showImplicit = False; 96static Bool synch = False; 97static Bool computeDflts = False; 98static Bool xkblist = False; 99unsigned warningLevel = 5; 100unsigned verboseLevel = 0; 101unsigned dirsToStrip = 0; 102unsigned optionalParts = 0; 103static char *preErrorMsg = NULL; 104static char *postErrorMsg = NULL; 105static char *errorPrefix = NULL; 106static unsigned int device_id = XkbUseCoreKbd; 107 108/***====================================================================***/ 109 110#define M(m) fprintf(stderr,(m)) 111#define M1(m,a) fprintf(stderr,(m),(a)) 112 113static void 114Usage(int argc, char *argv[]) 115{ 116 if (!xkblist) 117 M1("Usage: %s [options] input-file [ output-file ]\n", argv[0]); 118 else 119 M1("Usage: %s [options] file[(map)] ...\n", argv[0]); 120 M("Legal options:\n"); 121 M("-?,-help Print this message\n"); 122 if (!xkblist) 123 { 124 M("-a Show all actions\n"); 125 M("-C Create a C header file\n"); 126 } 127#ifdef DEBUG 128 M("-d [flags] Report debugging information\n"); 129#endif 130 M("-em1 <msg> Print <msg> before printing first error message\n"); 131 M("-emp <msg> Print <msg> at the start of each message line\n"); 132 M("-eml <msg> If there were any errors, print <msg> before exiting\n"); 133 if (!xkblist) 134 { 135 M("-dflts Compute defaults for missing parts\n"); 136 M("-I[<dir>] Specifies a top level directory for include\n"); 137 M(" directives. Multiple directories are legal.\n"); 138 M("-l [flags] List matching maps in the specified files\n"); 139 M(" f: list fully specified names\n"); 140 M(" h: also list hidden maps\n"); 141 M(" l: long listing (show flags)\n"); 142 M(" p: also list partial maps\n"); 143 M(" R: recursively list subdirectories\n"); 144 M(" default is all options off\n"); 145 } 146 M("-i <deviceid> Specifies device ID (not name) to compile for\n"); 147 M("-m[ap] <map> Specifies map to compile\n"); 148 M("-o <file> Specifies output file name\n"); 149 if (!xkblist) 150 { 151 M("-opt[ional] <parts> Specifies optional components of keymap\n"); 152 M(" Errors in optional parts are not fatal\n"); 153 M(" <parts> can be any combination of:\n"); 154 M(" c: compat map g: geometry\n"); 155 M(" k: keycodes s: symbols\n"); 156 M(" t: types\n"); 157 } 158 if (xkblist) 159 { 160 M("-p <count> Specifies the number of slashes to be stripped\n"); 161 M(" from the front of the map name on output\n"); 162 } 163 M("-R[<DIR>] Specifies the root directory for\n"); 164 M(" relative path names\n"); 165 M("-synch Force synchronization\n"); 166 if (xkblist) 167 { 168 M("-v [<flags>] Set level of detail for listing.\n"); 169 M(" flags are as for the -l option\n"); 170 } 171 M("-w [<lvl>] Set warning level (0=none, 10=all)\n"); 172 if (!xkblist) 173 { 174 M("-xkb Create an XKB source (.xkb) file\n"); 175 M("-xkm Create a compiled key map (.xkm) file\n"); 176 } 177 return; 178} 179 180/***====================================================================***/ 181 182static void 183setVerboseFlags(char *str) 184{ 185 for (; *str; str++) 186 { 187 switch (*str) 188 { 189 case 'f': 190 verboseLevel |= WantFullNames; 191 break; 192 case 'h': 193 verboseLevel |= WantHiddenMaps; 194 break; 195 case 'l': 196 verboseLevel |= WantLongListing; 197 break; 198 case 'p': 199 verboseLevel |= WantPartialMaps; 200 break; 201 case 'R': 202 verboseLevel |= ListRecursive; 203 break; 204 default: 205 if (warningLevel > 4) 206 { 207 WARN1("Unknown verbose option \"%c\"\n", (unsigned int) *str); 208 ACTION("Ignored\n"); 209 } 210 break; 211 } 212 } 213 return; 214} 215 216static Bool 217parseArgs(int argc, char *argv[]) 218{ 219 register int i, tmp; 220 221 i = strlen(argv[0]); 222 tmp = strlen("xkblist"); 223 if ((i >= tmp) && (strcmp(&argv[0][i - tmp], "xkblist") == 0)) 224 { 225 xkblist = True; 226 } 227 for (i = 1; i < argc; i++) 228 { 229 int itmp; 230 if ((argv[i][0] != '-') || (uStringEqual(argv[i], "-"))) 231 { 232 if (!xkblist) 233 { 234 if (inputFile == NULL) 235 inputFile = argv[i]; 236 else if (outputFile == NULL) 237 outputFile = argv[i]; 238 else if (warningLevel > 0) 239 { 240 WARN("Too many file names on command line\n"); 241 ACTION3 242 ("Compiling %s, writing to %s, ignoring %s\n", 243 inputFile, outputFile, argv[i]); 244 } 245 } 246 else if (!AddMatchingFiles(argv[i])) 247 return False; 248 } 249 else if ((strcmp(argv[i], "-?") == 0) 250 || (strcmp(argv[i], "-help") == 0)) 251 { 252 Usage(argc, argv); 253 exit(0); 254 } 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 ACTION1("\"%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 INFO1("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 messsages specified\n"); 307 ACTION2("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 ACTION2("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 ACTION2("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 ACTION1("\"%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 ACTION1("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 ACTION2("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 ACTION2("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 ACTION1("Trailing \"%s\" option ignored\n", argv[i - 1]); 462 } 463 } 464 else 465 { 466 char *tmp2; 467 for (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 WARN1 495 ("Illegal component for %s option\n", 496 argv[i - 1]); 497 ACTION1 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 INFO1("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 ACTION2("Using %s, ignoring %s\n", rootDir, argv[i]); 541 } 542 } 543 else 544 { 545 rootDir = &argv[i][2]; 546 if (warningLevel > 8) 547 { 548 WARN1("Changing root directory to \"%s\"\n", rootDir); 549 } 550 if ((chdir(rootDir) < 0) && (warningLevel > 0)) 551 { 552 WARN1("Couldn't change directory to \"%s\"\n", rootDir); 553 ACTION("Root directory (-R) option ignored\n"); 554 rootDir = NULL; 555 } 556 } 557 } 558 else if ((strcmp(argv[i], "-synch") == 0) 559 || (strcmp(argv[i], "-s") == 0)) 560 { 561 synch = True; 562 } 563 else if (strncmp(argv[i], "-v", 2) == 0) 564 { 565 char *str; 566 if (argv[i][2] != '\0') 567 str = &argv[i][2]; 568 else if ((i < (argc - 1)) && (argv[i + 1][0] != '-')) 569 str = argv[++i]; 570 else 571 str = NULL; 572 if (str) 573 setVerboseFlags(str); 574 } 575 else if (strncmp(argv[i], "-w", 2) == 0) 576 { 577 if ((i >= (argc - 1)) || (!isdigit(argv[i + 1][0]))) 578 { 579 warningLevel = 0; 580 if (isdigit(argv[i][1])) 581 if (sscanf(&argv[i][1], "%i", &itmp) == 1) 582 warningLevel = itmp; 583 } 584 else 585 { 586 if (sscanf(argv[++i], "%i", &itmp) == 1) 587 warningLevel = itmp; 588 } 589 } 590 else if ((strcmp(argv[i], "-xkb") == 0) && (!xkblist)) 591 { 592 if ((outputFormat != WANT_DEFAULT) 593 && (outputFormat != WANT_XKB_FILE)) 594 { 595 if (warningLevel > 0) 596 { 597 WARN("Multiple output file formats specified\n"); 598 ACTION1("\"%s\" flag ignored\n", argv[i]); 599 } 600 } 601 else 602 outputFormat = WANT_XKB_FILE; 603 } 604 else if ((strcmp(argv[i], "-xkm") == 0) && (!xkblist)) 605 { 606 if ((outputFormat != WANT_DEFAULT) 607 && (outputFormat != WANT_XKM_FILE)) 608 { 609 if (warningLevel > 0) 610 { 611 WARN("Multiple output file formats specified\n"); 612 ACTION1("\"%s\" flag ignored\n", argv[i]); 613 } 614 } 615 else 616 outputFormat = WANT_XKM_FILE; 617 } 618 else 619 { 620 ERROR1("Unknown flag \"%s\" on command line\n", argv[i]); 621 Usage(argc, argv); 622 return False; 623 } 624 } 625 if (xkblist) 626 inputFormat = INPUT_XKB; 627 else if (inputFile == NULL) 628 { 629 ERROR("No input file specified\n"); 630 return False; 631 } 632 else if (uStringEqual(inputFile, "-")) 633 { 634 inputFormat = INPUT_XKB; 635 } 636#ifndef WIN32 637 else if (strchr(inputFile, ':') == NULL) 638 { 639#else 640 else if ((strchr(inputFile, ':') == NULL) || (strlen(inputFile) > 2 && 641 isalpha(inputFile[0]) && 642 inputFile[1] == ':' 643 && strchr(inputFile + 2, 644 ':') == NULL)) 645 { 646#endif 647 int len; 648 len = strlen(inputFile); 649 if (inputFile[len - 1] == ')') 650 { 651 char *tmp; 652 if ((tmp = strchr(inputFile, '(')) != NULL) 653 { 654 *tmp = '\0'; 655 inputFile[len - 1] = '\0'; 656 tmp++; 657 if (*tmp == '\0') 658 { 659 WARN("Empty map in filename\n"); 660 ACTION("Ignored\n"); 661 } 662 else if (inputMap == NULL) 663 { 664 inputMap = uStringDup(tmp); 665 } 666 else 667 { 668 WARN("Map specified in filename and with -m flag\n"); 669 ACTION1("map from name (\"%s\") ignored\n", tmp); 670 } 671 } 672 else 673 { 674 ERROR1("Illegal name \"%s\" for input file\n", inputFile); 675 return False; 676 } 677 } 678 if ((len > 4) && (strcmp(&inputFile[len - 4], ".xkm") == 0)) 679 { 680 inputFormat = INPUT_XKM; 681 } 682 else 683 { 684 FILE *file; 685 file = fopen(inputFile, "r"); 686 if (file) 687 { 688 if (XkmProbe(file)) 689 inputFormat = INPUT_XKM; 690 else 691 inputFormat = INPUT_XKB; 692 fclose(file); 693 } 694 else 695 { 696 fprintf(stderr, "Cannot open \"%s\" for reading\n", 697 inputFile); 698 return False; 699 } 700 } 701 } 702 else 703 { 704 inDpyName = inputFile; 705 inputFile = NULL; 706 inputFormat = INPUT_XKM; 707 } 708 709 if (outputFormat == WANT_DEFAULT) 710 { 711 if (xkblist) 712 outputFormat = WANT_LISTING; 713 else if (inputFormat == INPUT_XKB) 714 outputFormat = WANT_XKM_FILE; 715 else 716 outputFormat = WANT_XKB_FILE; 717 } 718 if ((outputFormat == WANT_LISTING) && (inputFormat != INPUT_XKB)) 719 { 720 if (inputFile) 721 ERROR("Cannot generate a listing from a .xkm file (yet)\n"); 722 else 723 ERROR("Cannot generate a listing from an X connection (yet)\n"); 724 return False; 725 } 726 if (xkblist) 727 { 728 if (outputFile == NULL) 729 outputFile = uStringDup("-"); 730 else if (strchr(outputFile, ':') != NULL) 731 { 732 ERROR("Cannot write a listing to an X connection\n"); 733 return False; 734 } 735 } 736 else if ((!outputFile) && (inputFile) && uStringEqual(inputFile, "-")) 737 { 738 int len = strlen("stdin") + strlen(fileTypeExt[outputFormat]) + 2; 739 outputFile = uTypedCalloc(len, char); 740 if (outputFile == NULL) 741 { 742 WSGO("Cannot allocate space for output file name\n"); 743 ACTION("Exiting\n"); 744 exit(1); 745 } 746 sprintf(outputFile, "stdin.%s", fileTypeExt[outputFormat]); 747 } 748 else if ((outputFile == NULL) && (inputFile != NULL)) 749 { 750 int len; 751 char *base, *ext; 752 753 if (inputMap == NULL) 754 { 755 base = strrchr(inputFile, '/'); 756 if (base == NULL) 757 base = inputFile; 758 else 759 base++; 760 } 761 else 762 base = inputMap; 763 764 len = strlen(base) + strlen(fileTypeExt[outputFormat]) + 2; 765 outputFile = uTypedCalloc(len, char); 766 if (outputFile == NULL) 767 { 768 WSGO("Cannot allocate space for output file name\n"); 769 ACTION("Exiting\n"); 770 exit(1); 771 } 772 ext = strrchr(base, '.'); 773 if (ext == NULL) 774 sprintf(outputFile, "%s.%s", base, fileTypeExt[outputFormat]); 775 else 776 { 777 strcpy(outputFile, base); 778 strcpy(&outputFile[ext - base + 1], fileTypeExt[outputFormat]); 779 } 780 } 781 else if (outputFile == NULL) 782 { 783 int len; 784 char *ch, *name, buf[128]; 785 if (inDpyName[0] == ':') 786 snprintf(name = buf, sizeof(buf), "server%s", inDpyName); 787 else 788 name = inDpyName; 789 790 len = strlen(name) + strlen(fileTypeExt[outputFormat]) + 2; 791 outputFile = uTypedCalloc(len, char); 792 if (outputFile == NULL) 793 { 794 WSGO("Cannot allocate space for output file name\n"); 795 ACTION("Exiting\n"); 796 exit(1); 797 } 798 strcpy(outputFile, name); 799 for (ch = outputFile; (*ch) != '\0'; ch++) 800 { 801 if (*ch == ':') 802 *ch = '-'; 803 else if (*ch == '.') 804 *ch = '_'; 805 } 806 *ch++ = '.'; 807 strcpy(ch, fileTypeExt[outputFormat]); 808 } 809#ifdef WIN32 810 else if (strlen(outputFile) > 2 && 811 isalpha(outputFile[0]) && 812 outputFile[1] == ':' && strchr(outputFile + 2, ':') == NULL) 813 { 814 } 815#endif 816 else if (strchr(outputFile, ':') != NULL) 817 { 818 outDpyName = outputFile; 819 outputFile = NULL; 820 outputFormat = WANT_X_SERVER; 821 } 822 return True; 823} 824 825static Display * 826GetDisplay(char *program, char *dpyName) 827{ 828 int mjr, mnr, error; 829 Display *dpy; 830 831 mjr = XkbMajorVersion; 832 mnr = XkbMinorVersion; 833 dpy = XkbOpenDisplay(dpyName, NULL, NULL, &mjr, &mnr, &error); 834 if (dpy == NULL) 835 { 836 switch (error) 837 { 838 case XkbOD_BadLibraryVersion: 839 INFO3("%s was compiled with XKB version %d.%02d\n", 840 program, XkbMajorVersion, XkbMinorVersion); 841 ERROR2("X library supports incompatible version %d.%02d\n", 842 mjr, mnr); 843 break; 844 case XkbOD_ConnectionRefused: 845 ERROR1("Cannot open display \"%s\"\n", dpyName); 846 break; 847 case XkbOD_NonXkbServer: 848 ERROR1("XKB extension not present on %s\n", dpyName); 849 break; 850 case XkbOD_BadServerVersion: 851 INFO3("%s was compiled with XKB version %d.%02d\n", 852 program, XkbMajorVersion, XkbMinorVersion); 853 ERROR3("Server %s uses incompatible version %d.%02d\n", 854 dpyName, mjr, mnr); 855 break; 856 default: 857 WSGO1("Unknown error %d from XkbOpenDisplay\n", error); 858 } 859 } 860 else if (synch) 861 XSynchronize(dpy, True); 862 return dpy; 863} 864 865/***====================================================================***/ 866 867#ifdef DEBUG 868extern int yydebug; 869#endif 870 871int 872main(int argc, char *argv[]) 873{ 874 FILE *file; /* input file (or stdin) */ 875 XkbFile *rtrn; 876 XkbFile *mapToUse; 877 int ok; 878 XkbFileInfo result; 879 Status status; 880 881 yyin = stdin; 882 uSetEntryFile(NullString); 883 uSetDebugFile(NullString); 884 uSetErrorFile(NullString); 885 886 XkbInitIncludePath(); 887 if (!parseArgs(argc, argv)) 888 exit(1); 889#ifdef DEBUG 890 if (debugFlags & 0x2) 891 yydebug = 1; 892#endif 893 if (preErrorMsg) 894 uSetPreErrorMessage(preErrorMsg); 895 if (errorPrefix) 896 uSetErrorPrefix(errorPrefix); 897 if (postErrorMsg) 898 uSetPostErrorMessage(postErrorMsg); 899 file = NULL; 900 XkbInitAtoms(NULL); 901 XkbAddDefaultDirectoriesToPath(); 902 if (xkblist) 903 { 904 Bool gotSome; 905 gotSome = GenerateListing(outputFile); 906 if ((warningLevel > 7) && (!gotSome)) 907 return -1; 908 return 0; 909 } 910 if (inputFile != NULL) 911 { 912 if (uStringEqual(inputFile, "-")) 913 { 914 file = stdin; 915 inputFile = "stdin"; 916 } 917 else 918 { 919 file = fopen(inputFile, "r"); 920 } 921 } 922 else if (inDpyName != NULL) 923 { 924 inDpy = GetDisplay(argv[0], inDpyName); 925 if (!inDpy) 926 { 927 ACTION("Exiting\n"); 928 exit(1); 929 } 930 } 931 if (outDpyName != NULL) 932 { 933 outDpy = GetDisplay(argv[0], outDpyName); 934 if (!outDpy) 935 { 936 ACTION("Exiting\n"); 937 exit(1); 938 } 939 } 940 if ((inDpy == NULL) && (outDpy == NULL)) 941 { 942 int mjr, mnr; 943 mjr = XkbMajorVersion; 944 mnr = XkbMinorVersion; 945 if (!XkbLibraryVersion(&mjr, &mnr)) 946 { 947 INFO3("%s was compiled with XKB version %d.%02d\n", 948 argv[0], XkbMajorVersion, XkbMinorVersion); 949 ERROR2("X library supports incompatible version %d.%02d\n", 950 mjr, mnr); 951 ACTION("Exiting\n"); 952 exit(1); 953 } 954 } 955 if (file) 956 { 957 ok = True; 958 setScanState(inputFile, 1); 959 if ((inputFormat == INPUT_XKB) /* parse .xkb file */ 960 && (XKBParseFile(file, &rtrn) && (rtrn != NULL))) 961 { 962 fclose(file); 963 mapToUse = rtrn; 964 if (inputMap != NULL) /* map specified on cmdline? */ 965 { 966 while ((mapToUse) 967 && (!uStringEqual(mapToUse->name, inputMap))) 968 { 969 mapToUse = (XkbFile *) mapToUse->common.next; 970 } 971 if (!mapToUse) 972 { 973 FATAL2("No map named \"%s\" in \"%s\"\n", 974 inputMap, inputFile); 975 /* NOTREACHED */ 976 } 977 } 978 else if (rtrn->common.next != NULL) 979 { 980 /* look for map with XkbLC_Default flag. */ 981 mapToUse = rtrn; 982 for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next) 983 { 984 if (mapToUse->flags & XkbLC_Default) 985 break; 986 } 987 if (!mapToUse) 988 { 989 mapToUse = rtrn; 990 if (warningLevel > 4) 991 { 992 WARN1 993 ("No map specified, but \"%s\" has several\n", 994 inputFile); 995 ACTION1 996 ("Using the first defined map, \"%s\"\n", 997 mapToUse->name); 998 } 999 } 1000 } 1001 bzero((char *) &result, sizeof(result)); 1002 result.type = mapToUse->type; 1003 if ((result.xkb = XkbAllocKeyboard()) == NULL) 1004 { 1005 WSGO("Cannot allocate keyboard description\n"); 1006 /* NOTREACHED */ 1007 } 1008 switch (mapToUse->type) 1009 { 1010 case XkmSemanticsFile: 1011 case XkmLayoutFile: 1012 case XkmKeymapFile: 1013 ok = CompileKeymap(mapToUse, &result, MergeReplace); 1014 break; 1015 case XkmKeyNamesIndex: 1016 ok = CompileKeycodes(mapToUse, &result, MergeReplace); 1017 break; 1018 case XkmTypesIndex: 1019 ok = CompileKeyTypes(mapToUse, &result, MergeReplace); 1020 break; 1021 case XkmSymbolsIndex: 1022 /* if it's just symbols, invent key names */ 1023 result.xkb->flags |= AutoKeyNames; 1024 ok = False; 1025 break; 1026 case XkmCompatMapIndex: 1027 ok = CompileCompatMap(mapToUse, &result, MergeReplace, NULL); 1028 break; 1029 case XkmGeometryFile: 1030 case XkmGeometryIndex: 1031 /* if it's just a geometry, invent key names */ 1032 result.xkb->flags |= AutoKeyNames; 1033 ok = CompileGeometry(mapToUse, &result, MergeReplace); 1034 break; 1035 default: 1036 WSGO1("Unknown file type %d\n", mapToUse->type); 1037 ok = False; 1038 break; 1039 } 1040 result.xkb->device_spec = device_id; 1041 } 1042 else if (inputFormat == INPUT_XKM) /* parse xkm file */ 1043 { 1044 unsigned tmp; 1045 bzero((char *) &result, sizeof(result)); 1046 if ((result.xkb = XkbAllocKeyboard()) == NULL) 1047 { 1048 WSGO("Cannot allocate keyboard description\n"); 1049 /* NOTREACHED */ 1050 } 1051 tmp = XkmReadFile(file, 0, XkmKeymapLegal, &result); 1052 if (tmp == XkmKeymapLegal) 1053 { 1054 ERROR1("Cannot read XKM file \"%s\"\n", inputFile); 1055 ok = False; 1056 } 1057 result.xkb->device_spec = device_id; 1058 } 1059 else 1060 { 1061 INFO1("Errors encountered in %s; not compiled.\n", inputFile); 1062 ok = False; 1063 } 1064 } 1065 else if (inDpy != NULL) 1066 { 1067 bzero((char *) &result, sizeof(result)); 1068 result.type = XkmKeymapFile; 1069 result.xkb = XkbGetMap(inDpy, XkbAllMapComponentsMask, device_id); 1070 if (result.xkb == NULL) 1071 WSGO("Cannot load keyboard description\n"); 1072 if (XkbGetIndicatorMap(inDpy, ~0, result.xkb) != Success) 1073 WSGO("Could not load indicator map\n"); 1074 if (XkbGetControls(inDpy, XkbAllControlsMask, result.xkb) != Success) 1075 WSGO("Could not load keyboard controls\n"); 1076 if (XkbGetCompatMap(inDpy, XkbAllCompatMask, result.xkb) != Success) 1077 WSGO("Could not load compatibility map\n"); 1078 if (XkbGetNames(inDpy, XkbAllNamesMask, result.xkb) != Success) 1079 WSGO("Could not load names\n"); 1080 if ((status = XkbGetGeometry(inDpy, result.xkb)) != Success) 1081 { 1082 if (warningLevel > 3) 1083 { 1084 char buf[100]; 1085 buf[0] = '\0'; 1086 XGetErrorText(inDpy, status, buf, 100); 1087 WARN1("Could not load keyboard geometry for %s\n", inDpyName); 1088 ACTION1("%s\n", buf); 1089 ACTION("Resulting keymap file will not describe geometry\n"); 1090 } 1091 } 1092 if (computeDflts) 1093 ok = (ComputeKbdDefaults(result.xkb) == Success); 1094 else 1095 ok = True; 1096 } 1097 else 1098 { 1099 fprintf(stderr, "Cannot open \"%s\" to compile\n", inputFile); 1100 ok = 0; 1101 } 1102 if (ok) 1103 { 1104 FILE *out = stdout; 1105 if ((inDpy != outDpy) && 1106 (XkbChangeKbdDisplay(outDpy, &result) != Success)) 1107 { 1108 WSGO2("Error converting keyboard display from %s to %s\n", 1109 inDpyName, outDpyName); 1110 exit(1); 1111 } 1112 if (outputFile != NULL) 1113 { 1114 if (uStringEqual(outputFile, "-")) 1115 outputFile = "stdout"; 1116 else 1117 { 1118 /* 1119 * fix to prevent symlink attack (e.g., 1120 * ln -s /etc/passwd /var/tmp/server-0.xkm) 1121 */ 1122 /* 1123 * this patch may have POSIX, Linux, or GNU libc bias 1124 * -- Branden Robinson 1125 */ 1126 int outputFileFd; 1127 int binMode = 0; 1128 const char *openMode = "w"; 1129 unlink(outputFile); 1130#ifdef O_BINARY 1131 switch (outputFormat) 1132 { 1133 case WANT_XKM_FILE: 1134 binMode = O_BINARY; 1135 openMode = "wb"; 1136 break; 1137 default: 1138 binMode = 0; 1139 break; 1140 } 1141#endif 1142 outputFileFd = 1143 open(outputFile, O_WRONLY | O_CREAT | O_EXCL, 1144 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH 1145 | S_IWOTH | binMode); 1146 if (outputFileFd < 0) 1147 { 1148 ERROR1 1149 ("Cannot open \"%s\" to write keyboard description\n", 1150 outputFile); 1151 ACTION("Exiting\n"); 1152 exit(1); 1153 } 1154#ifndef WIN32 1155 out = fdopen(outputFileFd, openMode); 1156#else 1157 close(outputFileFd); 1158 out = fopen(outputFile, "wb"); 1159#endif 1160 /* end BR */ 1161 if (out == NULL) 1162 { 1163 ERROR1 1164 ("Cannot open \"%s\" to write keyboard description\n", 1165 outputFile); 1166 ACTION("Exiting\n"); 1167 exit(1); 1168 } 1169 } 1170 } 1171 switch (outputFormat) 1172 { 1173 case WANT_XKM_FILE: 1174 ok = XkbWriteXKMFile(out, &result); 1175 break; 1176 case WANT_XKB_FILE: 1177 ok = XkbWriteXKBFile(out, &result, showImplicit, NULL, NULL); 1178 break; 1179 case WANT_C_HDR: 1180 ok = XkbWriteCFile(out, outputFile, &result); 1181 break; 1182 case WANT_X_SERVER: 1183 if (!(ok = XkbWriteToServer(&result))) 1184 { 1185 ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode], 1186 _XkbErrLocation ? _XkbErrLocation : "unknown"); 1187 ACTION1("Couldn't write keyboard description to %s\n", 1188 outDpyName); 1189 } 1190 break; 1191 default: 1192 WSGO1("Unknown output format %d\n", outputFormat); 1193 ACTION("No output file created\n"); 1194 ok = False; 1195 break; 1196 } 1197 if (outputFormat != WANT_X_SERVER) 1198 { 1199 if (fclose(out)) 1200 { 1201 ERROR1("Cannot close \"%s\" properly (not enough space?)\n", 1202 outputFile); 1203 ok= False; 1204 } 1205 else if (!ok) 1206 { 1207 ERROR2("%s in %s\n", _XkbErrMessages[_XkbErrCode], 1208 _XkbErrLocation ? _XkbErrLocation : "unknown"); 1209 } 1210 if (!ok) 1211 { 1212 ACTION1("Output file \"%s\" removed\n", outputFile); 1213 unlink(outputFile); 1214 } 1215 } 1216 } 1217 if (inDpy) 1218 XCloseDisplay(inDpy); 1219 inDpy = NULL; 1220 if (outDpy) 1221 XCloseDisplay(outDpy); 1222 uFinishUp(); 1223 return (ok == 0); 1224} 1225