/*--------------------------------*-C-*---------------------------------*
 * File:	xdefaults.c
 *
 * obtain resources from ~/.Xdefaults or ~/.Xresources using either
 * REAL_RESOURCES (XGetDefault) or for memory savings FAKE_RESOURCES.
 *
 * This module is all new by Rob Nation
 *
 * You can do what you like with this source code provided you don't make
 * money from it and you include an unaltered copy of this message
 * (including the copyright).  As usual, the author accepts no
 * responsibility for anything, nor does he guarantee anything whatsoever.
 * ---------------------------------------------------------------------*
 * Extensive modifications by mj olesen <olesen@me.QueensU.CA>
 * No additional restrictions.
 *----------------------------------------------------------------------*/
#include "rxvt.h"
#include <ctype.h>
#include <X11/Xlib.h>
#include "debug.h"
#include "grkelot.h"
#include "xsetup.h"
#include "xdefaults.h"

/* #define DEBUG_RESOURCES */
/*----------------------------------------------------------------------*
 * extern functions referenced
 */
/*----------------------------------------------------------------------*
 * extern variables referenced
 */
/*----------------------------------------------------------------------*
 * extern variables declared here
 */
const char *rs_title = NULL;	/* title name for window */
const char *rs_iconName = NULL;	/* icon name for window */
const char *rs_geometry = NULL;	/* window geometry */
const char *rs_saveLines = NULL;	/* scrollback buffer [lines] */
#ifdef KEYSYM_RESOURCE
const unsigned char * KeySym_map [256];	/* probably mostly empty */
#endif

/*----------------------------------------------------------------------*
 * local variables
 */
/*----------------------------------------------------------------------*
 * local functions referenced
 */

/*----------------------------------------------------------------------*
 * local variables
 */
static const char *rs_loginShell = NULL;
static const char *rs_utmpInhibit = NULL;
static const char *rs_scrollBar = NULL;
#ifndef NO_RESOURCES
#ifdef PRINTPIPE
static const char *rs_printscreen_key = NULL;
#endif
static const char *rs_bigfont_key = NULL;
static const char *rs_smallfont_key = NULL;
static const char *rs_pageup_key = NULL;
static const char *rs_pagedown_key = NULL;
#endif

#ifdef MAPALERT_OPTION
static const char *rs_mapAlert = NULL;
#endif
static const char *rs_visualBell = NULL;
static const char *rs_reverseVideo = NULL;
#ifdef META8_OPTION
static const char *rs_meta8 = NULL;
#endif
#ifdef KANJI
static const char *rs_kanji_encoding = NULL;
#endif
#ifdef GREEK_SUPPORT
static const char *rs_greek_keyboard = NULL;
#endif

/* build a monolithic structure.
 * `string' options MUST have a usage argument
 * `switch' and `boolean' options have no argument
 *
 * if there is no description, then it won't appear in usage()
 */
