Merge branch 'devel/upgrade' into tizen
[platform/upstream/xrdb.git] / xrdb.c
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
95 typedef struct _Entry {
96     char *tag, *value;
97     int lineno;
98     Bool usable;
99 } Entry;
100 typedef struct _Buffer {
101     char *buff;
102     int  room, used;
103 } Buffer;
104 typedef struct _Entries {
105     Entry *entry;
106     int   room, used;
107 } Entries;
108
109 /* dynamically allocated strings */
110 #define CHUNK_SIZE 4096
111 typedef struct _String {
112     char *val;
113     int room, used;
114 } String;
115
116 static char *ProgramName;
117 static Bool quiet = False;
118 static char tmpname[32];
119 static char *filename = NULL;
120 #ifdef PATHETICCPP
121 static Bool need_real_defines = False;
122 static char tmpname2[32];
123 #endif
124 #ifdef WIN32
125 static char tmpname3[32];
126 #endif
127 static int oper = OPLOAD;
128 static char *editFile = NULL;
129 static const char *cpp_program = NULL;
130 static const char* const cpp_locations[] = { CPP };
131 static const char *backup_suffix = BACKUP_SUFFIX;
132 static Bool dont_execute = False;
133 static String defines;
134 static int defines_base;
135 #define MAX_CMD_DEFINES 512
136 static char *cmd_defines[MAX_CMD_DEFINES];
137 static int num_cmd_defines = 0;
138 static String includes;
139 static Display *dpy;
140 static Buffer buffer;
141 static Entries newDB;
142
143 static void fatal(const char *, ...) _X_ATTRIBUTE_PRINTF(1,2) _X_NORETURN;
144 static void addstring ( String *arg, const char *s );
145 static void addescapedstring ( String *arg, const char *s );
146 static void addtokstring ( String *arg, const char *s );
147 static void FormatEntries ( Buffer *b, Entries *entries );
148 static void StoreProperty ( Display *display, Window root, Atom res_prop );
149 static void Process ( int scrno, Bool doScreen, Bool execute );
150 static void ShuffleEntries ( Entries *db, Entries *dbs, int num );
151 static 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 */
155 static int _X_ATTRIBUTE_PRINTF(2,3)
156 asprintf(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
195 static void
196 InitBuffer(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
204 static void
205 FreeBuffer(Buffer *b)
206 {
207     free(b->buff);
208 }
209 #endif
210
211 static void
212 AppendToBuffer(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
222 static void
223 InitEntries(Entries *e)
224 {
225     e->room = INIT_ENTRY_SIZE;
226     e->used = 0;
227     e->entry = malloc(INIT_ENTRY_SIZE*sizeof(Entry));
228 }
229
230 static void
231 FreeEntries(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
244 static void
245 AddEntry(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
275 static int
276 CompareEntries(const void *e1, const void *e2)
277 {
278     return strcmp(((const Entry *)e1)->tag, ((const Entry *)e2)->tag);
279 }
280
281 static void
282 AppendEntryToBuffer(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  */
294 static const char *
295 FindFirst(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
313 static void
314 GetEntries(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
379 static void
380 GetEntriesString(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
391 static void
392 ReadFile(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
414 static void
415 AddDef(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
442 static void
443 AddSimpleDef(String *buff, const char *title)
444 {
445     AddDef(buff, title, (char *)NULL);
446 }
447
448 static void
449 AddDefQ(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
465 static void
466 AddNum(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
473 static void
474 AddDefTok(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
482 static void
483 AddDefHostname(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
498 static void
499 AddUndef(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
518 static void
519 DoCmdDefines(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
542 static int
543 Resolution(int pixels, int mm)
544 {
545     if (mm == 0)
546         return 0;
547     else
548         return ((pixels * 100000 / mm) + 50) / 100;
549 }
550
551
552 static void
553 DoDisplayDefines(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
599 static 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
610 static void
611 DoScreenDefines(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
670 static Entry *
671 FindEntry(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
701 static void
702 EditFile(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     }
727 cleanup:
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
735 static void _X_NORETURN
736 Syntax (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
773 static Bool
774 isabbreviation(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
795 static void
796 addstring(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
814 static void
815 addescapedstring(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
833 static void
834 addtokstring(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
849 int
850 main(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
1146 static void
1147 FormatEntries(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
1163 static void
1164 StoreProperty(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
1185 static void
1186 Process(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 = fdopen(fd, "w");
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
1371 static void
1372 ShuffleEntries(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
1408 static void
1409 ReProcess(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
1436 static void
1437 fatal(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 }