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