static struct {
   unsigned long flag;
   const char * * dp;		/* data pointer */
   const char * const kw;	/* keyword */
   const char * const opt;	/* option */
   const char * const arg;	/* argument */
   const char * const desc;	/* description */
#define STRG(dp,kw,opt,arg,desc)   { 0, dp, kw, opt, arg, desc }
#define SWCH(dp,kw,opt,flag,desc)  { (flag), dp, kw, opt, NULL, desc }
#define BOOL(dp,kw,opt,flag,desc)  { (Opt_Boolean|flag), dp, kw, opt, NULL, desc }
#define optList_strlen(i) (optList[i].flag ? 0 : (optList[i].arg ? strlen (optList[i].arg) : 1))
#define optList_isBool(i) (optList[i].flag&Opt_Boolean)
#define optList_size()    (sizeof(optList)/sizeof(optList[0]))
} optList[] = {
   STRG( NULL, NULL, "help", NULL, "print out this message" ),
     STRG(
	  &display_name,
	  NULL,
	  "display",
	  "displayname", "X server to contact" ),
     STRG( &display_name, NULL, "d", NULL, NULL ),	/* short form */
     STRG(
	  &rs_geometry,
	  "geometry",
	  "geometry", "geom",
	  "size (in characters) and position" ),
     STRG( &rs_geometry, NULL, "g", NULL, NULL ), 	/* short form */
     BOOL(
	  &rs_reverseVideo,
	  "reverseVideo",
	  "rv",
	  Opt_reverseVideo,
	  "reverse video" ),
     STRG(
	  &rs_color [COLORBG],
	  "background",
	  "bg", "color",
	  "background color" ),
     STRG(
	  &rs_color [COLORFG],
	  "foreground",
	  "fg", "color",
	  "foreground color" ),
     STRG( NULL, NULL, "color<n>", "color", "color <n>; <n> = 0-7, 10-17" ),
     STRG( &rs_color [COLORMIN+0], "color0", "color0", NULL, NULL ),
     STRG( &rs_color [COLORMIN+1], "color1", "color1", NULL, NULL ),
     STRG( &rs_color [COLORMIN+2], "color2", "color2", NULL, NULL ),
     STRG( &rs_color [COLORMIN+3], "color3", "color3", NULL, NULL ),
     STRG( &rs_color [COLORMIN+4], "color4", "color4", NULL, NULL ),
     STRG( &rs_color [COLORMIN+5], "color5", "color5", NULL, NULL ),
     STRG( &rs_color [COLORMIN+6], "color6", "color6", NULL, NULL ),
     STRG( &rs_color [COLORMIN+7], "color7", "color7", NULL, NULL ),
#ifndef NO_BOLDCOLOR
     STRG( &rs_color [BOLDMIN+0],  "color10", "color10", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+1],  "color11", "color11", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+2],  "color12", "color12", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+3],  "color13", "color13", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+4],  "color14", "color14", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+5],  "color15", "color15", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+6],  "color16", "color16", NULL, NULL ),
     STRG( &rs_color [BOLDMIN+7],  "color17", "color17", NULL, NULL ),
#endif	/* NO_BOLDCOLOR */

     STRG( &rs_color [SCROLLCOLOR],       "scrollColor", NULL, NULL, NULL ),
     STRG( &rs_color [TOPSHADOWCOLOR],    "topShadowColor", NULL, NULL, NULL ),
     STRG( &rs_color [BOTTOMSHADOWCOLOR], "bottomShadowColor", NULL, NULL, NULL ),

     STRG(
	  &rs_font [0],
	  "font",
	  "fn", "fontname",
	  "normal font" ),
     STRG( NULL, NULL, "font<n>", "fontname", "alternative font <n>" ),
     STRG( &rs_font [1], "font1", "font1", NULL, NULL),
     STRG( &rs_font [2], "font2", "font2", NULL, NULL),
     STRG( &rs_font [3], "font3", "font3", NULL, NULL),
     STRG( &rs_font [4], "font4", "font4", NULL, NULL),
#ifdef KANJI
     STRG(
	  &rs_kfont [0],
	  "kfont",
	  "fk", "fontname",
	  "kanji font" ),
     STRG( NULL, NULL, "kfont<n>", "fontname", "kanji alternative font <n>" ),
     STRG( &rs_kfont [1], "kfont1", "kfont1", NULL, NULL),
     STRG( &rs_kfont [2], "kfont2", "kfont2", NULL, NULL),
     STRG( &rs_kfont [3], "kfont3", "kfont3", NULL, NULL),
     STRG( &rs_kfont [4], "kfont4", "kfont4", NULL, NULL),
     STRG(
	  &rs_kanji_encoding,
	  "kanji_encoding",
	  "km",	  "mode",
	  "kanji encoding; mode = eucj | sjis" ),
#endif	/* KANJI */
#ifdef GREEK_SUPPORT
     STRG(
	  &rs_greek_keyboard,
	  "greek_keyboard",
	  "grk", "mode",
	  "greek keyboard mapping; mode = iso | ibm" ),
