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