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