#endif
     SWCH( NULL, NULL, "ic", Opt_iconic, NULL ),	/* short form */
     SWCH( NULL, NULL, "iconic", Opt_iconic, "start iconic" ),
     STRG(
	  &rs_name, NULL,
	  "name", "string",
	  "client instance, icon, and title strings" ),
     STRG(
	  &rs_title,
	  "title",
	  "title", "string",
	  "title name for window" ),
     STRG( &rs_title, NULL, "T", NULL, NULL ),  /* short form */
     STRG(
	  &rs_iconName,
	  "iconName",
	  "n", "string",
	  "icon name for window" ),
#ifndef NO_CURSORCOLOR
     STRG(
	  &rs_color [CURSORCOLOR],
	  "cursorColor",
	  "cr", "color",
	  "cursor color" ),
# ifndef NO_CURSORCOLOR2
     STRG(
	  &rs_color [CURSORCOLOR2],
	  "cursorColor2",
	  "cr2", "color",
	  "cursor text color" ),
# endif	/* NO_CURSORCOLOR2 */
#endif	/* NO_CURSORCOLOR */
     BOOL(
	  &rs_loginShell,
	  "loginShell",
	  "ls",
	   Opt_loginShell,
	  "login shell" ),
#ifdef MAPALERT_OPTION
     BOOL(
	  &rs_mapAlert,
	  "mapAlert",
	  "ma",
	  Opt_mapAlert,
	  "deiconify (map) on audio alert" ),
#endif
#ifdef META8_OPTION
     BOOL(
	  &rs_meta8,
	  "meta8",
	  "meta8",
	  Opt_meta8,
	  "Meta key set 8th bit" ),
#endif
     BOOL(
	  &rs_utmpInhibit,
	  "utmpInhibit",
	  "ut",
	  Opt_utmpInhibit,
	  "utmp inhibit" ),
     BOOL(
	  &rs_visualBell,
	  "visualBell",
	  "vb",
	  Opt_visualBell,
	  "visual bell" ),
     BOOL(
	  &rs_scrollBar,
	  "scrollBar",
	  "sb",
	  Opt_scrollBar,
	  "scrollbar" ),
     BOOL(&rs_scrollBar,
	  NULL,
	  "sbr",
	  Opt_sbRight,
	  "right-hand scrollbar" ),
     BOOL(&rs_scrollBar,
	  NULL,
	  "arrows",
	  Opt_sbArrows,
	  "scrollbar arrows" ),
     STRG(
	  &rs_saveLines,
	  "saveLines",
	  "sl", "number",
	  "number of scrolled lines to save" ),

#ifdef PRINTPIPE
     STRG(
	  &rs_print_pipe,
	  "print-pipe",
	  "print-pipe", "name",
	  "specify pipe for vt100 printer" ),
#endif
#ifndef NO_RESOURCES
#ifdef PRINTPIPE
     STRG( &rs_printscreen_key,	"printscreen_key",	NULL, NULL, NULL ),
#endif
     STRG( &rs_bigfont_key,	"bigfont_key",		NULL, NULL, NULL ),
     STRG( &rs_smallfont_key,	"smallfont_key",	NULL, NULL, NULL ),
     STRG( &rs_pageup_key,	"pageup_key",		NULL, NULL, NULL ),
     STRG( &rs_pagedown_key,	"pagedown_key",		NULL, NULL, NULL ),
#ifndef NO_MULTIPLE_CLICK
     STRG( &rs_cutchars,	"cutchars",		NULL, NULL, NULL ),
#endif
#endif
     SWCH(NULL, NULL, "C", Opt_console, "intercept console messages" ),
     STRG(NULL, NULL, "e", "command arg ...", "command to execute" )
};

#undef STRG
#undef SWCH
#undef BOOL

