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