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