/*----------------------------------------------------------------------*/
static void
usage (int brief)
{
   int i;

   fprintf (stderr, "\nUsage v"VERSION":\n  " APL_NAME);
   if (brief)
     {
	int col = 3 + strlen (APL_NAME);
	for (i = 0; i < optList_size(); i++)
	  {
	     if (optList[i].desc != NULL)
	       {
		  int len = 2;
		  if (!optList_isBool (i))
		    {
		       len = optList_strlen (i);
		       if (len > 0)
			 len++;		/* account for space */
		    }
		  len += 4 + strlen (optList[i].opt);

		  col += len;
		  if (col > 79)		/* assume regular width */
		    {
		       fprintf (stderr, "\n   ");	/* 3 spaces */
		       col = 3 + len;
		    }
		  fprintf (stderr, " [-");
		  if (optList_isBool (i))
		    fprintf (stderr, "/+");
		  fprintf (stderr, "%s", optList[i].opt);
		  if (optList_strlen (i))
		    fprintf (stderr, " %s]", optList[i].arg);
		  else
		    fprintf (stderr, "]");
	       }
	  }
	fprintf (stderr, "\n\n  use -help for a full description\n\n");
     }
   else
     {
	fprintf (stderr, " [options] [-e command args]\n\n"
		 "where options include:\n");

	for (i = 0; i < optList_size(); i++)
	  {
	     if (optList[i].desc != NULL)
	       {
		  int len = 27;

		  len -= strlen (optList[i].opt);
		  if (optList_isBool (i))
		    len -= 2;
		  fprintf (stderr, "    %s%s %-*s%s%s\n",
			   (optList_isBool (i) ? "-/+" : "-"),
			   optList[i].opt,
			   len,
			   (optList[i].arg ? optList[i].arg : ""),
			   (optList_isBool (i) ? "turn on/off " : ""),
			   optList[i].desc);

	       }
	  }
     }
   exit (EXIT_FAILURE);
}

#ifdef GREEK_SUPPORT
static void
set_greek_keyboard (const char *str)
{
   if (str == NULL || !*str)
     return;

   if (!strcmp (str, "iso"))
     greek_setmode (GREEK_ELOT928);		/* former -grk9 */
   else if (!strcmp (str, "ibm"))		/* former -grk4 */
     greek_setmode (GREEK_IBM437);
}
#endif

/*
 * get all command-line option (string and boolean)
 * before getting resources
 */
