mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-07 04:10:27 -08:00
This fixes a bug that has been present in Emacs since its creation. It was reported by Chris Torek in 1983 even before GNU Emacs existed, which must set some sort of record. (Torek's bug report was against a predecessor of GNU Emacs, but GNU Emacs happened to have the same common flaw.) See Torek's Usenet posting "setuid/setgid programs & Emacs" Article-I.D.: sri-arpa.858 Posted: Fri Apr 8 14:18:56 1983. * .bzrignore: Add lib/fcntl.h. * configure.ac (euidaccess): Remove check; gnulib does this for us now. (gl_FCNTL_O_FLAGS): Define a dummy version. * lib/at-func.c, lib/euidaccess.c, lib/faccessat.c, lib/fcntl.in.h: * lib/getgroups.c, lib/group-member.c, lib/root-uid.h: * lib/xalloc-oversized.h, m4/euidaccess.m4, m4/faccessat.m4: * m4/fcntl_h.m4, m4/getgroups.m4, m4/group-member.m4: New files, from gnulib. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * admin/merge-gnulib (GNULIB_MODULES): Add faccessat. (GNULIB_TOOL_FLAGS): Avoid at-internal, fchdir, malloc-posix, openat-die, openat-h, save-cwd. Do not avoid fcntl-h. Omit gnulib's m4/fcntl-o.m4. * nt/inc/ms-w32.h (AT_FDCWD, AT_EACCESS): New symbols. (access): Remove. (faccessat): New macro. * src/Makefile.in (LIB_EACCESS): New macro. (LIBES): Use it. * src/callproc.c (init_callproc): * src/charset.c (init_charset): * src/fileio.c (check_existing, check_executable, check_writable) (Ffile_readable_p): * src/lread.c (openp, load_path_check): * src/process.c (allocate_pty): * src/xrdb.c (file_p): Use effective UID when checking permissions, not real UID. * src/callproc.c (init_callproc): * src/charset.c (init_charset): * src/lread.c (load_path_check, init_lread): Test whether directories are accessible, not merely whether they exist. * src/conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): New macro. * src/fileio.c (check_existing, check_executable, check_writable) (Ffile_readable_p): Use symbolic names instead of integers for the flags, as they're portable now. (check_writable): New arg AMODE. All uses changed. Set errno on failure. (Ffile_readable_p): Use faccessat, not stat + open + close. (Ffile_writable_p): No need to call check_existing + check_writable. Just call check_writable and then look at errno. This saves a syscall. dir should never be nil; replace an unnecessary runtime check with an eassert. When checking the parent directory of a nonexistent file, check that the directory is searchable as well as writable, as we can't create files in unsearchable directories. (file_directory_p): New function, which uses 'stat' on most platforms but faccessat with D_OK (for efficiency) if WINDOWSNT. (Ffile_directory_p, Fset_file_times): Use it. (file_accessible_directory_p): New function, which uses a single syscall for efficiency. (Ffile_accessible_directory_p): Use it. * src/xrdb.c (file_p): Use file_directory_p. * src/lisp.h (file_directory_p, file_accessible_directory_p): New decls. * src/lread.c (openp): When opening a file, use fstat rather than stat, as that avoids a permissions race. When not opening a file, use file_directory_p rather than stat. (dir_warning): First arg is now a usage string, not a format. Use errno. All uses changed. * src/nsterm.m (ns_term_init): Remove unnecessary call to file-readable that merely introduced a race. * src/process.c, src/sysdep.c, src/term.c: All uses of '#ifdef O_NONBLOCK' changed to '#if O_NONBLOCK', to accommodate gnulib O_* style, and similarly for the other O_* flags. * src/w32.c (sys_faccessat): Rename from sys_access and switch to faccessat's API. All uses changed. * src/xrdb.c: Do not include <sys/stat.h>; no longer needed. (magic_db): Rename from magic_file_p. (magic_db, search_magic_path): Return an XrmDatabase rather than a char *, so that we don't have to test for file existence separately from opening the file for reading. This removes a race fixes a permission-checking problem, and simplifies the code. All uses changed. (file_p): Remove; no longer needed. Fixes: debbugs:12632
718 lines
17 KiB
C
718 lines
17 KiB
C
/* Deal with the X Resource Manager.
|
||
Copyright (C) 1990, 1993-1994, 2000-2012 Free Software Foundation, Inc.
|
||
|
||
Author: Joseph Arceneaux
|
||
Created: 4/90
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include <config.h>
|
||
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#include <epaths.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
|
||
#include "lisp.h"
|
||
|
||
/* This may include sys/types.h, and that somehow loses
|
||
if this is not done before the other system files. */
|
||
#include "xterm.h"
|
||
|
||
#include <X11/Xlib.h>
|
||
#include <X11/Xatom.h>
|
||
#include <X11/X.h>
|
||
#include <X11/Xutil.h>
|
||
#include <X11/Xresource.h>
|
||
#ifdef HAVE_PWD_H
|
||
#include <pwd.h>
|
||
#endif
|
||
|
||
#ifdef USE_MOTIF
|
||
/* For Vdouble_click_time. */
|
||
#include "keyboard.h"
|
||
#endif
|
||
|
||
char *x_get_string_resource (XrmDatabase rdb, const char *name,
|
||
const char *class);
|
||
|
||
|
||
/* X file search path processing. */
|
||
|
||
|
||
/* The string which gets substituted for the %C escape in XFILESEARCHPATH
|
||
and friends, or zero if none was specified. */
|
||
static char *x_customization_string;
|
||
|
||
|
||
/* Return the value of the emacs.customization (Emacs.Customization)
|
||
resource, for later use in search path decoding. If we find no
|
||
such resource, return zero. */
|
||
static char *
|
||
x_get_customization_string (XrmDatabase db, const char *name,
|
||
const char *class)
|
||
{
|
||
char *full_name = alloca (strlen (name) + sizeof "customization" + 3);
|
||
char *full_class = alloca (strlen (class) + sizeof "Customization" + 3);
|
||
char *result;
|
||
|
||
sprintf (full_name, "%s.%s", name, "customization");
|
||
sprintf (full_class, "%s.%s", class, "Customization");
|
||
|
||
result = x_get_string_resource (db, full_name, full_class);
|
||
|
||
if (result)
|
||
{
|
||
char *copy = xmalloc (strlen (result) + 1);
|
||
strcpy (copy, result);
|
||
return copy;
|
||
}
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Expand all the Xt-style %-escapes in STRING, whose length is given
|
||
by STRING_LEN. Here are the escapes we're supposed to recognize:
|
||
|
||
%N The value of the application's class name
|
||
%T The value of the type parameter ("app-defaults" in this
|
||
context)
|
||
%S The value of the suffix parameter ("" in this context)
|
||
%L The language string associated with the specified display
|
||
(We use the "LANG" environment variable here, if it's set.)
|
||
%l The language part of the display's language string
|
||
(We treat this just like %L. If someone can tell us what
|
||
we're really supposed to do, dandy.)
|
||
%t The territory part of the display's language string
|
||
(This never gets used.)
|
||
%c The codeset part of the display's language string
|
||
(This never gets used either.)
|
||
%C The customization string retrieved from the resource
|
||
database associated with display.
|
||
(This is x_customization_string.)
|
||
|
||
Return the resource database if its file was read successfully, and
|
||
refers to %L only when the LANG environment variable is set, or
|
||
otherwise provided by X.
|
||
|
||
ESCAPED_SUFFIX is postpended to STRING if it is non-zero.
|
||
%-escapes in ESCAPED_SUFFIX are expanded.
|
||
|
||
Return NULL otherwise. */
|
||
|
||
static XrmDatabase
|
||
magic_db (const char *string, ptrdiff_t string_len, const char *class,
|
||
const char *escaped_suffix)
|
||
{
|
||
XrmDatabase db;
|
||
char *lang = getenv ("LANG");
|
||
|
||
ptrdiff_t path_size = 100;
|
||
char *path = xmalloc (path_size);
|
||
ptrdiff_t path_len = 0;
|
||
|
||
const char *p = string;
|
||
|
||
while (p < string + string_len)
|
||
{
|
||
/* The chunk we're about to stick on the end of result. */
|
||
const char *next = NULL;
|
||
ptrdiff_t next_len;
|
||
|
||
if (*p == '%')
|
||
{
|
||
p++;
|
||
|
||
if (p >= string + string_len)
|
||
next_len = 0;
|
||
else
|
||
switch (*p)
|
||
{
|
||
case '%':
|
||
next = "%";
|
||
next_len = 1;
|
||
break;
|
||
|
||
case 'C':
|
||
next = (x_customization_string
|
||
? x_customization_string
|
||
: "");
|
||
next_len = strlen (next);
|
||
break;
|
||
|
||
case 'N':
|
||
next = class;
|
||
next_len = strlen (class);
|
||
break;
|
||
|
||
case 'T':
|
||
next = "app-defaults";
|
||
next_len = strlen (next);
|
||
break;
|
||
|
||
default:
|
||
case 'S':
|
||
next_len = 0;
|
||
break;
|
||
|
||
case 'L':
|
||
case 'l':
|
||
if (! lang)
|
||
{
|
||
xfree (path);
|
||
return NULL;
|
||
}
|
||
|
||
next = lang;
|
||
next_len = strlen (next);
|
||
break;
|
||
|
||
case 't':
|
||
case 'c':
|
||
xfree (path);
|
||
return NULL;
|
||
}
|
||
}
|
||
else
|
||
next = p, next_len = 1;
|
||
|
||
/* Do we have room for this component followed by a '\0' ? */
|
||
if (path_size - path_len <= next_len)
|
||
{
|
||
if (min (PTRDIFF_MAX, SIZE_MAX) / 2 - 1 - path_len < next_len)
|
||
memory_full (SIZE_MAX);
|
||
path_size = (path_len + next_len + 1) * 2;
|
||
path = xrealloc (path, path_size);
|
||
}
|
||
|
||
memcpy (path + path_len, next, next_len);
|
||
path_len += next_len;
|
||
|
||
p++;
|
||
|
||
/* If we've reached the end of the string, append ESCAPED_SUFFIX. */
|
||
if (p >= string + string_len && escaped_suffix)
|
||
{
|
||
string = escaped_suffix;
|
||
string_len = strlen (string);
|
||
p = string;
|
||
escaped_suffix = NULL;
|
||
}
|
||
}
|
||
|
||
path[path_len] = '\0';
|
||
db = XrmGetFileDatabase (path);
|
||
xfree (path);
|
||
return db;
|
||
}
|
||
|
||
|
||
static char *
|
||
gethomedir (void)
|
||
{
|
||
struct passwd *pw;
|
||
char *ptr;
|
||
char *copy;
|
||
|
||
if ((ptr = getenv ("HOME")) == NULL)
|
||
{
|
||
if ((ptr = getenv ("LOGNAME")) != NULL
|
||
|| (ptr = getenv ("USER")) != NULL)
|
||
pw = getpwnam (ptr);
|
||
else
|
||
pw = getpwuid (getuid ());
|
||
|
||
if (pw)
|
||
ptr = pw->pw_dir;
|
||
}
|
||
|
||
if (ptr == NULL)
|
||
return xstrdup ("/");
|
||
|
||
copy = xmalloc (strlen (ptr) + 2);
|
||
strcpy (copy, ptr);
|
||
strcat (copy, "/");
|
||
|
||
return copy;
|
||
}
|
||
|
||
|
||
/* Find the first element of SEARCH_PATH which exists and is readable,
|
||
after expanding the %-escapes. Return 0 if we didn't find any, and
|
||
the path name of the one we found otherwise. */
|
||
|
||
static XrmDatabase
|
||
search_magic_path (const char *search_path, const char *class,
|
||
const char *escaped_suffix)
|
||
{
|
||
const char *s, *p;
|
||
|
||
for (s = search_path; *s; s = p)
|
||
{
|
||
for (p = s; *p && *p != ':'; p++)
|
||
;
|
||
|
||
if (p > s)
|
||
{
|
||
XrmDatabase db = magic_db (s, p - s, class, escaped_suffix);
|
||
if (db)
|
||
return db;
|
||
}
|
||
else if (*p == ':')
|
||
{
|
||
static char const ns[] = "%N%S";
|
||
XrmDatabase db = magic_db (ns, strlen (ns), class, escaped_suffix);
|
||
if (db)
|
||
return db;
|
||
}
|
||
|
||
if (*p == ':')
|
||
p++;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Producing databases for individual sources. */
|
||
|
||
static XrmDatabase
|
||
get_system_app (const char *class)
|
||
{
|
||
const char *path;
|
||
|
||
path = getenv ("XFILESEARCHPATH");
|
||
if (! path) path = PATH_X_DEFAULTS;
|
||
|
||
return search_magic_path (path, class, 0);
|
||
}
|
||
|
||
|
||
static XrmDatabase
|
||
get_fallback (Display *display)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
|
||
static XrmDatabase
|
||
get_user_app (const char *class)
|
||
{
|
||
XrmDatabase db = 0;
|
||
const char *path;
|
||
|
||
/* Check for XUSERFILESEARCHPATH. It is a path of complete file
|
||
names, not directories. */
|
||
path = getenv ("XUSERFILESEARCHPATH");
|
||
if (path)
|
||
db = search_magic_path (path, class, 0);
|
||
|
||
if (! db)
|
||
{
|
||
/* Check for APPLRESDIR; it is a path of directories. In each,
|
||
we have to search for LANG/CLASS and then CLASS. */
|
||
path = getenv ("XAPPLRESDIR");
|
||
if (path)
|
||
{
|
||
db = search_magic_path (path, class, "/%L/%N");
|
||
if (!db)
|
||
db = search_magic_path (path, class, "/%N");
|
||
}
|
||
}
|
||
|
||
if (! db)
|
||
{
|
||
/* Check in the home directory. This is a bit of a hack; let's
|
||
hope one's home directory doesn't contain any %-escapes. */
|
||
char *home = gethomedir ();
|
||
db = search_magic_path (home, class, "%L/%N");
|
||
if (! db)
|
||
db = search_magic_path (home, class, "%N");
|
||
xfree (home);
|
||
}
|
||
|
||
return db;
|
||
}
|
||
|
||
|
||
static XrmDatabase
|
||
get_user_db (Display *display)
|
||
{
|
||
XrmDatabase db;
|
||
char *xdefs;
|
||
|
||
#ifdef PBaseSize /* Cheap way to test for X11R4 or later. */
|
||
xdefs = XResourceManagerString (display);
|
||
#else
|
||
xdefs = display->xdefaults;
|
||
#endif
|
||
|
||
if (xdefs != NULL)
|
||
db = XrmGetStringDatabase (xdefs);
|
||
else
|
||
{
|
||
char *home;
|
||
char *xdefault;
|
||
|
||
home = gethomedir ();
|
||
xdefault = xmalloc (strlen (home) + sizeof ".Xdefaults");
|
||
strcpy (xdefault, home);
|
||
strcat (xdefault, ".Xdefaults");
|
||
db = XrmGetFileDatabase (xdefault);
|
||
xfree (home);
|
||
xfree (xdefault);
|
||
}
|
||
|
||
#ifdef HAVE_XSCREENRESOURCESTRING
|
||
/* Get the screen-specific resources too. */
|
||
xdefs = XScreenResourceString (DefaultScreenOfDisplay (display));
|
||
if (xdefs != NULL)
|
||
{
|
||
XrmMergeDatabases (XrmGetStringDatabase (xdefs), &db);
|
||
XFree (xdefs);
|
||
}
|
||
#endif
|
||
|
||
return db;
|
||
}
|
||
|
||
static XrmDatabase
|
||
get_environ_db (void)
|
||
{
|
||
XrmDatabase db;
|
||
char *p;
|
||
char *path = 0;
|
||
|
||
if ((p = getenv ("XENVIRONMENT")) == NULL)
|
||
{
|
||
static char const xdefaults[] = ".Xdefaults-";
|
||
char *home = gethomedir ();
|
||
char const *host = SSDATA (Vsystem_name);
|
||
ptrdiff_t pathsize = (strlen (home) + sizeof xdefaults
|
||
+ SBYTES (Vsystem_name));
|
||
path = xrealloc (home, pathsize);
|
||
strcat (strcat (path, xdefaults), host);
|
||
p = path;
|
||
}
|
||
|
||
db = XrmGetFileDatabase (p);
|
||
|
||
xfree (path);
|
||
|
||
return db;
|
||
}
|
||
|
||
/* External interface. */
|
||
|
||
/* Types of values that we can find in a database */
|
||
|
||
#define XrmStringType "String" /* String representation */
|
||
static XrmRepresentation x_rm_string; /* Quark representation */
|
||
|
||
/* Load X resources based on the display and a possible -xrm option. */
|
||
|
||
XrmDatabase
|
||
x_load_resources (Display *display, const char *xrm_string,
|
||
const char *myname, const char *myclass)
|
||
{
|
||
XrmDatabase user_database;
|
||
XrmDatabase rdb;
|
||
XrmDatabase db;
|
||
char line[256];
|
||
|
||
#if defined USE_MOTIF || !defined HAVE_XFT || !defined USE_LUCID
|
||
const char *helv = "-*-helvetica-medium-r-*--*-120-*-*-*-*-iso8859-1";
|
||
#endif
|
||
|
||
#ifdef USE_MOTIF
|
||
const char *courier = "-*-courier-medium-r-*-*-*-120-*-*-*-*-iso8859-1";
|
||
#endif
|
||
|
||
x_rm_string = XrmStringToQuark (XrmStringType);
|
||
#ifndef USE_X_TOOLKIT
|
||
/* pmr@osf.org says this shouldn't be done if USE_X_TOOLKIT.
|
||
I suspect it's because the toolkit version does this elsewhere. */
|
||
XrmInitialize ();
|
||
#endif
|
||
rdb = XrmGetStringDatabase ("");
|
||
|
||
/* Add some font defaults. If the font `helv' doesn't exist, widgets
|
||
will use some other default font. */
|
||
#ifdef USE_MOTIF
|
||
|
||
sprintf (line, "%s.pane.background: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fontList: %s", myclass, helv);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*menu*background: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*menubar*background: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*verticalScrollBar.background: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*verticalScrollBar.troughColor: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s.dialog*.background: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb.Text.background: white", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb.FilterText.background: white", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb*DirList.background: white", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb*ItemsList.background: white", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb*background: grey75", myclass);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb.Text.fontList: %s", myclass, courier);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb.FilterText.fontList: %s", myclass, courier);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb*ItemsList.fontList: %s", myclass, courier);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb*DirList.fontList: %s", myclass, courier);
|
||
XrmPutLineResource (&rdb, line);
|
||
|
||
/* Set double click time of list boxes in the file selection
|
||
dialog from `double-click-time'. */
|
||
if (INTEGERP (Vdouble_click_time) && XINT (Vdouble_click_time) > 0)
|
||
{
|
||
sprintf (line, "%s*fsb*DirList.doubleClickInterval: %"pI"d",
|
||
myclass, XFASTINT (Vdouble_click_time));
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "%s*fsb*ItemsList.doubleClickInterval: %"pI"d",
|
||
myclass, XFASTINT (Vdouble_click_time));
|
||
XrmPutLineResource (&rdb, line);
|
||
}
|
||
|
||
#else /* not USE_MOTIF */
|
||
|
||
sprintf (line, "Emacs.dialog*.background: grey75");
|
||
XrmPutLineResource (&rdb, line);
|
||
#if !defined (HAVE_XFT) || !defined (USE_LUCID)
|
||
sprintf (line, "Emacs.dialog*.font: %s", helv);
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "*XlwMenu*font: %s", helv);
|
||
XrmPutLineResource (&rdb, line);
|
||
#endif
|
||
sprintf (line, "*XlwMenu*background: grey75");
|
||
XrmPutLineResource (&rdb, line);
|
||
sprintf (line, "Emacs*verticalScrollBar.background: grey75");
|
||
XrmPutLineResource (&rdb, line);
|
||
|
||
#endif /* not USE_MOTIF */
|
||
|
||
user_database = get_user_db (display);
|
||
|
||
/* Figure out what the "customization string" is, so we can use it
|
||
to decode paths. */
|
||
xfree (x_customization_string);
|
||
x_customization_string
|
||
= x_get_customization_string (user_database, myname, myclass);
|
||
|
||
/* Get application system defaults */
|
||
db = get_system_app (myclass);
|
||
if (db != NULL)
|
||
XrmMergeDatabases (db, &rdb);
|
||
|
||
/* Get Fallback resources */
|
||
db = get_fallback (display);
|
||
if (db != NULL)
|
||
XrmMergeDatabases (db, &rdb);
|
||
|
||
/* Get application user defaults */
|
||
db = get_user_app (myclass);
|
||
if (db != NULL)
|
||
XrmMergeDatabases (db, &rdb);
|
||
|
||
/* get User defaults */
|
||
if (user_database != NULL)
|
||
XrmMergeDatabases (user_database, &rdb);
|
||
|
||
/* Get Environment defaults. */
|
||
db = get_environ_db ();
|
||
if (db != NULL)
|
||
XrmMergeDatabases (db, &rdb);
|
||
|
||
/* Last, merge in any specification from the command line. */
|
||
if (xrm_string != NULL)
|
||
{
|
||
db = XrmGetStringDatabase (xrm_string);
|
||
if (db != NULL)
|
||
XrmMergeDatabases (db, &rdb);
|
||
}
|
||
|
||
return rdb;
|
||
}
|
||
|
||
|
||
/* Retrieve the value of the resource specified by NAME with class CLASS
|
||
and of type TYPE from database RDB. The value is returned in RET_VALUE. */
|
||
|
||
static int
|
||
x_get_resource (XrmDatabase rdb, const char *name, const char *class,
|
||
XrmRepresentation expected_type, XrmValue *ret_value)
|
||
{
|
||
XrmValue value;
|
||
XrmName namelist[100];
|
||
XrmClass classlist[100];
|
||
XrmRepresentation type;
|
||
|
||
XrmStringToNameList (name, namelist);
|
||
XrmStringToClassList (class, classlist);
|
||
|
||
if (XrmQGetResource (rdb, namelist, classlist, &type, &value) == True
|
||
&& (type == expected_type))
|
||
{
|
||
if (type == x_rm_string)
|
||
ret_value->addr = (char *) value.addr;
|
||
else
|
||
memcpy (ret_value->addr, value.addr, ret_value->size);
|
||
|
||
return value.size;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Retrieve the string resource specified by NAME with CLASS from
|
||
database RDB. */
|
||
|
||
char *
|
||
x_get_string_resource (XrmDatabase rdb, const char *name, const char *class)
|
||
{
|
||
XrmValue value;
|
||
|
||
if (inhibit_x_resources)
|
||
/* --quick was passed, so this is a no-op. */
|
||
return NULL;
|
||
|
||
if (x_get_resource (rdb, name, class, x_rm_string, &value))
|
||
return (char *) value.addr;
|
||
|
||
return (char *) 0;
|
||
}
|
||
|
||
/* Stand-alone test facilities. */
|
||
|
||
#ifdef TESTRM
|
||
|
||
typedef char **List;
|
||
#define arg_listify(len, list) (list)
|
||
#define car(list) (*(list))
|
||
#define cdr(list) (list + 1)
|
||
#define NIL(list) (! *(list))
|
||
#define free_arglist(list)
|
||
|
||
static List
|
||
member (char *elt, List list)
|
||
{
|
||
List p;
|
||
|
||
for (p = list; ! NIL (p); p = cdr (p))
|
||
if (! strcmp (elt, car (p)))
|
||
return p;
|
||
|
||
return p;
|
||
}
|
||
|
||
static void
|
||
fatal (char *msg, char *prog)
|
||
{
|
||
if (errno)
|
||
perror (prog);
|
||
|
||
(void) fprintf (stderr, msg, prog);
|
||
exit (1);
|
||
}
|
||
|
||
int
|
||
main (int argc, char **argv)
|
||
{
|
||
Display *display;
|
||
char *displayname, *resource_string, *class, *name;
|
||
XrmDatabase xdb;
|
||
List arg_list, lp;
|
||
|
||
arg_list = arg_listify (argc, argv);
|
||
|
||
lp = member ("-d", arg_list);
|
||
if (!NIL (lp))
|
||
displayname = car (cdr (lp));
|
||
else
|
||
displayname = "localhost:0.0";
|
||
|
||
lp = member ("-xrm", arg_list);
|
||
if (! NIL (lp))
|
||
resource_string = car (cdr (lp));
|
||
else
|
||
resource_string = (char *) 0;
|
||
|
||
lp = member ("-c", arg_list);
|
||
if (! NIL (lp))
|
||
class = car (cdr (lp));
|
||
else
|
||
class = "Emacs";
|
||
|
||
lp = member ("-n", arg_list);
|
||
if (! NIL (lp))
|
||
name = car (cdr (lp));
|
||
else
|
||
name = "emacs";
|
||
|
||
free_arglist (arg_list);
|
||
|
||
if (!(display = XOpenDisplay (displayname)))
|
||
fatal ("Can't open display '%s'\n", XDisplayName (displayname));
|
||
|
||
xdb = x_load_resources (display, resource_string, name, class);
|
||
|
||
/* In a real program, you'd want to also do this: */
|
||
display->db = xdb;
|
||
|
||
while (1)
|
||
{
|
||
char query_name[90];
|
||
char query_class[90];
|
||
|
||
printf ("Name: ");
|
||
gets (query_name);
|
||
|
||
if (strlen (query_name))
|
||
{
|
||
char *value;
|
||
|
||
printf ("Class: ");
|
||
gets (query_class);
|
||
|
||
value = x_get_string_resource (xdb, query_name, query_class);
|
||
|
||
if (value != NULL)
|
||
printf ("\t%s(%s): %s\n\n", query_name, query_class, value);
|
||
else
|
||
printf ("\tNo Value.\n\n");
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
printf ("\tExit.\n\n");
|
||
|
||
XCloseDisplay (display);
|
||
|
||
return 0;
|
||
}
|
||
#endif /* TESTRM */
|