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