xrdb.c revision 5d375e47
1/* 2 * xrdb - X resource manager database utility 3 * 4 */ 5 6/* 7 * COPYRIGHT 1987, 1991 8 * DIGITAL EQUIPMENT CORPORATION 9 * MAYNARD, MASSACHUSETTS 10 * MASSACHUSETTS INSTITUTE OF TECHNOLOGY 11 * CAMBRIDGE, MASSACHUSETTS 12 * ALL RIGHTS RESERVED. 13 * 14 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND 15 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. 16 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR 17 * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. 18 * 19 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS, 20 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT 21 * SET FORTH ABOVE. 22 * 23 * 24 * Permission to use, copy, modify, and distribute this software and its 25 * documentation for any purpose and without fee is hereby granted, provided 26 * that the above copyright notice appear in all copies and that both that 27 * copyright notice and this permission notice appear in supporting 28 * documentation, and that the name of Digital Equipment Corporation not be 29 * used in advertising or publicity pertaining to distribution of the software 30 * without specific, written prior permission. 31 */ 32 33/* 34 * this program is used to load, or dump the resource manager database 35 * in the server. 36 * 37 * Original Author: Jim Gettys, August 28, 1987 38 * Extensively Modified: Phil Karlton, January 5, 1987 39 * Modified a Bunch More: Bob Scheifler, February, 1991 40 */ 41 42#ifdef HAVE_CONFIG_H 43#include <config.h> 44#endif 45 46#include <X11/Xlib.h> 47#include <X11/Xutil.h> 48#include <X11/Xatom.h> 49#include <X11/Xos.h> 50#include <X11/Xmu/SysUtil.h> 51#include <stdio.h> 52#include <ctype.h> 53#include <errno.h> 54#include <stdlib.h> 55#include <stdarg.h> 56 57#ifdef NEED_SYS_PARAM_H 58# include <sys/param.h> /* defines MAXHOSTNAMELEN on BSD & Linux */ 59#endif 60 61#ifdef NEED_NETDB_H 62# include <netdb.h> /* defines MAXHOSTNAMELEN on Solaris */ 63#endif 64 65#define SCREEN_RESOURCES "SCREEN_RESOURCES" 66 67#ifndef CPP 68#ifdef __UNIXOS2__ 69/* expected to be in path */ 70#define CPP "cpp" 71#else 72#define CPP "/usr/lib/cpp" 73#endif /* __UNIXOS2__ */ 74#endif /* CPP */ 75 76#define INIT_BUFFER_SIZE 10000 77#define INIT_ENTRY_SIZE 500 78 79#define RALL 0 80#define RGLOBAL 1 81#define RSCREEN 2 82#define RSCREENS 3 83 84#define OPSYMBOLS 0 85#define OPQUERY 1 86#define OPREMOVE 2 87#define OPEDIT 3 88#define OPLOAD 4 89#define OPMERGE 5 90#define OPOVERRIDE 6 91 92#define RESOURCE_PROPERTY_NAME "RESOURCE_MANAGER" 93#define BACKUP_SUFFIX ".bak" /* for editting */ 94 95typedef struct _Entry { 96 char *tag, *value; 97 int lineno; 98 Bool usable; 99} Entry; 100typedef struct _Buffer { 101 char *buff; 102 int room, used; 103} Buffer; 104typedef struct _Entries { 105 Entry *entry; 106 int room, used; 107} Entries; 108 109/* dynamically allocated strings */ 110#define CHUNK_SIZE 4096 111typedef struct _String { 112 char *val; 113 int room, used; 114} String; 115 116static char *ProgramName; 117static Bool quiet = False; 118static char tmpname[32]; 119static char *filename = NULL; 120#ifdef PATHETICCPP 121static Bool need_real_defines = False; 122static char tmpname2[32]; 123#ifdef WIN32 124static char tmpname3[32]; 125#endif 126#endif 127static int oper = OPLOAD; 128static char *editFile = NULL; 129static const char *cpp_program = NULL; 130#ifndef CPP_ARGS 131#define CPP_ARGS NULL 132#endif 133static const char *cpp_args = CPP_ARGS; 134static const char* const cpp_locations[] = { CPP }; 135static char *backup_suffix = BACKUP_SUFFIX; 136static Bool dont_execute = False; 137static String defines; 138static int defines_base; 139#define MAX_CMD_DEFINES 512 140static char *cmd_defines[MAX_CMD_DEFINES]; 141static int num_cmd_defines = 0; 142static String includes; 143static Display *dpy; 144static Buffer buffer; 145static Entries newDB; 146 147static void fatal(char *, ...); 148static void addstring ( String *arg, const char *s ); 149static void FormatEntries ( Buffer *buffer, Entries *entries ); 150static void StoreProperty ( Display *dpy, Window root, Atom res_prop ); 151static void Process ( int scrno, Bool doScreen, Bool execute ); 152static void ShuffleEntries ( Entries *db, Entries *dbs, int num ); 153static void ReProcess ( int scrno, Bool doScreen ); 154 155#ifndef HAVE_ASPRINTF 156/* sprintf variant found in newer libc's which allocates string to print to */ 157static int _X_ATTRIBUTE_PRINTF(2,3) 158asprintf(char ** ret, const char *format, ...) 159{ 160 char buf[256]; 161 int len; 162 va_list ap; 163 164 va_start(ap, format); 165 len = vsnprintf(buf, sizeof(buf), format, ap); 166 va_end(ap); 167 168 if (len < 0) 169 return -1; 170 171 if (len < sizeof(buf)) 172 { 173 *ret = strdup(buf); 174 } 175 else 176 { 177 *ret = malloc(len + 1); /* snprintf doesn't count trailing '\0' */ 178 if (*ret != NULL) 179 { 180 va_start(ap, format); 181 len = vsnprintf(*ret, len + 1, format, ap); 182 va_end(ap); 183 if (len < 0) { 184 free(*ret); 185 *ret = NULL; 186 } 187 } 188 } 189 190 if (*ret == NULL) 191 return -1; 192 193 return len; 194} 195#endif /* HAVE_ASPRINTF */ 196 197static void 198InitBuffer(Buffer *b) 199{ 200 b->room = INIT_BUFFER_SIZE; 201 b->used = 0; 202 b->buff = (char *)malloc(INIT_BUFFER_SIZE*sizeof(char)); 203} 204 205#ifdef notyet 206static void 207FreeBuffer(Buffer *b) 208{ 209 free(b->buff); 210} 211#endif 212 213static void 214AppendToBuffer(Buffer *b, char *str, int len) 215{ 216 while (b->used + len > b->room) { 217 b->buff = (char *)realloc(b->buff, 2*b->room*(sizeof(char))); 218 b->room *= 2; 219 } 220 strncpy(b->buff + b->used, str, len); 221 b->used += len; 222} 223 224static void 225InitEntries(Entries *e) 226{ 227 e->room = INIT_ENTRY_SIZE; 228 e->used = 0; 229 e->entry = (Entry *)malloc(INIT_ENTRY_SIZE*sizeof(Entry)); 230} 231 232static void 233FreeEntries(Entries *e) 234{ 235 register int i; 236 237 for (i = 0; i < e->used; i++) { 238 if (e->entry[i].usable) { 239 free(e->entry[i].tag); 240 free(e->entry[i].value); 241 } 242 } 243 free((char *)e->entry); 244} 245 246static void 247AddEntry(Entries *e, Entry *entry) 248{ 249 register int n; 250 251 for (n = 0; n < e->used; n++) { 252 if (!strcmp(e->entry[n].tag, entry->tag)) { 253 /* overwrite old entry */ 254 if (e->entry[n].lineno && !quiet) { 255 fprintf (stderr, 256 "%s: \"%s\" on line %d overrides entry on line %d\n", 257 ProgramName, entry->tag, entry->lineno, 258 e->entry[n].lineno); 259 } 260 free(e->entry[n].tag); 261 free(e->entry[n].value); 262 entry->usable = True; 263 e->entry[n] = *entry; 264 return; /* ok to leave, now there's only one of each tag in db */ 265 } 266 } 267 268 if (e->used == e->room) { 269 e->entry = (Entry *)realloc((char *)e->entry, 270 2*e->room*(sizeof(Entry))); 271 e->room *= 2; 272 } 273 entry->usable = True; 274 e->entry[e->used++] = *entry; 275} 276 277 278static int 279CompareEntries(const void *e1, const void *e2) 280{ 281 return strcmp(((Entry *)e1)->tag, ((Entry *)e2)->tag); 282} 283 284static void 285AppendEntryToBuffer(Buffer *buffer, Entry *entry) 286{ 287 AppendToBuffer(buffer, entry->tag, strlen(entry->tag)); 288 AppendToBuffer(buffer, ":\t", 2); 289 AppendToBuffer(buffer, entry->value, strlen(entry->value)); 290 AppendToBuffer(buffer, "\n", 1); 291} 292 293/* 294 * Return the position of the first unescaped occurrence of dest in string. 295 * If lines is non-null, return the number of newlines skipped over. 296 */ 297static char * 298FindFirst(char *string, char dest, int *lines) 299{ 300 if (lines) 301 *lines = 0; 302 for (;;) { 303 if (*string == '\0') 304 return NULL; 305 if (*string == '\\') { 306 if (*++string == '\0') 307 return NULL; 308 } else if (*string == dest) 309 return string; 310 if (*string == '\n' && lines) 311 (*lines)++; 312 string++; 313 } 314} 315 316static void 317GetEntries(Entries *entries, Buffer *buff, int bequiet) 318{ 319 register char *line, *colon, *temp, *str; 320 Entry entry; 321 register int length; 322 int lineno = 0; 323 int lines_skipped; 324 325 str = buff->buff; 326 if (!str) return; 327 for ( ; str < buff->buff + buff->used; 328 str = line + 1, lineno += lines_skipped) { 329 line = FindFirst(str, '\n', &lines_skipped); 330 lineno++; 331 if (!line) 332 line = buff->buff + buff->used; 333 if (*str == '!') 334 continue; 335 if (*str == '\n') 336 continue; 337 if (!bequiet && *str == '#') { 338 int dummy; 339 if (sscanf (str, "# %d", &dummy) == 1 || 340 sscanf (str, "# line %d", &dummy) == 1) 341 lineno = dummy - 1; 342 continue; 343 } 344 for (temp = str; 345 *temp && *temp != '\n' && isascii(*temp) && isspace(*temp); 346 temp++) ; 347 if (!*temp || *temp == '\n') continue; 348 349 colon = FindFirst(str, ':', NULL); 350 if (!colon || colon > line) { 351 if (!bequiet && !quiet) 352 fprintf (stderr, 353 "%s: colon missing on line %d, ignoring line\n", 354 ProgramName, lineno); 355 continue; 356 } 357 358 /* strip leading and trailing blanks from name and store result */ 359 while (*str == ' ' || *str == '\t') 360 str++; 361 length = colon - str; 362 while (length && (str[length-1] == ' ' || str[length-1] == '\t')) 363 length--; 364 temp = (char *)malloc(length + 1); 365 strncpy(temp, str, length); 366 temp[length] = '\0'; 367 entry.tag = temp; 368 369 /* strip leading and trailing blanks from value and store result */ 370 colon++; 371 while (*colon == ' ' || *colon == '\t') 372 colon++; 373 length = line - colon; 374 temp = (char *)malloc(length + 1); 375 strncpy(temp, colon, length); 376 temp[length] = '\0'; 377 entry.value = temp; 378 entry.lineno = bequiet ? 0 : lineno; 379 380 AddEntry(entries, &entry); 381 } 382} 383 384static void 385GetEntriesString(Entries *entries, char *str) 386{ 387 Buffer buff; 388 389 if (str && *str) { 390 buff.buff = str; 391 buff.used = strlen(str); 392 GetEntries(entries, &buff, 1); 393 } 394} 395 396static void 397ReadFile(Buffer *buffer, FILE *input) 398{ 399 char buf[BUFSIZ + 1]; 400 register int bytes; 401 402 buffer->used = 0; 403 while (!feof(input) && (bytes = fread(buf, 1, BUFSIZ, input)) > 0) { 404#ifdef WIN32 405 char *p; 406 buf[bytes] = '\0'; 407 for (p = buf; p = strchr(p, '\r'); ) { 408 if (p[-1] == '\\' && p[1] == '\n') { 409 bytes -= 3; 410 strcpy(p - 1, p + 2); 411 } 412 } 413#endif 414 AppendToBuffer(buffer, buf, bytes); 415 } 416 AppendToBuffer(buffer, "", 1); 417} 418 419static void 420AddDef(String *buff, char *title, char *value) 421{ 422#ifdef PATHETICCPP 423 if (need_real_defines) { 424 addstring(buff, "\n#define "); 425 addstring(buff, title); 426 if (value && (value[0] != '\0')) { 427 addstring(buff, " "); 428 addstring(buff, value); 429 } 430 return; 431 } 432#endif 433 if (buff->used) { 434 if (oper == OPSYMBOLS) 435 addstring(buff, "\n-D"); 436 else 437 addstring(buff, " -D"); 438 } else 439 addstring(buff, "-D"); 440 addstring(buff, title); 441 if (value && (value[0] != '\0')) { 442 addstring(buff, "="); 443 addstring(buff, value); 444 } 445} 446 447static void 448AddDefQ(String *buff, char *title, char *value) 449{ 450#ifdef PATHETICCPP 451 if (need_real_defines) 452 AddDef(buff, title, value); 453 else 454#endif 455 if (value && (value[0] != '\0')) { 456 AddDef(buff, title, "\""); 457 addstring(buff, value); 458 addstring(buff, "\""); 459 } else 460 AddDef(buff, title, NULL); 461} 462 463static void 464AddNum(String *buff, char *title, int value) 465{ 466 char num[20]; 467 snprintf(num, sizeof(num), "%d", value); 468 AddDef(buff, title, num); 469} 470 471static void 472AddSimpleDef(String *buff, char *title) 473{ 474 AddDef(buff, title, (char *)NULL); 475} 476 477static void 478AddDefTok(String *buff, char *prefix, char *title) 479{ 480 char *s; 481 char name[512]; 482 char c; 483 484 snprintf(name, sizeof(name), "%s%s", prefix, title); 485 for (s = name; (c = *s); s++) { 486 if (!isalpha(c) && !isdigit(c) && c != '_') 487 *s = '_'; 488 } 489 AddSimpleDef(buff, name); 490} 491 492static void 493AddUndef(String *buff, char *title) 494{ 495#ifdef PATHETICCPP 496 if (need_real_defines) { 497 addstring(buff, "\n#undef "); 498 addstring(buff, title); 499 return; 500 } 501#endif 502 if (buff->used) { 503 if (oper == OPSYMBOLS) 504 addstring(buff, "\n-U"); 505 else 506 addstring(buff, " -U"); 507 } else 508 addstring(buff, "-U"); 509 addstring(buff, title); 510} 511 512static void 513DoCmdDefines(String *buff) 514{ 515 int i; 516 char *arg, *val; 517 518 for (i = 0; i < num_cmd_defines; i++) { 519 arg = cmd_defines[i]; 520 if (arg[1] == 'D') { 521 val = strchr(arg, '='); 522 if (val) { 523 *val = '\0'; 524 AddDefQ(buff, arg + 2, val + 1); 525 *val = '='; 526 } else 527 AddSimpleDef(buff, arg + 2); 528 } else 529 AddUndef(buff, arg + 2); 530 } 531} 532 533static int 534Resolution(int pixels, int mm) 535{ 536 if (mm == 0) 537 return 0; 538 else 539 return ((pixels * 100000 / mm) + 50) / 100; 540} 541 542 543static void 544DoDisplayDefines(Display *display, String *defs, char *host) 545{ 546#ifndef MAXHOSTNAMELEN 547#define MAXHOSTNAMELEN 255 548#endif 549 char client[MAXHOSTNAMELEN], server[MAXHOSTNAMELEN], *colon; 550 char **extnames; 551 int n; 552 553 XmuGetHostname(client, MAXHOSTNAMELEN); 554 strncpy(server, XDisplayName(host), sizeof(server)); 555 server[sizeof(server) - 1] = '\0'; 556 /* search for final colon to skip over any embedded colons in IPv6 557 numeric address forms */ 558 colon = strrchr(server, ':'); 559 n = 0; 560 if (colon) { 561 /* remove extra colon if there are exactly two, since it indicates 562 DECnet. Three colons is an IPv6 address ending in :: though. */ 563 if ((colon > server) && (*(colon-1) == ':') && 564 ( ((colon - 1) == server) || (*(colon-2) != ':') ) ) { 565 *(colon-1) = ':'; 566 } 567 *colon++ = '\0'; 568 sscanf(colon, "%d", &n); 569 } 570 if (!*server || !strcmp(server, "unix") || !strcmp(server, "localhost")) 571 strcpy(server, client); 572 AddDef(defs, "HOST", server); /* R3 compatibility */ 573 AddDef(defs, "SERVERHOST", server); 574 AddDefTok(defs, "SRVR_", server); 575 AddNum(defs, "DISPLAY_NUM", n); 576 AddDef(defs, "CLIENTHOST", client); 577 AddDefTok(defs, "CLNT_", client); 578 AddNum(defs, "VERSION", ProtocolVersion(display)); 579 AddNum(defs, "REVISION", ProtocolRevision(display)); 580 AddDefQ(defs, "VENDOR", ServerVendor(display)); 581 AddDefTok(defs, "VNDR_", ServerVendor(display)); 582 AddNum(defs, "RELEASE", VendorRelease(display)); 583 AddNum(defs, "NUM_SCREENS", ScreenCount(display)); 584 extnames = XListExtensions(display, &n); 585 while (--n >= 0) 586 AddDefTok(defs, "EXT_", extnames[n]); 587 XFreeExtensionList(extnames); 588} 589 590static char *ClassNames[] = { 591 "StaticGray", 592 "GrayScale", 593 "StaticColor", 594 "PseudoColor", 595 "TrueColor", 596 "DirectColor" 597}; 598 599static void 600DoScreenDefines(Display *display, int scrno, String *defs) 601{ 602 Screen *screen; 603 Visual *visual; 604 XVisualInfo vinfo, *vinfos; 605 int nv, i, j; 606 char name[50]; 607 608 screen = ScreenOfDisplay(display, scrno); 609 visual = DefaultVisualOfScreen(screen); 610 vinfo.screen = scrno; 611 vinfos = XGetVisualInfo(display, VisualScreenMask, &vinfo, &nv); 612 AddNum(defs, "SCREEN_NUM", scrno); 613 AddNum(defs, "WIDTH", screen->width); 614 AddNum(defs, "HEIGHT", screen->height); 615 AddNum(defs, "X_RESOLUTION", Resolution(screen->width,screen->mwidth)); 616 AddNum(defs, "Y_RESOLUTION", Resolution(screen->height,screen->mheight)); 617 AddNum(defs, "PLANES", DisplayPlanes(display, scrno)); 618 AddNum(defs, "BITS_PER_RGB", visual->bits_per_rgb); 619 AddDef(defs, "CLASS", ClassNames[visual->class]); 620 snprintf(name, sizeof(name), "CLASS_%s", ClassNames[visual->class]); 621 AddNum(defs, name, (int)visual->visualid); 622 switch(visual->class) { 623 case StaticColor: 624 case PseudoColor: 625 case TrueColor: 626 case DirectColor: 627 AddSimpleDef(defs, "COLOR"); 628 break; 629 } 630 for (i = 0; i < nv; i++) { 631 for (j = i; --j >= 0; ) { 632 if (vinfos[j].class == vinfos[i].class && 633 vinfos[j].depth == vinfos[i].depth) 634 break; 635 } 636 if (j < 0) { 637 snprintf(name, sizeof(name), "CLASS_%s_%d", 638 ClassNames[vinfos[i].class], vinfos[i].depth); 639 AddNum(defs, name, (int)vinfos[i].visualid); 640 } 641 } 642 XFree((char *)vinfos); 643} 644 645static Entry * 646FindEntry(Entries *db, Buffer *b) 647{ 648 int i; 649 register Entry *e; 650 Entries phoney; 651 Entry entry; 652 653 entry.usable = False; 654 entry.tag = NULL; 655 entry.value = NULL; 656 phoney.used = 0; 657 phoney.room = 1; 658 phoney.entry = &entry; 659 GetEntries(&phoney, b, 1); 660 if (phoney.used < 1) 661 return NULL; 662 for (i = 0; i < db->used; i++) { 663 e = &db->entry[i]; 664 if (!e->usable) 665 continue; 666 if (strcmp(e->tag, entry.tag)) 667 continue; 668 e->usable = False; 669 if (strcmp(e->value, entry.value)) 670 return e; 671 return NULL; 672 } 673 return NULL; 674} 675 676static void 677EditFile(Entries *new, FILE *in, FILE *out) 678{ 679 Buffer b; 680 char buff[BUFSIZ]; 681 register Entry *e; 682 register char *c; 683 int i; 684 685 InitBuffer(&b); 686 while (in) { 687 b.used = 0; 688 while (1) { 689 buff[0] ='\0'; 690 if (!fgets(buff, BUFSIZ, in)) 691 goto cleanup; 692 AppendToBuffer(&b, buff, strlen(buff)); 693 c = &b.buff[b.used - 1]; 694 if ((*(c--) == '\n') && (b.used == 1 || *c != '\\')) 695 break; 696 } 697 if ((e = FindEntry(new, &b))) 698 fprintf(out, "%s:\t%s\n", e->tag, e->value); 699 else 700 fwrite(b.buff, 1, b.used, out); 701 } 702cleanup: 703 for (i = 0; i < new->used; i++) { 704 e = &new->entry[i]; 705 if (e->usable) 706 fprintf(out, "%s:\t%s\n", e->tag, e->value); 707 } 708} 709 710static void 711Syntax (void) 712{ 713 fprintf (stderr, 714 "usage: %s [-options ...] [filename]\n\n" 715 "where options include:\n" 716 " -display host:dpy display to use\n" 717 " -all do all resources [default]\n" 718 " -global do screen-independent resources\n" 719 " -screen do screen-specific resources for one screen\n" 720 " -screens do screen-specific resources for all screens\n" 721 " -n show but don't do changes\n" 722 " -cpp filename preprocessor to use [%s]\n" 723 " -nocpp do not use a preprocessor\n" 724 " -query query resources\n" 725 " -load load resources from file [default]\n" 726 " -override add in resources from file\n" 727 " -merge merge resources from file & sort\n" 728 " -edit filename edit resources into file\n" 729 " -backup string backup suffix for -edit [%s]\n" 730 " -symbols show preprocessor symbols\n" 731 " -remove remove resources\n" 732 " -retain avoid server reset (avoid using this)\n" 733 " -quiet don't warn about duplicates\n" 734 " -Dname[=value], -Uname, -Idirectory passed to preprocessor\n" 735 "\n" 736 "A - or no input filename represents stdin.\n", 737 ProgramName, CPP, BACKUP_SUFFIX); 738 exit (1); 739} 740 741/* 742 * The following is a hack until XrmParseCommand is ready. It determines 743 * whether or not the given string is an abbreviation of the arg. 744 */ 745 746static Bool 747isabbreviation(char *arg, char *s, int minslen) 748{ 749 int arglen; 750 int slen; 751 752 /* exact match */ 753 if (!strcmp (arg, s)) return (True); 754 755 arglen = strlen (arg); 756 slen = strlen (s); 757 758 /* too long or too short */ 759 if (slen >= arglen || slen < minslen) return (False); 760 761 /* abbreviation */ 762 if (strncmp (arg, s, slen) == 0) return (True); 763 764 /* bad */ 765 return (False); 766} 767 768static void 769addstring(String *arg, const char *s) 770{ 771 if(arg->used + strlen(s) + 1 >= arg->room) { 772 if(arg->val) 773 arg->val = (char *)realloc(arg->val, arg->room + CHUNK_SIZE); 774 else 775 arg->val = (char *)malloc(arg->room + CHUNK_SIZE); 776 if(arg->val == NULL) 777 fatal("%s: Not enough memory\n", ProgramName); 778 arg->room += CHUNK_SIZE; 779 } 780 if(arg->used) 781 strcat(arg->val, s); 782 else 783 strcpy(arg->val, s); 784 arg->used += strlen(s); 785} 786 787 788int 789main(int argc, char *argv[]) 790{ 791 int i; 792 char *displayname = NULL; 793 int whichResources = RALL; 794 int retainProp = 0; 795 FILE *fp = NULL; 796 Bool need_newline; 797 798 ProgramName = argv[0]; 799 800 defines.room = defines.used = includes.room = includes.used = 0; 801 802 /* initialize the includes String struct */ 803 addstring(&includes, ""); 804 805 /* Pick the default cpp to use. This needs to be done before 806 * we parse the command line in order to honor -nocpp which sets 807 * it back to NULL. 808 */ 809 if (cpp_program == NULL) { 810 int number_of_elements 811 = (sizeof cpp_locations) / (sizeof cpp_locations[0]); 812 int j; 813 814 for (j = 0; j < number_of_elements; j++) { 815 if (access(cpp_locations[j], X_OK) == 0) { 816 cpp_program = cpp_locations[j]; 817 break; 818 } 819 } 820 } 821 822 /* needs to be replaced with XrmParseCommand */ 823 824 for (i = 1; i < argc; i++) { 825 char *arg = argv[i]; 826 827 if (arg[0] == '-') { 828 if (arg[1] == '\0') { 829 filename = NULL; 830 continue; 831 } else if (isabbreviation ("-help", arg, 2)) { 832 Syntax (); 833 /* doesn't return */ 834 } else if (isabbreviation ("-display", arg, 2)) { 835 if (++i >= argc) Syntax (); 836 displayname = argv[i]; 837 continue; 838 } else if (isabbreviation ("-geometry", arg, 3)) { 839 if (++i >= argc) Syntax (); 840 /* ignore geometry */ 841 continue; 842 } else if (isabbreviation ("-cpp", arg, 2)) { 843 if (++i >= argc) Syntax (); 844 cpp_program = argv[i]; 845 continue; 846 } else if (isabbreviation ("-cppargs", arg, 2)) { 847 if (++i >= argc) Syntax (); 848 cpp_args = argv[i]; 849 continue; 850 } else if (!strcmp ("-n", arg)) { 851 dont_execute = True; 852 continue; 853 } else if (isabbreviation ("-nocpp", arg, 3)) { 854 cpp_program = NULL; 855 continue; 856 } else if (isabbreviation ("-query", arg, 2)) { 857 oper = OPQUERY; 858 continue; 859 } else if (isabbreviation ("-load", arg, 2)) { 860 oper = OPLOAD; 861 continue; 862 } else if (isabbreviation ("-merge", arg, 2)) { 863 oper = OPMERGE; 864 continue; 865 } else if (isabbreviation ("-override", arg, 2)) { 866 oper = OPOVERRIDE; 867 continue; 868 } else if (isabbreviation ("-symbols", arg, 3)) { 869 oper = OPSYMBOLS; 870 continue; 871 } else if (isabbreviation ("-remove", arg, 4)) { 872 oper = OPREMOVE; 873 continue; 874 } else if (isabbreviation ("-edit", arg, 2)) { 875 if (++i >= argc) Syntax (); 876 oper = OPEDIT; 877 editFile = argv[i]; 878 continue; 879 } else if (isabbreviation ("-backup", arg, 2)) { 880 if (++i >= argc) Syntax (); 881 backup_suffix = argv[i]; 882 continue; 883 } else if (isabbreviation ("-all", arg, 2)) { 884 whichResources = RALL; 885 continue; 886 } else if (isabbreviation ("-global", arg, 3)) { 887 whichResources = RGLOBAL; 888 continue; 889 } else if (isabbreviation ("-screen", arg, 3)) { 890 whichResources = RSCREEN; 891 continue; 892 } else if (!strcmp ("-screens", arg)) { 893 whichResources = RSCREENS; 894 continue; 895 } else if (isabbreviation ("-retain", arg, 4)) { 896 retainProp = 1; 897 continue; 898 } else if (isabbreviation ("-quiet", arg, 2)) { 899 quiet = True; 900 continue; 901 } else if (arg[1] == 'I') { 902 addstring(&includes, " "); 903 addstring(&includes, arg); 904 continue; 905 } else if (arg[1] == 'U' || arg[1] == 'D') { 906 if (num_cmd_defines < MAX_CMD_DEFINES) { 907 cmd_defines[num_cmd_defines++] = arg; 908 } else { 909 fatal("%s: Too many -U/-D arguments\n", ProgramName); 910 } 911 continue; 912 } 913 Syntax (); 914 } else if (arg[0] == '=') 915 continue; 916 else 917 filename = arg; 918 } /* end for */ 919 920#ifndef WIN32 921 while ((i = open("/dev/null", O_RDONLY)) < 3) 922 ; /* make sure later freopen won't clobber things */ 923 (void) close(i); 924#endif 925 /* Open display */ 926 if (!(dpy = XOpenDisplay (displayname))) 927 fatal("%s: Can't open display '%s'\n", ProgramName, 928 XDisplayName (displayname)); 929 930 if (whichResources == RALL && ScreenCount(dpy) == 1) 931 whichResources = RGLOBAL; 932 933#ifdef PATHETICCPP 934 if (cpp_program && 935 (oper == OPLOAD || oper == OPMERGE || oper == OPOVERRIDE)) { 936 need_real_defines = True; 937#ifdef WIN32 938 strcpy(tmpname2, "xrdbD_XXXXXX"); 939 strcpy(tmpname3, "\\temp\\xrdbD_XXXXXX"); 940#else 941#ifdef __UNIXOS2__ 942 { char *tmpdir=getenv("TMP"); 943 if (!tmpdir) tmpdir="/"; 944 sprintf(tmpname2, "%s/xrdbD_XXXXXX",tmpdir); 945 } 946#else 947 strcpy(tmpname2, "/tmp/xrdbD_XXXXXX"); 948#endif 949#endif 950 (void) mktemp(tmpname2); 951 } 952#endif 953 954 if (!filename && 955#ifdef PATHETICCPP 956 need_real_defines 957#else 958 (oper == OPLOAD || oper == OPMERGE || oper == OPOVERRIDE) && 959 (whichResources == RALL || whichResources == RSCREENS) 960#endif 961 ) { 962 char inputbuf[1024]; 963#ifdef WIN32 964 strcpy(tmpname, "\\temp\\xrdb_XXXXXX"); 965#else 966#ifdef __UNIXOS2__ 967 { char *tmpdir=getenv("TMP"); 968 if (!tmpdir) tmpdir="/"; 969 sprintf(tmpname, "%s/xrdb_XXXXXX",tmpdir); 970 } 971#else 972 strcpy(tmpname, "/tmp/xrdb_XXXXXX"); 973#endif 974#endif 975#ifndef HAVE_MKSTEMP 976 (void) mktemp(tmpname); 977 filename = tmpname; 978 fp = fopen(filename, "w"); 979#else 980 { 981 int fd = mkstemp(tmpname); 982 filename = tmpname; 983 fp = fdopen(fd, "w"); 984 } 985#endif /* MKSTEMP */ 986 if (!fp) 987 fatal("%s: Failed to open temp file: %s\n", ProgramName, 988 filename); 989 while (fgets(inputbuf, sizeof(inputbuf), stdin) != NULL) 990 fputs(inputbuf, fp); 991 fclose(fp); 992 } 993 994 DoDisplayDefines(dpy, &defines, displayname); 995 defines_base = defines.used; 996 need_newline = (oper == OPQUERY || oper == OPSYMBOLS || 997 (dont_execute && oper != OPREMOVE)); 998 InitBuffer(&buffer); 999 if (whichResources == RGLOBAL) 1000 Process(DefaultScreen(dpy), False, True); 1001 else if (whichResources == RSCREEN) 1002 Process(DefaultScreen(dpy), True, True); 1003 else if (whichResources == RSCREENS || 1004 (oper != OPLOAD && oper != OPMERGE && oper != OPOVERRIDE)) { 1005 if (whichResources == RALL && oper != OPSYMBOLS) { 1006 if (need_newline) 1007 printf("! screen-independent resources\n"); 1008 Process(0, False, True); 1009 if (need_newline) 1010 printf("\n"); 1011 } 1012 for (i = 0; i < ScreenCount(dpy); i++) { 1013 if (need_newline) { 1014 if (oper == OPSYMBOLS) 1015 printf("# screen %d symbols\n", i); 1016 else { 1017 printf("! screen %d resources\n", i); 1018 printf("#if SCREEN_NUM == %d\n", i); 1019 } 1020 } 1021 Process(i, True, True); 1022 if (need_newline) { 1023 if (oper != OPSYMBOLS) 1024 printf("#endif\n"); 1025 if (i+1 != ScreenCount(dpy)) 1026 printf("\n"); 1027 } 1028 } 1029 } 1030 else { 1031 Entries *dbs; 1032 1033 dbs = (Entries *)malloc(ScreenCount(dpy) * sizeof(Entries)); 1034 for (i = 0; i < ScreenCount(dpy); i++) { 1035 Process(i, True, False); 1036 dbs[i] = newDB; 1037 } 1038 InitEntries(&newDB); 1039 if (oper == OPMERGE || oper == OPOVERRIDE) 1040 GetEntriesString(&newDB, XResourceManagerString(dpy)); 1041 ShuffleEntries(&newDB, dbs, ScreenCount(dpy)); 1042 if (need_newline) 1043 printf("! screen-independent resources\n"); 1044 ReProcess(0, False); 1045 if (need_newline) 1046 printf("\n"); 1047 for (i = 0; i < ScreenCount(dpy); i++) { 1048 newDB = dbs[i]; 1049 if (need_newline) { 1050 printf("! screen %d resources\n", i); 1051 printf("#if SCREEN_NUM == %d\n", i); 1052 } 1053 ReProcess(i, True); 1054 if (need_newline) { 1055 printf("#endif\n"); 1056 if (i+1 != ScreenCount(dpy)) 1057 printf("\n"); 1058 } 1059 } 1060 } 1061 1062 if (fp) 1063 unlink(filename); 1064 if (retainProp) 1065 XSetCloseDownMode(dpy, RetainPermanent); 1066 XCloseDisplay(dpy); 1067 exit (0); 1068} 1069 1070 1071static void 1072FormatEntries(Buffer *buffer, Entries *entries) 1073{ 1074 register int i; 1075 1076 buffer->used = 0; 1077 if (!entries->used) 1078 return; 1079 if (oper == OPMERGE) 1080 qsort(entries->entry, entries->used, sizeof(Entry), 1081 CompareEntries); 1082 for (i = 0; i < entries->used; i++) { 1083 if (entries->entry[i].usable) 1084 AppendEntryToBuffer(buffer, &entries->entry[i]); 1085 } 1086} 1087 1088static void 1089StoreProperty(Display *dpy, Window root, Atom res_prop) 1090{ 1091 int len = buffer.used; 1092 int mode = PropModeReplace; 1093 unsigned char *buf = (unsigned char *)buffer.buff; 1094 int max = (XMaxRequestSize(dpy) << 2) - 28; 1095 1096 if (len > max) { 1097 XGrabServer(dpy); 1098 do { 1099 XChangeProperty(dpy, root, res_prop, XA_STRING, 8, mode, buf, max); 1100 buf += max; 1101 len -= max; 1102 mode = PropModeAppend; 1103 } while (len > max); 1104 } 1105 XChangeProperty(dpy, root, res_prop, XA_STRING, 8, mode, buf, len); 1106 if (mode != PropModeReplace) 1107 XUngrabServer(dpy); 1108} 1109 1110static void 1111Process(int scrno, Bool doScreen, Bool execute) 1112{ 1113 char *xdefs; 1114 Window root; 1115 Atom res_prop; 1116 FILE *input, *output; 1117 char *cmd; 1118 1119 defines.val[defines_base] = '\0'; 1120 defines.used = defines_base; 1121 buffer.used = 0; 1122 InitEntries(&newDB); 1123 DoScreenDefines(dpy, scrno, &defines); 1124 DoCmdDefines(&defines); 1125 if (doScreen) { 1126 xdefs = XScreenResourceString (ScreenOfDisplay(dpy, scrno)); 1127 root = RootWindow(dpy, scrno); 1128 res_prop = XInternAtom(dpy, SCREEN_RESOURCES, False); 1129 } else { 1130 xdefs = XResourceManagerString (dpy); 1131 root = RootWindow(dpy, 0); 1132 res_prop = XA_RESOURCE_MANAGER; 1133 } 1134 if (oper == OPSYMBOLS) { 1135 printf ("%s\n", defines.val); 1136 } else if (oper == OPQUERY) { 1137 if (xdefs) 1138 printf ("%s", xdefs); /* fputs broken in SunOS 4.0 */ 1139 } else if (oper == OPREMOVE) { 1140 if (xdefs) 1141 XDeleteProperty(dpy, root, res_prop); 1142 } else if (oper == OPEDIT) { 1143 char template[100], old[100]; 1144 1145 input = fopen(editFile, "r"); 1146 snprintf(template, sizeof(template), "%sXXXXXX", editFile); 1147#ifndef HAVE_MKSTEMP 1148 (void) mktemp(template); 1149 output = fopen(template, "w"); 1150#else 1151 { 1152 int fd = mkstemp(template); 1153 output = fdopen(fd, "w"); 1154 } 1155#endif 1156 if (!output) 1157 fatal("%s: can't open temporary file '%s'\n", ProgramName, template); 1158 GetEntriesString(&newDB, xdefs); 1159 EditFile(&newDB, input, output); 1160 if (input) 1161 fclose(input); 1162 fclose(output); 1163 snprintf(old, sizeof(old), "%s%s", editFile, backup_suffix); 1164 if (dont_execute) { /* then write to standard out */ 1165 char buf[BUFSIZ]; 1166 int n; 1167 1168 output = fopen (template, "r"); 1169 if (output) { 1170 while ((n = fread (buf, 1, sizeof buf, output)) > 0) { 1171 fwrite (buf, 1, n, stdout); 1172 } 1173 fclose (output); 1174 } 1175 unlink (template); 1176 } else { 1177 rename (editFile, old); 1178 if (rename (template, editFile)) 1179 fatal("%s: can't rename file '%s' to '%s'\n", ProgramName, 1180 template, editFile); 1181 } 1182 } else { 1183 if (oper == OPMERGE || oper == OPOVERRIDE) 1184 GetEntriesString(&newDB, xdefs); 1185#ifdef PATHETICCPP 1186 if (need_real_defines) { 1187#ifdef WIN32 1188 if (!(input = fopen(tmpname2, "w"))) 1189 fatal("%s: can't open file '%s'\n", ProgramName, tmpname2); 1190 fputs(defines.val, input); 1191 fprintf(input, "\n#include \"%s\"\n", filename); 1192 fclose(input); 1193 (void) mktemp(tmpname3); 1194 if (asprintf(&cmd, "%s%s%s -P%s %s > %s", cpp_program, 1195 cpp_args ? " " : "", 1196 cpp_args ? cpp_args : "", 1197 includes.val, 1198 tmpname2, tmpname3) == -1) 1199 fatal("%s: Out of memory\n", ProgramName); 1200 if (system(cmd) < 0) 1201 fatal("%s: cannot run '%s'\n", ProgramName, cmd); 1202 free(cmd); 1203 if (!(input = fopen(tmpname3, "r"))) 1204 fatal("%s: can't open file '%s'\n", ProgramName, tmpname3); 1205#else 1206 if (!freopen(tmpname2, "w+", stdin)) 1207 fatal("%s: can't open file '%s'\n", ProgramName, tmpname2); 1208 fputs(defines.val, stdin); 1209 fprintf(stdin, "\n#include \"%s\"\n", filename); 1210 fflush(stdin); 1211 fseek(stdin, 0, 0); 1212 if (asprintf(&cmd, "%s%s%s -P%s", cpp_program, 1213 cpp_args ? " " : "", 1214 cpp_args ? cpp_args : "", 1215 includes.val) == -1) 1216 fatal("%s: Out of memory\n", ProgramName); 1217 if (!(input = popen(cmd, "r"))) 1218 fatal("%s: cannot run '%s'\n", ProgramName, cmd); 1219 free(cmd); 1220#endif 1221 } else { 1222#endif 1223 if (filename) { 1224 if (!freopen (filename, "r", stdin)) 1225 fatal("%s: can't open file '%s'\n", ProgramName, filename); 1226 } 1227 if (cpp_program) { 1228#ifdef WIN32 1229 (void) mktemp(tmpname3); 1230 if (asprintf(&cmd, "%s%s%s -P%s %s %s > %s", cpp_program, 1231 cpp_args ? " " : "", 1232 cpp_args ? cpp_args : "", 1233 includes.val, defines.val, 1234 filename ? filename : "", tmpname3) == -1) 1235 fatal("%s: Out of memory\n", ProgramName); 1236 if (system(cmd) < 0) 1237 fatal("%s: cannot run '%s'\n", ProgramName, cmd); 1238 free(cmd); 1239 if (!(input = fopen(tmpname3, "r"))) 1240 fatal("%s: can't open file '%s'\n", ProgramName, tmpname3); 1241#else 1242 if (asprintf(&cmd, "%s%s%s -P%s %s %s", cpp_program, 1243 cpp_args ? " " : "", 1244 cpp_args ? cpp_args : "", 1245 includes.val, defines.val, 1246 filename ? filename : "") == -1) 1247 fatal("%s: Out of memory\n", ProgramName); 1248 if (!(input = popen(cmd, "r"))) 1249 fatal("%s: cannot run '%s'\n", ProgramName, cmd); 1250 free(cmd); 1251#endif 1252 } else { 1253 input = stdin; 1254 } 1255#ifdef PATHETICCPP 1256 } 1257#endif 1258 ReadFile(&buffer, input); 1259 if (cpp_program) { 1260#ifdef WIN32 1261 fclose(input); 1262#else 1263 pclose(input); 1264#endif 1265 } 1266#ifdef PATHETICCPP 1267 if (need_real_defines) { 1268 unlink(tmpname2); 1269#ifdef WIN32 1270 if (tmpname3[strlen(tmpname3) - 1] != 'X') 1271 unlink(tmpname3); 1272#endif 1273 } 1274#endif 1275 GetEntries(&newDB, &buffer, 0); 1276 if (execute) { 1277 FormatEntries(&buffer, &newDB); 1278 if (dont_execute) { 1279 if (buffer.used > 0) { 1280 fwrite (buffer.buff, 1, buffer.used, stdout); 1281 if (buffer.buff[buffer.used - 1] != '\n') putchar ('\n'); 1282 } 1283 } else if (buffer.used > 1 || !doScreen) 1284 StoreProperty (dpy, root, res_prop); 1285 else 1286 XDeleteProperty (dpy, root, res_prop); 1287 } 1288 } 1289 if (execute) 1290 FreeEntries(&newDB); 1291 if (doScreen && xdefs) 1292 XFree(xdefs); 1293} 1294 1295static void 1296ShuffleEntries(Entries *db, Entries *dbs, int num) 1297{ 1298 int *hits; 1299 register int i, j, k; 1300 Entries cur, cmp; 1301 char *curtag, *curvalue; 1302 1303 hits = (int *)malloc(num * sizeof(int)); 1304 cur = dbs[0]; 1305 for (i = 0; i < cur.used; i++) { 1306 curtag = cur.entry[i].tag; 1307 curvalue = cur.entry[i].value; 1308 for (j = 1; j < num; j++) { 1309 cmp = dbs[j]; 1310 for (k = 0; k < cmp.used; k++) { 1311 if (cmp.entry[k].usable && 1312 !strcmp(curtag, cmp.entry[k].tag) && 1313 !strcmp(curvalue, cmp.entry[k].value)) 1314 { 1315 hits[j] = k; 1316 break; 1317 } 1318 } 1319 if (k == cmp.used) 1320 break; 1321 } 1322 if (j == num) { 1323 AddEntry(db, &cur.entry[i]); 1324 hits[0] = i; 1325 for (j = 0; j < num; j++) 1326 dbs[j].entry[hits[j]].usable = False; 1327 } 1328 } 1329 free((char *)hits); 1330} 1331 1332static void 1333ReProcess(int scrno, Bool doScreen) 1334{ 1335 Window root; 1336 Atom res_prop; 1337 1338 FormatEntries(&buffer, &newDB); 1339 if (doScreen) { 1340 root = RootWindow(dpy, scrno); 1341 res_prop = XInternAtom(dpy, SCREEN_RESOURCES, False); 1342 } else { 1343 root = RootWindow(dpy, 0); 1344 res_prop = XA_RESOURCE_MANAGER; 1345 } 1346 if (dont_execute) { 1347 if (buffer.used > 0) { 1348 fwrite (buffer.buff, 1, buffer.used, stdout); 1349 if (buffer.buff[buffer.used - 1] != '\n') putchar ('\n'); 1350 } 1351 } else { 1352 if (buffer.used > 1 || !doScreen) 1353 StoreProperty (dpy, root, res_prop); 1354 else 1355 XDeleteProperty (dpy, root, res_prop); 1356 } 1357 FreeEntries(&newDB); 1358} 1359 1360static void 1361fatal(char *msg, ...) 1362{ 1363 va_list args; 1364 1365 if (errno != 0) 1366 perror(ProgramName); 1367 va_start(args, msg); 1368 vfprintf(stderr, msg, args); 1369 va_end(args); 1370 exit(1); 1371} 1372