(ediff-autostore-merges): Doc fix.
[bpt/emacs.git] / src / xrdb.c
CommitLineData
f3a0bf5c 1/* Deal with the X Resource Manager.
3a22ee35 2 Copyright (C) 1990, 1993, 1994 Free Software Foundation.
f3a0bf5c 3
3b7ad313
EN
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
f3a0bf5c 7it under the terms of the GNU General Public License as published by
d4327fec 8the Free Software Foundation; either version 2, or (at your option)
f3a0bf5c
JB
9any later version.
10
3b7ad313 11GNU Emacs is distributed in the hope that it will be useful,
f3a0bf5c
JB
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
3b7ad313
EN
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
f3a0bf5c
JB
20
21/* Written by jla, 4/90 */
22
a2a4d43e 23#ifdef emacs
18160b98 24#include <config.h>
a2a4d43e
JB
25#endif
26
012c3e71
RS
27#include <paths.h>
28
837255fb
JB
29#include <stdio.h>
30
a2a4d43e
JB
31#if 1 /* I'd really appreciate it if this code could go away... -JimB */
32/* this avoids lossage in the `dual-universe' headers on AT&T SysV X11 */
33#ifdef USG5
8f510a71 34#ifndef SYSV
a2a4d43e 35#define SYSV
8f510a71 36#endif
a2a4d43e
JB
37#include <unistd.h>
38#endif /* USG5 */
39
40#endif /* 1 */
54c908b5 41
f3a0bf5c
JB
42#include <X11/Xlib.h>
43#include <X11/Xatom.h>
dfc35f5f 44#if 0
f3a0bf5c 45#include <X11/Xos.h>
dfc35f5f 46#endif
f3a0bf5c
JB
47#include <X11/X.h>
48#include <X11/Xutil.h>
49#include <X11/Xresource.h>
531ff254
JB
50#ifdef VMS
51#include "vms-pwd.h"
52#else
f3a0bf5c 53#include <pwd.h>
531ff254 54#endif
f3a0bf5c
JB
55#include <sys/stat.h>
56
0f2cd61f
RS
57#if !defined(S_ISDIR) && defined(S_IFDIR)
58#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
59#endif
60
f3a0bf5c 61extern char *getenv ();
3d5d61ae
JB
62
63/* This does cause trouble on AIX. I'm going to take the comment at
64 face value. */
65#if 0
dfc35f5f
JB
66extern short getuid (); /* If this causes portability problems,
67 I think we should just delete it; it'll
68 default to `int' anyway. */
3d5d61ae
JB
69#endif
70
2d487ae5 71#ifdef DECLARE_GETPWUID_WITH_UID_T
3e0be4d0
RS
72extern struct passwd *getpwuid (uid_t);
73extern struct passwd *getpwnam (const char *);
74#else
f3a0bf5c
JB
75extern struct passwd *getpwuid ();
76extern struct passwd *getpwnam ();
3e0be4d0 77#endif
f3a0bf5c 78
e29ad342
KH
79extern char *get_system_name ();
80
837255fb
JB
81/* Make sure not to #include anything after these definitions. Let's
82 not step on anyone's prototypes. */
83#ifdef emacs
84#define malloc xmalloc
85#define realloc xrealloc
86#define free xfree
87#endif
88
89char *x_get_string_resource ();
90static int file_p ();
91
92\f
93/* X file search path processing. */
94
95
96/* The string which gets substituted for the %C escape in XFILESEARCHPATH
97 and friends, or zero if none was specified. */
98char *x_customization_string;
99
100
101/* Return the value of the emacs.customization (Emacs.Customization)
102 resource, for later use in search path decoding. If we find no
103 such resource, return zero. */
104char *
105x_get_customization_string (db, name, class)
106 XrmDatabase db;
107 char *name, *class;
108{
109 char *full_name
110 = (char *) alloca (strlen (name) + sizeof ("customization") + 3);
111 char *full_class
112 = (char *) alloca (strlen (class) + sizeof ("Customization") + 3);
113 char *result;
114
115 sprintf (full_name, "%s.%s", name, "customization");
116 sprintf (full_class, "%s.%s", class, "Customization");
117
118 result = x_get_string_resource (db, full_name, full_class);
119
120 if (result)
abfc2e5f
RS
121 {
122 char *copy = (char *) malloc (strlen (result) + 1);
123 strcpy (copy, result);
124 return copy;
125 }
837255fb
JB
126 else
127 return 0;
128}
129
130
131/* Expand all the Xt-style %-escapes in STRING, whose length is given
132 by STRING_LEN. Here are the escapes we're supposed to recognize:
133
134 %N The value of the application's class name
135 %T The value of the type parameter ("app-defaults" in this
136 context)
137 %S The value of the suffix parameter ("" in this context)
138 %L The language string associated with the specified display
139 (We use the "LANG" environment variable here, if it's set.)
140 %l The language part of the display's language string
141 (We treat this just like %L. If someone can tell us what
142 we're really supposed to do, dandy.)
143 %t The territory part of the display's language string
144 (This never gets used.)
145 %c The codeset part of the display's language string
146 (This never gets used either.)
147 %C The customization string retrieved from the resource
148 database associated with display.
149 (This is x_customization_string.)
150
151 Return the expanded file name if it exists and is readable, and
152 refers to %L only when the LANG environment variable is set, or
153 otherwise provided by X.
154
155 ESCAPED_SUFFIX and SUFFIX are postpended to STRING if they are
156 non-zero. %-escapes in ESCAPED_SUFFIX are expanded; STRING is left
157 alone.
158
159 Return NULL otherwise. */
160
161static char *
162magic_file_p (string, string_len, class, escaped_suffix, suffix)
163 char *string;
164 int string_len;
165 char *class, *escaped_suffix, *suffix;
166{
167 char *lang = getenv ("LANG");
168
169 int path_size = 100;
170 char *path = (char *) malloc (path_size);
171 int path_len = 0;
172
173 char *p = string;
174
175 while (p < string + string_len)
176 {
177 /* The chunk we're about to stick on the end of result. */
178 char *next;
179 int next_len;
180
181 if (*p == '%')
182 {
183 p++;
184
185 if (p >= string + string_len)
186 next_len = 0;
187 else
188 switch (*p)
189 {
190 case '%':
191 next = "%";
192 next_len = 1;
193 break;
194
195 case 'C':
196 next = (x_customization_string
197 ? x_customization_string
198 : "");
199 next_len = strlen (next);
200 break;
201
202 case 'N':
203 next = class;
204 next_len = strlen (class);
205 break;
206
207 case 'T':
208 next = "app-defaults";
209 next_len = strlen (next);
210 break;
211
212 default:
213 case 'S':
214 next_len = 0;
215 break;
216
217 case 'L':
218 case 'l':
219 if (! lang)
220 {
221 free (path);
222 return NULL;
223 }
224
225 next = lang;
226 next_len = strlen (next);
227 break;
228
229 case 't':
230 case 'c':
231 free (path);
232 return NULL;
233 }
234 }
235 else
236 next = p, next_len = 1;
237
238 /* Do we have room for this component followed by a '\0' ? */
239 if (path_len + next_len + 1 > path_size)
240 {
241 path_size = (path_len + next_len + 1) * 2;
242 path = (char *) realloc (path, path_size);
243 }
244
245 bcopy (next, path + path_len, next_len);
246 path_len += next_len;
247
248 p++;
249
250 /* If we've reached the end of the string, append ESCAPED_SUFFIX. */
251 if (p >= string + string_len && escaped_suffix)
252 {
253 string = escaped_suffix;
254 string_len = strlen (string);
255 p = string;
256 escaped_suffix = NULL;
257 }
258 }
259
260 /* Perhaps we should add the SUFFIX now. */
261 if (suffix)
262 {
263 int suffix_len = strlen (suffix);
264
265 if (path_len + suffix_len + 1 > path_size)
266 {
267 path_size = (path_len + suffix_len + 1);
268 path = (char *) realloc (path, path_size);
269 }
270
271 bcopy (suffix, path + path_len, suffix_len);
272 path_len += suffix_len;
273 }
274
275 path[path_len] = '\0';
276
277 if (! file_p (path))
278 {
279 free (path);
280 return NULL;
281 }
282
283 return path;
284}
285
286
f3a0bf5c 287static char *
837255fb 288gethomedir ()
f3a0bf5c 289{
f3a0bf5c
JB
290 struct passwd *pw;
291 char *ptr;
837255fb 292 char *copy;
f3a0bf5c
JB
293
294 if ((ptr = getenv ("HOME")) == NULL)
295 {
1cac1f6f
KH
296 if ((ptr = getenv ("LOGNAME")) != NULL
297 || (ptr = getenv ("USER")) != NULL)
f3a0bf5c
JB
298 pw = getpwnam (ptr);
299 else
1cac1f6f 300 pw = getpwuid (getuid ());
837255fb 301
f3a0bf5c
JB
302 if (pw)
303 ptr = pw->pw_dir;
f3a0bf5c
JB
304 }
305
837255fb
JB
306 if (ptr == NULL)
307 return "/";
f3a0bf5c 308
837255fb
JB
309 copy = (char *) malloc (strlen (ptr) + 2);
310 strcpy (copy, ptr);
311 strcat (copy, "/");
f3a0bf5c 312
837255fb 313 return copy;
f3a0bf5c
JB
314}
315
837255fb 316
f3a0bf5c
JB
317static int
318file_p (path)
319 char *path;
320{
321 struct stat status;
322
dfc35f5f 323 return (access (path, 4) == 0 /* exists and is readable */
f3a0bf5c 324 && stat (path, &status) == 0 /* get the status */
0f2cd61f 325 && (S_ISDIR (status.st_mode)) == 0); /* not a directory */
f3a0bf5c
JB
326}
327
f3a0bf5c 328
837255fb
JB
329/* Find the first element of SEARCH_PATH which exists and is readable,
330 after expanding the %-escapes. Return 0 if we didn't find any, and
331 the path name of the one we found otherwise. */
f3a0bf5c 332
837255fb
JB
333static char *
334search_magic_path (search_path, class, escaped_suffix, suffix)
335 char *search_path, *class, *escaped_suffix, *suffix;
f3a0bf5c 336{
837255fb 337 register char *s, *p;
d4327fec 338
837255fb 339 for (s = search_path; *s; s = p)
f3a0bf5c 340 {
837255fb
JB
341 for (p = s; *p && *p != ':'; p++)
342 ;
343
0f2cd61f 344 if (p > s)
f3a0bf5c 345 {
0f2cd61f 346 char *path = magic_file_p (s, p - s, class, escaped_suffix, suffix);
837255fb
JB
347 if (path)
348 return path;
f3a0bf5c 349 }
0f2cd61f 350 else if (*p == ':')
f3a0bf5c 351 {
0f2cd61f
RS
352 char *path;
353
354 s = "%N%S";
355 path = magic_file_p (s, strlen (s), class, escaped_suffix, suffix);
837255fb
JB
356 if (path)
357 return path;
f3a0bf5c
JB
358 }
359
837255fb
JB
360 if (*p == ':')
361 p++;
f3a0bf5c
JB
362 }
363
364 return 0;
365}
366\f
837255fb
JB
367/* Producing databases for individual sources. */
368
f3a0bf5c
JB
369static XrmDatabase
370get_system_app (class)
371 char *class;
372{
837255fb
JB
373 XrmDatabase db = NULL;
374 char *path;
f3a0bf5c 375
837255fb 376 path = getenv ("XFILESEARCHPATH");
58c7da0c 377 if (! path) path = PATH_X_DEFAULTS;
f3a0bf5c 378
837255fb
JB
379 path = search_magic_path (path, class, 0, 0);
380 if (path)
381 {
382 db = XrmGetFileDatabase (path);
383 free (path);
384 }
f3a0bf5c 385
f3a0bf5c
JB
386 return db;
387}
388
837255fb 389
f3a0bf5c
JB
390static XrmDatabase
391get_fallback (display)
392 Display *display;
393{
394 XrmDatabase db;
395
396 return NULL;
397}
398
837255fb 399
f3a0bf5c
JB
400static XrmDatabase
401get_user_app (class)
402 char *class;
403{
837255fb
JB
404 char *path;
405 char *file = 0;
3fa18fb2 406 char *free_it = 0;
837255fb
JB
407
408 /* Check for XUSERFILESEARCHPATH. It is a path of complete file
409 names, not directories. */
410 if (((path = getenv ("XUSERFILESEARCHPATH"))
411 && (file = search_magic_path (path, class, 0, 0)))
412
413 /* Check for APPLRESDIR; it is a path of directories. In each,
414 we have to search for LANG/CLASS and then CLASS. */
415 || ((path = getenv ("XAPPLRESDIR"))
416 && ((file = search_magic_path (path, class, "/%L/%N", 0))
417 || (file = search_magic_path (path, class, "/%N", 0))))
418
419 /* Check in the home directory. This is a bit of a hack; let's
420 hope one's home directory doesn't contain any %-escapes. */
3fa18fb2
RS
421 || (free_it = gethomedir (),
422 ((file = search_magic_path (free_it, class, "%L/%N", 0))
423 || (file = search_magic_path (free_it, class, "%N", 0)))))
f3a0bf5c 424 {
837255fb
JB
425 XrmDatabase db = XrmGetFileDatabase (file);
426 free (file);
3fa18fb2
RS
427 if (free_it)
428 free (free_it);
837255fb 429 return db;
f3a0bf5c 430 }
3fa18fb2
RS
431
432 if (free_it)
433 free (free_it);
434 return NULL;
f3a0bf5c
JB
435}
436
837255fb 437
f3a0bf5c
JB
438static XrmDatabase
439get_user_db (display)
440 Display *display;
441{
442 XrmDatabase db;
443 char *xdefs;
444
2c8d1900 445#ifdef PBaseSize /* Cheap way to test for X11R4 or later. */
b631f177
JB
446 xdefs = XResourceManagerString (display);
447#else
a2a4d43e 448 xdefs = display->xdefaults;
b631f177
JB
449#endif
450
f3a0bf5c
JB
451 if (xdefs != NULL)
452 db = XrmGetStringDatabase (xdefs);
453 else
454 {
837255fb
JB
455 char *home;
456 char *xdefault;
f3a0bf5c 457
837255fb
JB
458 home = gethomedir ();
459 xdefault = (char *) malloc (strlen (home) + sizeof (".Xdefaults"));
460 strcpy (xdefault, home);
f3a0bf5c
JB
461 strcat (xdefault, ".Xdefaults");
462 db = XrmGetFileDatabase (xdefault);
837255fb
JB
463 free (home);
464 free (xdefault);
f3a0bf5c
JB
465 }
466
d8717d15 467#ifdef HAVE_XSCREENRESOURCESTRING
9b37b1c2
JB
468 /* Get the screen-specific resources too. */
469 xdefs = XScreenResourceString (DefaultScreenOfDisplay (display));
470 if (xdefs != NULL)
fdce0b39
JB
471 {
472 XrmMergeDatabases (XrmGetStringDatabase (xdefs), &db);
473 XFree (xdefs);
474 }
9b37b1c2
JB
475#endif
476
f3a0bf5c
JB
477 return db;
478}
479
480static XrmDatabase
481get_environ_db ()
482{
483 XrmDatabase db;
484 char *p;
e29ad342 485 char *path = 0, *home = 0, *host;
f3a0bf5c
JB
486
487 if ((p = getenv ("XENVIRONMENT")) == NULL)
488 {
837255fb 489 home = gethomedir ();
e29ad342 490 host = get_system_name ();
837255fb
JB
491 path = (char *) malloc (strlen (home)
492 + sizeof (".Xdefaults-")
493 + strlen (host));
494 sprintf (path, "%s%s%s", home, ".Xdefaults-", host);
f3a0bf5c
JB
495 p = path;
496 }
497
498 db = XrmGetFileDatabase (p);
837255fb
JB
499
500 if (path) free (path);
501 if (home) free (home);
837255fb 502
f3a0bf5c
JB
503 return db;
504}
505\f
837255fb
JB
506/* External interface. */
507
f3a0bf5c
JB
508/* Types of values that we can find in a database */
509
510#define XrmStringType "String" /* String representation */
511XrmRepresentation x_rm_string; /* Quark representation */
512
513/* Load X resources based on the display and a possible -xrm option. */
514
515XrmDatabase
837255fb 516x_load_resources (display, xrm_string, myname, myclass)
f3a0bf5c 517 Display *display;
837255fb 518 char *xrm_string, *myname, *myclass;
f3a0bf5c
JB
519{
520 char *xdefs;
837255fb 521 XrmDatabase user_database;
f3a0bf5c
JB
522 XrmDatabase rdb;
523 XrmDatabase db;
524
525 x_rm_string = XrmStringToQuark (XrmStringType);
082dc211
RS
526#ifndef USE_X_TOOLKIT
527 /* pmr@osf.org says this shouldn't be done if USE_X_TOOLKIT.
528 I suspect it's because the toolkit version does this elsewhere. */
f3a0bf5c 529 XrmInitialize ();
082dc211 530#endif
f3a0bf5c
JB
531 rdb = XrmGetStringDatabase ("");
532
837255fb
JB
533 user_database = get_user_db (display);
534
535 /* Figure out what the "customization string" is, so we can use it
536 to decode paths. */
537 if (x_customization_string)
538 free (x_customization_string);
539 x_customization_string
540 = x_get_customization_string (user_database, myname, myclass);
541
f3a0bf5c
JB
542 /* Get application system defaults */
543 db = get_system_app (myclass);
544 if (db != NULL)
545 XrmMergeDatabases (db, &rdb);
546
547 /* Get Fallback resources */
548 db = get_fallback (display);
549 if (db != NULL)
550 XrmMergeDatabases (db, &rdb);
551
552 /* Get application user defaults */
553 db = get_user_app (myclass);
554 if (db != NULL)
555 XrmMergeDatabases (db, &rdb);
556
557 /* get User defaults */
837255fb
JB
558 if (user_database != NULL)
559 XrmMergeDatabases (user_database, &rdb);
f3a0bf5c
JB
560
561 /* Get Environment defaults. */
562 db = get_environ_db ();
563 if (db != NULL)
564 XrmMergeDatabases (db, &rdb);
565
566 /* Last, merge in any specification from the command line. */
567 if (xrm_string != NULL)
568 {
569 db = XrmGetStringDatabase (xrm_string);
570 if (db != NULL)
571 XrmMergeDatabases (db, &rdb);
572 }
573
574 return rdb;
575}
576
837255fb 577
f3a0bf5c
JB
578/* Retrieve the value of the resource specified by NAME with class CLASS
579 and of type TYPE from database RDB. The value is returned in RET_VALUE. */
580
581int
582x_get_resource (rdb, name, class, expected_type, ret_value)
583 XrmDatabase rdb;
584 char *name, *class;
585 XrmRepresentation expected_type;
586 XrmValue *ret_value;
587{
588 XrmValue value;
589 XrmName namelist[100];
590 XrmClass classlist[100];
591 XrmRepresentation type;
592
593 XrmStringToNameList(name, namelist);
594 XrmStringToClassList(class, classlist);
595
596 if (XrmQGetResource (rdb, namelist, classlist, &type, &value) == True
597 && (type == expected_type))
598 {
599 if (type == x_rm_string)
41ab0754 600 ret_value->addr = (char *) value.addr;
f3a0bf5c
JB
601 else
602 bcopy (value.addr, ret_value->addr, ret_value->size);
603
604 return value.size;
605 }
606
607 return 0;
608}
609
610/* Retrieve the string resource specified by NAME with CLASS from
611 database RDB. */
612
613char *
614x_get_string_resource (rdb, name, class)
615 XrmDatabase rdb;
616 char *name, *class;
617{
618 XrmValue value;
619
620 if (x_get_resource (rdb, name, class, x_rm_string, &value))
621 return (char *) value.addr;
622
623 return (char *) 0;
624}
625\f
837255fb
JB
626/* Stand-alone test facilities. */
627
f3a0bf5c 628#ifdef TESTRM
837255fb
JB
629
630typedef char **List;
631#define arg_listify(len, list) (list)
632#define car(list) (*(list))
633#define cdr(list) (list + 1)
634#define NIL(list) (! *(list))
635#define free_arglist(list)
636
637static List
638member (elt, list)
639 char *elt;
640 List list;
641{
642 List p;
643
644 for (p = list; ! NIL (p); p = cdr (p))
645 if (! strcmp (elt, car (p)))
646 return p;
647
648 return p;
649}
f3a0bf5c
JB
650
651static void
652fatal (msg, prog, x1, x2, x3, x4, x5)
653 char *msg, *prog;
654 int x1, x2, x3, x4, x5;
655{
656 extern int errno;
657
658 if (errno)
659 perror (prog);
660
661 (void) fprintf (stderr, msg, prog, x1, x2, x3, x4, x5);
662 exit (1);
663}
664
665main (argc, argv)
666 int argc;
667 char **argv;
668{
669 Display *display;
837255fb 670 char *displayname, *resource_string, *class, *name;
f3a0bf5c 671 XrmDatabase xdb;
837255fb 672 List arg_list, lp;
f3a0bf5c
JB
673
674 arg_list = arg_listify (argc, argv);
675
676 lp = member ("-d", arg_list);
677 if (!NIL (lp))
678 displayname = car (cdr (lp));
679 else
680 displayname = "localhost:0.0";
681
682 lp = member ("-xrm", arg_list);
683 if (! NIL (lp))
684 resource_string = car (cdr (lp));
685 else
686 resource_string = (char *) 0;
687
688 lp = member ("-c", arg_list);
689 if (! NIL (lp))
690 class = car (cdr (lp));
691 else
692 class = "Emacs";
693
837255fb
JB
694 lp = member ("-n", arg_list);
695 if (! NIL (lp))
696 name = car (cdr (lp));
697 else
698 name = "emacs";
f3a0bf5c 699
837255fb 700 free_arglist (arg_list);
f3a0bf5c
JB
701
702 if (!(display = XOpenDisplay (displayname)))
703 fatal ("Can't open display '%s'\n", XDisplayName (displayname));
704
837255fb 705 xdb = x_load_resources (display, resource_string, name, class);
f3a0bf5c 706
f3a0bf5c
JB
707 /* In a real program, you'd want to also do this: */
708 display->db = xdb;
f3a0bf5c
JB
709
710 while (1)
711 {
837255fb
JB
712 char query_name[90];
713 char query_class[90];
714
715 printf ("Name: ");
716 gets (query_name);
f3a0bf5c 717
837255fb 718 if (strlen (query_name))
f3a0bf5c 719 {
837255fb
JB
720 char *value;
721
722 printf ("Class: ");
723 gets (query_class);
724
725 value = x_get_string_resource (xdb, query_name, query_class);
f3a0bf5c
JB
726
727 if (value != NULL)
837255fb 728 printf ("\t%s(%s): %s\n\n", query_name, query_class, value);
f3a0bf5c
JB
729 else
730 printf ("\tNo Value.\n\n");
731 }
732 else
733 break;
734 }
735 printf ("\tExit.\n\n");
736
737 XCloseDisplay (display);
738}
739#endif /* TESTRM */