/*
 * resource.c
 * Manage resources for gmemusage
 *
 * Copyright (C) 1997, 1998 by Raju Mathur.
 * Lots of code and ideas shamelessly stolen from xlockmore-3.0/resource.c
 *
 * See file COPYING (included in this distribution) for copyright information.
 */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xresource.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>

#include "common.h"
#include "defaults.h"
/*
 * Built-in defaults
 */
#define DEF_FONT	"fixed"
#define DEF_GEOMETRY	"320x400"
#define DEF_NCOLORS	"3"
#ifdef PASTEL_COLORS
#define DEF_COLOR1	"maroon"
#define DEF_COLOR2	"OliveDrab"
#define DEF_COLOR3	"SlateBlue"
#else
#define DEF_COLOR1	"red"
#define DEF_COLOR2	"green"
#define DEF_COLOR3	"blue"
#endif
#define DEF_COLOR4	""
#define DEF_COLOR5	""
#define DEF_COLOR6	""
#define DEF_COLOR7	""
#define DEF_COLOR8	""
#define DEF_COLOR9	""
#define DEF_FOREGROUND	"black"
#define DEF_BACKGROUND	"grey4"
#define DEF_UPDATE	"5"
#define DEF_THRESHHOLD	"400"
#define DEF_CLASSNAME	"Gmemusage"

static XrmOptionDescRec optionTable [] = 
{
   { "-geometry" , ".geometry" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-ncolors" , ".ncolors" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color1" , ".color1" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color2" , ".color2" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color3" , ".color3" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color4" , ".color4" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color5" , ".color5" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color6" , ".color6" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color7" , ".color7" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color8" , ".color8" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-color9" , ".color9" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-font" , ".font" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-foreground" , ".foreground" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-background" , ".background" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-update" , ".update" , XrmoptionSepArg , (caddr_t) NULL } ,
   { "-threshhold" , ".threshhold" , XrmoptionSepArg , (caddr_t) NULL } ,
} ;
const int
   optionEntries = sizeof ( optionTable ) / sizeof ( optionTable [0] ) ;

static XrmOptionDescRec displayTable [] =
{
   { "-display" , ".display" , XrmoptionSepArg , (caddr_t) NULL }
} ;
const int
   displayEntries = sizeof ( displayTable ) / sizeof ( displayTable [0] ) ;

static XrmOptionDescRec nameTable [] =
{
   { "-name" , ".name" , XrmoptionSepArg , (caddr_t) NULL }
} ;
const int
   nameEntries = sizeof ( nameTable ) / sizeof ( nameTable [0] ) ;
/*
 * Variables holding default values
 */
static char
   *dName ;
char
   *dGeometry ,
   *dDisplay ,
   *dFont ,
   *dForeground ,
   *dBackground ,
   *dColor [MaxColors] ;
int
   dnColors ,
   dUpdate ,
   dThreshhold ;

#define t_String	0
#define t_Int		1

typedef struct {
    caddr_t    *var;
    char       *name;
    char       *class;
    char       *def;
    int         type;
}           argtype;
/*
 * Warning! If you change anything here do also modify the Help function
 * accordingly.
 */
