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