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