static argtype optionVars[] = {
    { (caddr_t *) &dFont , "font" , "Font" , DEF_FONT , t_String } ,
    { (caddr_t *) &dGeometry , "geometry" , "Geometry" , DEF_GEOMETRY , t_String } ,
    { (caddr_t *) &dnColors , "ncolors" , "NColors" , DEF_NCOLORS , t_Int } ,
    { (caddr_t *) &dColor [0] , "color1" , "Color" , DEF_COLOR1 , t_String } ,
    { (caddr_t *) &dColor [1] , "color2" , "Color" , DEF_COLOR2 , t_String } ,
    { (caddr_t *) &dColor [2] , "color3" , "Color" , DEF_COLOR3 , t_String } ,
    { (caddr_t *) &dColor [3] , "color4" , "Color" , DEF_COLOR4 , t_String } ,
    { (caddr_t *) &dColor [4] , "color5" , "Color" , DEF_COLOR5 , t_String } ,
    { (caddr_t *) &dColor [5] , "color6" , "Color" , DEF_COLOR6 , t_String } ,
    { (caddr_t *) &dColor [6] , "color7" , "Color" , DEF_COLOR7 , t_String } ,
    { (caddr_t *) &dColor [7] , "color8" , "Color" , DEF_COLOR8 , t_String } ,
    { (caddr_t *) &dColor [8] , "color9" , "Color" , DEF_COLOR9 , t_String } ,
    { (caddr_t *) &dForeground , "foreground" , "Foreground" , DEF_FOREGROUND ,
      t_String } ,
    { (caddr_t *) &dBackground , "background" , "Background" , DEF_BACKGROUND ,
      t_String } ,
    { (caddr_t *) &dUpdate , "update" , "Interval" , DEF_UPDATE , t_Int } ,
    { (caddr_t *) &dThreshhold , "threshhold" , "Threshhold" , DEF_THRESHHOLD ,
      t_Int } ,
} ;
const int
   nVars = sizeof ( optionVars ) / sizeof ( optionVars [0] ) ;
/*
 * Print out the help message. This'll need to be kept up-to-date as and when
 * commandline options change.
 */
static void
   Help ( void )
{
   fprintf ( stderr , "gmemusage ver %s by Raju (OldMonk) Mathur\n"
	     "Usage: %s [options]\n"
	     "where options (defaults/resources in brackets) are:\n"
	     "    -name resourcename  Class name to use for resources (%s) (name)\n"
	     "    -display display    X server to contact ($DISPLAY) (display)\n"
	     "    -geometry geometry  Initial window geometry (%s) (geometry)\n"
	     "    -font font          Text font (%s) (font)\n"
	     "    -background color   Color to use for window background (%s) (background)\n"
	     "    -update seconds     Update interval (%s) (update)\n"
	     "    -threshhold kb      Threshhold to merge small processes (%s) (threshhold)\n"
	     "    -ncolors ncolors    How many colors to use (%s) (ncolors)\n"
	     "    -color1 color       color 1 (%s) (color1)\n"
	     "    -color2 color       color 2 (%s) (color2)\n"
	     "    -color3 color       color 3 (%s) (color3)\n"
	     "    -color[4-9] color   colors 4 to 9 (undefined) (color4-color9)\n"
	     "    -help               Print this message\n" ,
	     version , progname , DEF_CLASSNAME , DEF_GEOMETRY , DEF_FONT ,
	     DEF_BACKGROUND , DEF_UPDATE , DEF_THRESHHOLD , DEF_NCOLORS ,
	     DEF_COLOR1 , DEF_COLOR2 , DEF_COLOR3 ) ;
}
/*
 * Begin shameless thefts mentioned earlier. All I did was (1) remove the
 * extra cases for t_Float and t_Bool and (2) hit ESC C-q at the opening
 * brace in Xemacs for this function.
 */
static void
GetResource ( XrmDatabase database , char *parentname , char *parentclass ,
	      char *name , char *class , int valueType , char *def ,
	      caddr_t *valuep )
{
   char       *type;
   XrmValue    value;
   char       *string;
   char        buffer[1024];
   char        fullname[1024];
   char        fullclass[1024];
   int         len;

   (void) sprintf(fullname, "%s.%s", parentname, name);
   (void) sprintf(fullclass, "%s.%s", parentclass, class);
   if (XrmGetResource(database, fullname, fullclass, &type, &value)) {
      string = value.addr;
      len = value.size;
   } else {
      string = def;
      len = strlen(string);
   }
   (void) strncpy(buffer, string, sizeof(buffer));
   buffer[sizeof(buffer) - 1] = '\0';

   switch (valueType) {
    case t_String:
    {
       char       *s = (char *) malloc(len + 1);
       if (s == (char *) NULL)
       {
	  fprintf ( stderr ,"%s: GetResource - couldn't allocate memory" ,
		    progname ) ;
	  perror ( "" ) ;
	  exit ( 1 ) ;
       }
       (void) strncpy(s, string, len);
       s[len] = '\0';
       *((char **) valuep) = s;
    }
    break;

    case t_Int:
     *((int *) valuep) = atoi(buffer);
     break;
   }
}
static XrmDatabase
   optionDB = NULL ,
   serverDB = NULL ,
   mergedDB = NULL ;
