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