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