/*
 * External interface to resources. Again, shamelessly stolen from
 * xlockmore-3.0 source. I don't want to do what xlockmore does: open the
 * display within the GetResource, so we'll break that function up into
 * two seperate pieces: the first one will get the "name" and "display"
 * resources, return ot the parent which will then open the display
 * and then call the second one which will get the rest of the resources.
 */
void
GetInitialResources ( int *argc , char **argv )
{
   XrmDatabase
      nameDB = NULL ,
      displayDB = NULL ;

   XrmInitialize () ;
/*
 * Get -name if we need to look for resources under another name.
 */
   XrmParseCommand ( &nameDB , nameTable , nameEntries , progname , argc ,
		     argv ) ;
   GetResource ( nameDB , progname , "*" , "name" , "Name" , t_String ,
		 DEF_CLASSNAME , &dName ) ;
   XrmParseCommand ( &displayDB , displayTable , displayEntries , progname ,
		     argc , argv ) ;
   dDisplay = getenv ( "DISPLAY" ) ;
   if ( !dDisplay )
   {
      dDisplay = "" ;
   }
   GetResource ( displayDB , progname , dName , "display" , "Display" ,
		 t_String , dDisplay , &dDisplay ) ;
#if 0
   printf("name %s, display %s\n",dName,dDisplay);
#endif
   XrmMergeDatabases ( nameDB , &mergedDB ) ;
   XrmMergeDatabases ( displayDB , &mergedDB ) ;
   return ;
}
/*
 * Now get the rest of the resources. This function will be called after the
 * display has been opened by the application. I'm not going to look in
 * ~/.Xdefaults or ~/.Xresources (presumably rdb has already set those values
 * in the display RDB) and not in .../app-defaults/... since we are not looking
 * at an app-default file for the app yet.
 *
 * So finally the databases we are left with are:
 *	- Server resources (presumably from rdb(1))
 *	- cmdline resources
 *	- environment (for DISPLAY)
 */
void
   GetResources ( Display *display , int argc , char **argv )
{
   char
      *serverString ;
   register int
      i ;

   serverString = XResourceManagerString ( display ) ;
   if ( serverString )
   {
      serverDB = XrmGetStringDatabase ( serverString ) ;
      (void) XrmMergeDatabases ( serverDB , &mergedDB ) ;
   }
   XrmParseCommand ( &optionDB , optionTable , optionEntries , progname ,
		     &argc , argv ) ;
/*
 * Now there shouldn't be any arguments left at all. If there are any,
 * they're errors and we should flag them.
 */
   if ( argc > 1 )		/* argv[0] still remains, presumably */
   {
      if ( strcmp ( argv [1] , "-help" ) )
      {
	 fprintf ( stderr , "%s: unknown option %s\n" , progname , argv [1] ) ;
      }
      Help () ;
      exit ( 1 ) ;
   }
   XrmMergeDatabases ( optionDB , &mergedDB ) ;
   for ( i = 0 ; i < nVars ; i++ )
   {
      GetResource ( mergedDB , progname , dName , optionVars [i] . name ,
		    optionVars [i] . class , optionVars [i] . type ,
		    optionVars [i] . def , optionVars [i] . var ) ;
   }
   XrmDestroyDatabase ( mergedDB ) ;
   return ;
}
