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