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