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