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