(unexec): Cast 1st arg in mmap calls.
[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;
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 */