void
get_options (int argc, char * argv[])
{
   int i, bad_option = 0;
   static const char * const On = "ON", * Off = "OFF";

#ifdef XTERM_SCROLLBAR
   Options &= ~(Opt_sbArrows | Opt_sbRight);
#endif

   for (i = 1; i < argc; i++)
     {
	int entry;
	const char * flag;
	char * opt = argv [i];

#ifdef DEBUG_RESOURCES
	fprintf (stderr, "argv[%d] = %s: ", i, argv[i]);
#endif
	if (*opt == '-')
	  {
	     opt++;
	     flag = On;
	     if (*opt == '-')	/* long option */
	       opt++;
	  }
	else if (*opt == '+')
	  {
	     opt++;
	     flag = Off;
	     if (*opt == '+')	/* long option */
	       opt++;
	  }
	else
	  {
	     bad_option = 1;
	     print_error ("bad option \"%s\"", opt);
	     continue;
	  }

        if (!strcmp(opt, "help")) usage (0);

        for (entry = 0; entry < optList_size(); entry++)
	  if (
	      ((optList[entry].opt != NULL) &&
	       !strcmp (opt, optList[entry].opt)) ||
	      ((optList[entry].kw != NULL) &&
	       !strcmp (opt, optList[entry].kw))
	      )
            break;

	if (entry < optList_size())
	  {
	     if (optList_strlen (entry))	/* string value */
	       {
		  char *val = argv [i+1];
		  i++;

#ifdef DEBUG_RESOURCES
		  fprintf (stderr, "string (%s,%s) = ",
			   optList[entry].opt, optList[entry].kw);
#endif
		  if (flag == On && val && optList [entry].dp) {
#ifdef DEBUG_RESOURCES
		     fprintf (stderr, "\"%s\"\n", val);
#endif
		     *(optList [entry].dp) = val;

		     /* some special cases */
		     if (!strcmp(opt, "name"))	{
			if (rs_iconName == NULL)	rs_iconName = val;
			if (rs_title == NULL)	rs_title = val;
		     }
		     else if (!strcmp(opt, "n"))	{
			if (rs_title == NULL)	rs_title = val;
		     }
		  }
#ifdef DEBUG_RESOURCES
		  else fprintf (stderr, "???\n");
#endif
	       }
	     else			/* boolean value */
	       {
#ifdef DEBUG_RESOURCES
		  fprintf (stderr, "boolean (%s,%s) = %s\n",
			   optList[entry].opt, optList[entry].kw, flag);
#endif
		  if (flag == On)
		    Options |= (optList[entry].flag);
		  else
		    Options &= ~(optList[entry].flag);

		  if (optList [entry].dp)
		    *(optList [entry].dp) = flag;
	       }
	  }
	else
	  {
	     /* various old-style options, just ignore
	      * Obsolete since about Jan 96,
	      * so they can probably eventually be removed
	      */
	     const char * msg = "bad";
	     if (!strcmp(opt, "7") || !strcmp(opt, "8") ||
		 !strcmp(opt, "fat") || !strcmp(opt, "thin") ||
		 !strncmp(opt, "meta", 4)
#ifdef GREEK_SUPPORT
		 /* these obsolete 12 May 1996 (v2.17) */
		 || !strcmp (opt, "grk4") || !strcmp (opt, "grk9")
#endif
		 )
	       msg = "obsolete";
	     else
	       bad_option = 1;
	     print_error ("%s option \"%c%s\"",
			  msg, (flag == On ? '-' : '+'), opt);
	  }
     }

#ifdef KANJI
   set_kanji_encoding (rs_kanji_encoding);
#endif
#ifdef GREEK_SUPPORT
   set_greek_keyboard (rs_greek_keyboard);
#endif

#ifndef NO_SCROLLBAR
   /* rationalize scrollbar selections */
   if (Options & Opt_sbRight) Options |= (Opt_sbSlider);
# ifndef XTERM_SCROLLBAR
   if (Options & Opt_sbArrows) Options |= (Opt_sbSlider);
# endif	/* XTERM_SCROLLBAR */
#endif	/* NO_SCROLLBAR */

   if (bad_option)
     usage (1);
}

#ifndef NO_RESOURCES
/*
 * a replacement for strcasecmp() to avoid linking an entire library
 */
static int
my_strcasecmp (const char *s1, const char *s2)
{
   for (/*nil*/; (*s1 && *s2); s1++, s2++)
     {
	register int c1, c2;
	c1 = toupper (*s1);
	c2 = toupper (*s2);
	if (c1 != c2)
	  return (c1 - c2);
     }
   return (int) (*s1 - *s2);
}

/*----------------------------------------------------------------------*/
/*
 * interprete backslash-escaped strings
 */
#ifdef KEYSYM_RESOURCE
static char *
escaped_string (char * str)
{
   register char *p, ch;
   int i, len;

   if (str == NULL || (len = strlen (str)) == 0)
     return NULL;

   /* use 'i' to increment through destination and p through source */
   for (p = str, i = 0; i < len; i++)
     {
	ch = *p++;
	if (ch == '\\')
	  {
	     ch = *p;
	     if (isdigit (ch))	/* octal */
	       {
		  int j, num = 0;
		  for (j = 0; j < 3 && (ch >= '0' && ch <= '7'); j++)
		    {
		       num = num * 010 + (ch - '0');
		       p++; len--;
		       ch = *p;
		    }
		  ch = (unsigned char) num;
	       }
	     else
	       {
		  p++; len--;	/* one fewer char remains */
		  switch (ch) {
		   case 'b': ch = '\b';	break;	/* backspace */
		   case 'E':
		   case 'e': ch =  033;	break;	/* escape */
		   case 'n': ch = '\n';	break;	/* new-line */
		   case 'r': ch = '\r';	break;	/* carriage-return */
		   case 't': ch = '\t';	break;	/* tab */
		  }
	       }
	  }
	str [i] = ch;
     }
   str [len] = '\0';

   return strlen (str) ? str : NULL;
}
#endif	/* KEYSYM_RESOURCE */

