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