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