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