Update FSF's address in the preamble.
[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
367#define X_DEFAULT_SEARCH_PATH "/usr/lib/X11/%L/%T/%N%C%S:/usr/lib/X11/%l/%T/%N%C%S:/usr/lib/X11/%T/%N%C%S:/usr/lib/X11/%L/%T/%N%S:/usr/lib/X11/%l/%T/%N%S:/usr/lib/X11/%T/%N%S"
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
JB
376 path = getenv ("XFILESEARCHPATH");
377 if (! path) path = X_DEFAULT_SEARCH_PATH;
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;
406
407 /* Check for XUSERFILESEARCHPATH. It is a path of complete file
408 names, not directories. */
409 if (((path = getenv ("XUSERFILESEARCHPATH"))
410 && (file = search_magic_path (path, class, 0, 0)))
411
412 /* Check for APPLRESDIR; it is a path of directories. In each,
413 we have to search for LANG/CLASS and then CLASS. */
414 || ((path = getenv ("XAPPLRESDIR"))
415 && ((file = search_magic_path (path, class, "/%L/%N", 0))
416 || (file = search_magic_path (path, class, "/%N", 0))))
417
418 /* Check in the home directory. This is a bit of a hack; let's
419 hope one's home directory doesn't contain any %-escapes. */
420 || (path = gethomedir (),
421 ((file = search_magic_path (path, class, "%L/%N", 0))
422 || (file = search_magic_path (path, class, "%N", 0)))))
f3a0bf5c 423 {
837255fb
JB
424 XrmDatabase db = XrmGetFileDatabase (file);
425 free (file);
426 return db;
f3a0bf5c 427 }
837255fb 428 else
f3a0bf5c 429 return NULL;
f3a0bf5c
JB
430}
431
837255fb 432
f3a0bf5c
JB
433static XrmDatabase
434get_user_db (display)
435 Display *display;
436{
437 XrmDatabase db;
438 char *xdefs;
439
2c8d1900 440#ifdef PBaseSize /* Cheap way to test for X11R4 or later. */
b631f177
JB
441 xdefs = XResourceManagerString (display);
442#else
a2a4d43e 443 xdefs = display->xdefaults;
b631f177
JB
444#endif
445
f3a0bf5c
JB
446 if (xdefs != NULL)
447 db = XrmGetStringDatabase (xdefs);
448 else
449 {
837255fb
JB
450 char *home;
451 char *xdefault;
f3a0bf5c 452
837255fb
JB
453 home = gethomedir ();
454 xdefault = (char *) malloc (strlen (home) + sizeof (".Xdefaults"));
455 strcpy (xdefault, home);
f3a0bf5c
JB
456 strcat (xdefault, ".Xdefaults");
457 db = XrmGetFileDatabase (xdefault);
837255fb
JB
458 free (home);
459 free (xdefault);
f3a0bf5c
JB
460 }
461
d8717d15 462#ifdef HAVE_XSCREENRESOURCESTRING
9b37b1c2
JB
463 /* Get the screen-specific resources too. */
464 xdefs = XScreenResourceString (DefaultScreenOfDisplay (display));
465 if (xdefs != NULL)
fdce0b39
JB
466 {
467 XrmMergeDatabases (XrmGetStringDatabase (xdefs), &db);
468 XFree (xdefs);
469 }
9b37b1c2
JB
470#endif
471
f3a0bf5c
JB
472 return db;
473}
474
475static XrmDatabase
476get_environ_db ()
477{
478 XrmDatabase db;
479 char *p;
e29ad342 480 char *path = 0, *home = 0, *host;
f3a0bf5c
JB
481
482 if ((p = getenv ("XENVIRONMENT")) == NULL)
483 {
837255fb 484 home = gethomedir ();
e29ad342 485 host = get_system_name ();
837255fb
JB
486 path = (char *) malloc (strlen (home)
487 + sizeof (".Xdefaults-")
488 + strlen (host));
489 sprintf (path, "%s%s%s", home, ".Xdefaults-", host);
f3a0bf5c
JB
490 p = path;
491 }
492
493 db = XrmGetFileDatabase (p);
837255fb
JB
494
495 if (path) free (path);
496 if (home) free (home);
837255fb 497
f3a0bf5c
JB
498 return db;
499}
500\f
837255fb
JB
501/* External interface. */
502
f3a0bf5c
JB
503/* Types of values that we can find in a database */
504
505#define XrmStringType "String" /* String representation */
506XrmRepresentation x_rm_string; /* Quark representation */
507
508/* Load X resources based on the display and a possible -xrm option. */
509
510XrmDatabase
837255fb 511x_load_resources (display, xrm_string, myname, myclass)
f3a0bf5c 512 Display *display;
837255fb 513 char *xrm_string, *myname, *myclass;
f3a0bf5c
JB
514{
515 char *xdefs;
837255fb 516 XrmDatabase user_database;
f3a0bf5c
JB
517 XrmDatabase rdb;
518 XrmDatabase db;
519
520 x_rm_string = XrmStringToQuark (XrmStringType);
082dc211
RS
521#ifndef USE_X_TOOLKIT
522 /* pmr@osf.org says this shouldn't be done if USE_X_TOOLKIT.
523 I suspect it's because the toolkit version does this elsewhere. */
f3a0bf5c 524 XrmInitialize ();
082dc211 525#endif
f3a0bf5c
JB
526 rdb = XrmGetStringDatabase ("");
527
837255fb
JB
528 user_database = get_user_db (display);
529
530 /* Figure out what the "customization string" is, so we can use it
531 to decode paths. */
532 if (x_customization_string)
533 free (x_customization_string);
534 x_customization_string
535 = x_get_customization_string (user_database, myname, myclass);
536
f3a0bf5c
JB
537 /* Get application system defaults */
538 db = get_system_app (myclass);
539 if (db != NULL)
540 XrmMergeDatabases (db, &rdb);
541
542 /* Get Fallback resources */
543 db = get_fallback (display);
544 if (db != NULL)
545 XrmMergeDatabases (db, &rdb);
546
547 /* Get application user defaults */
548 db = get_user_app (myclass);
549 if (db != NULL)
550 XrmMergeDatabases (db, &rdb);
551
552 /* get User defaults */
837255fb
JB
553 if (user_database != NULL)
554 XrmMergeDatabases (user_database, &rdb);
f3a0bf5c
JB
555
556 /* Get Environment defaults. */
557 db = get_environ_db ();
558 if (db != NULL)
559 XrmMergeDatabases (db, &rdb);
560
561 /* Last, merge in any specification from the command line. */
562 if (xrm_string != NULL)
563 {
564 db = XrmGetStringDatabase (xrm_string);
565 if (db != NULL)
566 XrmMergeDatabases (db, &rdb);
567 }
568
569 return rdb;
570}
571
837255fb 572
f3a0bf5c
JB
573/* Retrieve the value of the resource specified by NAME with class CLASS
574 and of type TYPE from database RDB. The value is returned in RET_VALUE. */
575
576int
577x_get_resource (rdb, name, class, expected_type, ret_value)
578 XrmDatabase rdb;
579 char *name, *class;
580 XrmRepresentation expected_type;
581 XrmValue *ret_value;
582{
583 XrmValue value;
584 XrmName namelist[100];
585 XrmClass classlist[100];
586 XrmRepresentation type;
587
588 XrmStringToNameList(name, namelist);
589 XrmStringToClassList(class, classlist);
590
591 if (XrmQGetResource (rdb, namelist, classlist, &type, &value) == True
592 && (type == expected_type))
593 {
594 if (type == x_rm_string)
41ab0754 595 ret_value->addr = (char *) value.addr;
f3a0bf5c
JB
596 else
597 bcopy (value.addr, ret_value->addr, ret_value->size);
598
599 return value.size;
600 }
601
602 return 0;
603}
604
605/* Retrieve the string resource specified by NAME with CLASS from
606 database RDB. */
607
608char *
609x_get_string_resource (rdb, name, class)
610 XrmDatabase rdb;
611 char *name, *class;
612{
613 XrmValue value;
614
615 if (x_get_resource (rdb, name, class, x_rm_string, &value))
616 return (char *) value.addr;
617
618 return (char *) 0;
619}
620\f
837255fb
JB
621/* Stand-alone test facilities. */
622
f3a0bf5c 623#ifdef TESTRM
837255fb
JB
624
625typedef char **List;
626#define arg_listify(len, list) (list)
627#define car(list) (*(list))
628#define cdr(list) (list + 1)
629#define NIL(list) (! *(list))
630#define free_arglist(list)
631
632static List
633member (elt, list)
634 char *elt;
635 List list;
636{
637 List p;
638
639 for (p = list; ! NIL (p); p = cdr (p))
640 if (! strcmp (elt, car (p)))
641 return p;
642
643 return p;
644}
f3a0bf5c
JB
645
646static void
647fatal (msg, prog, x1, x2, x3, x4, x5)
648 char *msg, *prog;
649 int x1, x2, x3, x4, x5;
650{
651 extern int errno;
652
653 if (errno)
654 perror (prog);
655
656 (void) fprintf (stderr, msg, prog, x1, x2, x3, x4, x5);
657 exit (1);
658}
659
660main (argc, argv)
661 int argc;
662 char **argv;
663{
664 Display *display;
837255fb 665 char *displayname, *resource_string, *class, *name;
f3a0bf5c 666 XrmDatabase xdb;
837255fb 667 List arg_list, lp;
f3a0bf5c
JB
668
669 arg_list = arg_listify (argc, argv);
670
671 lp = member ("-d", arg_list);
672 if (!NIL (lp))
673 displayname = car (cdr (lp));
674 else
675 displayname = "localhost:0.0";
676
677 lp = member ("-xrm", arg_list);
678 if (! NIL (lp))
679 resource_string = car (cdr (lp));
680 else
681 resource_string = (char *) 0;
682
683 lp = member ("-c", arg_list);
684 if (! NIL (lp))
685 class = car (cdr (lp));
686 else
687 class = "Emacs";
688
837255fb
JB
689 lp = member ("-n", arg_list);
690 if (! NIL (lp))
691 name = car (cdr (lp));
692 else
693 name = "emacs";
f3a0bf5c 694
837255fb 695 free_arglist (arg_list);
f3a0bf5c
JB
696
697 if (!(display = XOpenDisplay (displayname)))
698 fatal ("Can't open display '%s'\n", XDisplayName (displayname));
699
837255fb 700 xdb = x_load_resources (display, resource_string, name, class);
f3a0bf5c 701
f3a0bf5c
JB
702 /* In a real program, you'd want to also do this: */
703 display->db = xdb;
f3a0bf5c
JB
704
705 while (1)
706 {
837255fb
JB
707 char query_name[90];
708 char query_class[90];
709
710 printf ("Name: ");
711 gets (query_name);
f3a0bf5c 712
837255fb 713 if (strlen (query_name))
f3a0bf5c 714 {
837255fb
JB
715 char *value;
716
717 printf ("Class: ");
718 gets (query_class);
719
720 value = x_get_string_resource (xdb, query_name, query_class);
f3a0bf5c
JB
721
722 if (value != NULL)
837255fb 723 printf ("\t%s(%s): %s\n\n", query_name, query_class, value);
f3a0bf5c
JB
724 else
725 printf ("\tNo Value.\n\n");
726 }
727 else
728 break;
729 }
730 printf ("\tExit.\n\n");
731
732 XCloseDisplay (display);
733}
734#endif /* TESTRM */