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