/*
 * remove leading/trailing space and strip-off leading/trailing quotes
 */
#ifdef FAKE_RESOURCES
static char *
trim_string (char * str)
{
   int n;

   if (str == NULL || *str == '\0')
     return NULL;

   while (*str && isspace (*str)) str++;	/* skip leading spaces */

   /* trim trailing space */
   n = strlen (str) - 1;
   while (n > 0 && isspace (str [n])) n--;
   str [n+1] = '\0';

   if (str [0] == '"')		/* strip leading/trailing quotes */
     {
	str++; n--;
	if (str [n] == '"')
	  str [n--] = '\0';
     }
   return n >= 0 ? str : NULL;
}

/*
 * the matching algorithm used for FAKE_RESOURCES
 */
static int
get_xdefaults (FILE * stream, const char * name)
{
   int len, found = 0;
   char *text, buffer [256];

   if (stream == NULL) return 0;
   len = strlen (name);
   while ((text = fgets (buffer, sizeof(buffer), stream)) != NULL)
     {
	int entry, n;

	while (*text && isspace (*text)) text++;    /* leading whitespace */

	if (((text [len] != '*' && text [len] != '.')) ||
	    (len && strncmp (text, name, len)))
	  continue;
	text += (len + 1);	/* skip `name*' or `name.' */

	/*
	 * look for something like this (XK_Delete)
	 * rxvt*keysym.0xFFFF: "\177"
	 */
# ifdef KEYSYM_RESOURCE
	n = strlen ("keysym");
	if ((text [n] == '.') && !strncmp (text, "keysym", n))
	  {
	     int sym;
	     text += (n + 1);	/* skip `keysym.' */

	     /* some scanf() have trouble with a 0x prefix */
	     if (text [0] == '0' && toupper (text [1]) == 'X')
	       text += 2;
	     if (sscanf (text, "%x:", &sym) == 1)
	       {
		  if (sym >= 0xFF00)	/* only do extended keys */
		    sym -= 0xFF00;
		  if (sym < 0 || sym > 0xFF) continue;

		  /* cue to ':' , it is there if sscanf() worked */
		  text = strchr (text, ':');

		  /* skip `:' */
		  text = escaped_string (trim_string (text + 1));
		  n = (text == NULL) ? 0 : strlen (text);
		  if (n && KeySym_map [sym] == NULL)
		    {
		       /* only if not previously set */
		       char *p = MALLOC ((n+1) * sizeof(char), "KeySym_map");
		       strcpy (p, text);
		       KeySym_map [sym] = p;
		    }
	       }
	  }
	else
# undef KEYSYM_kw
# endif	/* KEYSYM_RESOURCE */
	  for (entry = 0; entry < optList_size(); entry++)
	  {
	     const char * const kw = optList[entry].kw;
	     if (kw == NULL)
	       continue;
	     n = strlen (kw);
	     if ((text [n] == ':') && !strncmp (text, kw, n))
	       {
		  /* only if not previously set */
		  if (*(optList[entry].dp) == NULL)
		    {
		       /* skip `keyword:' */
		       text = trim_string (text + n + 1);
		       if ((n = (text == NULL) ? 0 : strlen (text)) != 0)
			 {
			    char *p = MALLOC ((n+1) * sizeof(char), kw);
			    strcpy (p, text);

			    *(optList[entry].dp) = p;

			    if (optList_isBool (entry))
			      {
				 if (!my_strcasecmp (p, "TRUE"))
				   Options |= (optList[entry].flag);
				 else
				   Options &= ~(optList[entry].flag);
			      }
			    found++;
			 }
		    }
		  break;
	       }
	  }
     }
   rewind (stream);
   return found;
}
#endif	/* FAKE_RESOURCES */

/*
 * read the resources files
 * using XGetDefault() or the hand-rolled replacement
 */
