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