void
extract_resources (Display *dsply, const char *name)
{
   int found = 0;
#if defined (REAL_RESOURCES)
   /*
    * get resources using the X library function
    */
   int entry;
   for (entry = 0; entry < optList_size(); entry++)
     {
	char * p;
	const char * kw =  optList[entry].kw;
	if (kw == NULL || *(optList[entry].dp) != NULL)
	  continue;	/* previously set */
	if ((p = XGetDefault (dsply, name, kw)) != NULL ||
	    (p = XGetDefault (dsply, APL_SUBCLASS, kw)) != NULL ||
	    (p = XGetDefault (dsply, APL_CLASS, kw)) != NULL)
	  {
	     *optList[entry].dp = p;

	     if (optList_isBool (entry))
	       {
		  if (!my_strcasecmp (p, "TRUE"))
		    Options |= (optList[entry].flag);
		  else
		    Options &= ~(optList[entry].flag);
	       }
	     found++;
	  }
     }

#elif defined (FAKE_RESOURCES)
   /* get resources the hard way, but save lots of memory */
   const char * fname[] = { ".Xdefaults", ".Xresources" };
   char *home;
   FILE *fd = NULL;

   /*
    * The normal order to match resources is the following:
    * @ global resources (partial match, ~/.Xdefaults)
    * @ application file resources (XAPPLOADDIR/RXvt)
    * @ class resources (~/.Xdefaults)
    * @ private resources (~/.Xdefaults)
    *
    * However, for FAKE_RESOURCES, the matching algorithm checks if a
    * resource string value has already been allocated and won't overwrite
    * it with (in this case) a less specific resource value.
    *
    * This avoids multiple allocation.  Also, when we've called this
    * routine command-line string options have already been applied so we
    * needn't to allocate for those resources.
    *
    * So, search in resources from most to least specific.
    *
    * Also, use a special sub-class so that we can use either or both of
    * "XTerm" and "RXvt" as class names.
    */
   if ((home = getenv ("HOME")) != NULL)
     {
	int i, len = strlen (home) + 2;
	char * f = NULL;
	for (i = 0; i < (sizeof(fname)/sizeof(fname[0])); i++)
	  {
	     f = REALLOC (f, (len + strlen (fname[i])) * sizeof(char),
			  fname [i]);
	     sprintf (f, "%s/%s", home, fname [i]);

	     if ((fd = fopen (f, "r")) != NULL)
	       break;
	  }
	FREE (f, "get_Xdefaults", "fname");
     }

   found += get_xdefaults (fd, name);
   found += get_xdefaults (fd, APL_SUBCLASS);

#ifdef XAPPLOADDIR
     {
	FILE *ad = fopen (XAPPLOADDIR "/" APL_SUBCLASS, "r");
	if (ad != NULL)
	  {
	     found += get_xdefaults (ad, "");
	     fclose (ad);
	  }
     }
#endif	/* XAPPLOADDIR */

   found += get_xdefaults (fd, APL_CLASS);
   found += get_xdefaults (fd, "");	/* partial match */
   if (fd != NULL)
     fclose (fd);
#endif	/* FAKE_RESOURCES */

   if (!found)
     return;

#ifdef KANJI
   set_kanji_encoding (rs_kanji_encoding);
#endif

#define to_keysym(pks,str) do { KeySym sym;\
if (str && ((sym = XStringToKeysym(str)) != 0)) *pks = sym; } while (0)

#ifdef PRINTPIPE
   to_keysym ( &ks_printscreen,	rs_printscreen_key );
#endif
   to_keysym ( &ks_pageup,	rs_pageup_key );
   to_keysym ( &ks_pagedown,	rs_pagedown_key );
   to_keysym ( &ks_bigfont,	rs_bigfont_key );
   to_keysym ( &ks_smallfont,	rs_smallfont_key );
#undef to_keysym
}
#else	/* NO_RESOURCES */
/* dummy function */
void extract_resources (Display *dsply, const char *name){}
#endif	/* NO_RESOURCES */
/*----------------------- end-of-file (C source) -----------------------*/
