/*
 *  WindowMaker window manager
 * 
 *  Copyright (c) 1997 Alfredo K. Kojima
 * 
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "wconfig.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
 
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#include "generic/wwmlib.h"

#include "WindowMaker.h"
#include "window.h"
#include "resource.h"
#include "funcs.h"
#include "texture.h"
#include "keybind.h"


/*** Globals ***/
extern WShortKey *wKeyBindings[WKBD_LAST];


#ifdef offsetof
#define wOffset(type, field) ((void *) offsetof(type, field))
#else
#define wOffset(type, field) ((void *)((char *)&((type *)NULL)->field))
#endif 


/* data types */
#define TBOOLEAN	2	/* char */
#define TDISPLAY	4	/* type of geometry display window (char) */
#define TSTRING		5	/* char* */
#define TFOCUS		6	/* focus mode */
#define TPLACEMENT	7	/* window placement mode */
#define TTEXTURE	8	/* texture types */
#define T2COLORS	9	/* gradient colors */
#define TKEYCODE	10	/* kbd shortcut */
#define TCOORDINATE	11
#define TJUSTIFICATION	12	/* text justification */
#define TINTEGER	13
/* for window attribute values */
#define TWSTRING	16
#define TWWATTRIB	17	/* window attribute bit field */


typedef struct WOption {
    char type;			       /* option type */
    char *name;			       /* name of option */
    void *ptr;			       /* pointer to data (or offset) */
} WOption;

typedef struct WSection {
    char *name;
    WOption *options;
} WSection;



/*
 * Decoration setting is done in this order:
 * - see if there's a default in the resource database
 * - if the application is GNUstep set the resources asked by the app
 * - if not, try to guess the settings
 * 
 * For icons: (ascending precedence)
 * - use global default
 * - use application specified icon bitmap
 * - use application specified icon window
 * - use database with window class
 * - use database with window instance
 * - use database with window name
 */

static HashTable *wResDatabase=NULL;

extern WResource wResourceDefault;

extern WPreferences wPreferences;

extern char *PixmapPath;


/* window attribute options */
static WOption WindowOptions[] = {
    {TWSTRING, 	"icon", 		wOffset(WResource, icon_file)},
    {TWSTRING, 	"icontext", 		wOffset(WResource, icon_text)},
    {TWWATTRIB,	"titlebar", 		(void*)WA_TITLEBAR},
    {TWWATTRIB,	"resizable", 		(void*)WA_RESIZABLE},
    {TWWATTRIB,	"closable", 		(void*)WA_CLOSABLE},
    {TWWATTRIB,	"miniaturizable",	(void*)WA_MINIATURIZABLE},
    {TWWATTRIB,	"shadeable",		(void*)WA_SHADEABLE},
    {TWWATTRIB,	"floating",		(void*)WA_FLOATING},
    {TWWATTRIB,	"skipwindowlist",	(void*)WA_SKIP_WINDOW_LIST},
    {TWWATTRIB,	"omnipresent",		(void*)WA_OMNIPRESENT},
    {TWWATTRIB,	"focusable",		(void*)WA_FOCUSABLE},
    {TWWATTRIB,	"controlkeyboard",	(void*)WA_IGNORE_KEYS},
    {TWWATTRIB,	"controlmouse",		(void*)WA_IGNORE_MOUSE},
    {TWWATTRIB, "ignorehideothers",	(void*)WA_IGNORE_HIDE_OTHERS},
    {TWWATTRIB, "noappicon",		(void*)WA_NOT_APPLICATION},
#if 0
    {TWWATTRIB, "startwithdrawn",	(void*)WA_START_WITHDRAWN},
#endif
    {0,		NULL,			NULL},
};
#undef ofs



/* preference options */
static WOption PreferenceOptions[] = {
    {TBOOLEAN,	"dithering", 		&wPreferences.dithering},
    {TINTEGER,	"colormapsize", 	&wPreferences.cmap_size},
    {TSTRING,	"pixmappath",		&PixmapPath},
    {TDISPLAY,	"resizedisplay",	&wPreferences.size_display},
    {TDISPLAY, 	"movedisplay",		&wPreferences.move_display},
    {TBOOLEAN, 	"colormapfollowsmouse",	&wPreferences.colormap_follows_mouse},
    {TFOCUS, 	"focusmode",		&wPreferences.focus_mode},
    {TINTEGER,	"raisedelay",		&wPreferences.raise_delay},
    {TBOOLEAN, 	"alignsubmenus",	&wPreferences.align_menus},
    {TBOOLEAN,  "ontoptransients",	&wPreferences.on_top_transients},
    {TPLACEMENT,"windowplacement",	&wPreferences.window_placement},
    {TBOOLEAN,	"ignorefocusclick",	&wPreferences.ignore_focus_click},
    {TCOORDINATE, "rootmenuposition",	&wPreferences.root_menu_pos},
    {TBOOLEAN, "usesaveunders",		&wPreferences.use_saveunders},
    {TBOOLEAN, "opaquemove",		&wPreferences.opaque_move},
    {TBOOLEAN, "animations",		&wPreferences.animations},
    {TBOOLEAN, "autowrap",		&wPreferences.autowrap},
#ifdef NEWAPPICON
    {TBOOLEAN, "strictns",		&wPreferences.strict_ns},
#endif
    {0,		NULL,			NULL},
};


/* style options */
static WOption StyleOptions[] = {
    {TSTRING, 	"iconbackpixmap",	&wPreferences.icon_back_file},
    {TSTRING, 	"icondotspixmap",	&wPreferences.icon_dots_file},
    {TSTRING, 	"logopixmap",		&wPreferences.logo_file},
    {TJUSTIFICATION, "titletextjustify",  &wPreferences.title_justification},
      
    {TSTRING, 	"windowtitlefont",	&wPreferences.title_font},
    {TSTRING, 	"menutitlefont",	&wPreferences.menu_title_font},
    {TSTRING, 	"menutextfont",		&wPreferences.menu_entry_font},
    {TSTRING, 	"icontitlefont",	&wPreferences.icon_title_font},
    {TSTRING, 	"displayfont",		&wPreferences.info_font},
    {TSTRING, 	"dialogfont",		&wPreferences.panel_title_font},
    {TSTRING, 	"dialogmessagefont",	&wPreferences.panel_text_font},
      
    {TSTRING, 	"ftitlebackcolor",	&wPreferences.ftitle_bcolor},
    {TSTRING, 	"ftitleforecolor",	&wPreferences.ftitle_fcolor},
    {TSTRING, 	"ptitlebackcolor",	&wPreferences.ptitle_bcolor},
    {TSTRING, 	"ptitleforecolor",	&wPreferences.ptitle_fcolor},
    {TSTRING, 	"utitlebackcolor",	&wPreferences.utitle_bcolor},
    {TSTRING, 	"utitleforecolor",	&wPreferences.utitle_fcolor},
    {TSTRING, 	"menutitlebackcolor",	&wPreferences.menu_title_bcolor},
    {TSTRING, 	"menutitleforecolor",	&wPreferences.menu_title_fcolor},
    {TSTRING, 	"menutextdisabledcolor",&wPreferences.menu_text_dcolor},
    {TSTRING, 	"menutextbackcolor",	&wPreferences.menu_text_bcolor},
    {TSTRING, 	"menutextforecolor",	&wPreferences.menu_text_fcolor},

    {TTEXTURE, 	"ftitletexture",	&wPreferences.ftitle_texture},
    {TTEXTURE, 	"ptitletexture",	&wPreferences.ptitle_texture},
    {TTEXTURE, 	"utitletexture",	&wPreferences.utitle_texture},
    {TTEXTURE, 	"menutitletexture",	&wPreferences.menu_title_texture},
    {TTEXTURE, 	"menutexttexture",	&wPreferences.menu_text_texture},

    {T2COLORS,	"ftitlegradient",	&wPreferences.ftitle_gradient},
    {T2COLORS,	"ptitlegradient",	&wPreferences.ptitle_gradient},
    {T2COLORS,	"utitlegradient",	&wPreferences.utitle_gradient},
    {T2COLORS,	"menutitlegradient",	&wPreferences.menu_title_gradient},
    {T2COLORS,	"menutextgradient",	&wPreferences.menu_text_gradient},

    {0,		NULL,			NULL},      
};


/* keyboard shortcuts */
static WOption KbdShortcut[] = {
    {TKEYCODE, 	"rootmenu",		(void*)WKBD_ROOTMENU},
    {TKEYCODE, 	"miniaturize",		(void*)WKBD_MINIATURIZE},
    {TKEYCODE, 	"hide",			(void*)WKBD_HIDE},
    {TKEYCODE, 	"close",		(void*)WKBD_CLOSE},
    {TKEYCODE, 	"maximize",		(void*)WKBD_MAXIMIZE},
    {TKEYCODE, 	"raise",		(void*)WKBD_RAISE},
    {TKEYCODE, 	"lower",		(void*)WKBD_LOWER},
    {TKEYCODE, 	"raiselower",		(void*)WKBD_RAISELOWER},
    {TKEYCODE, 	"shade",		(void*)WKBD_SHADE},
    {TKEYCODE, 	"focusnext",		(void*)WKBD_FOCUSNEXT},
    {TKEYCODE, 	"focusprev",		(void*)WKBD_FOCUSPREV},
    {TKEYCODE, 	"nextworkspace",	(void*)WKBD_NEXTWORKSPACE},
    {TKEYCODE, 	"prevworkspace",	(void*)WKBD_PREVWORKSPACE},
    {TKEYCODE, 	"workspace1",		(void*)WKBD_WORKSPACE1},
    {TKEYCODE, 	"workspace2",		(void*)WKBD_WORKSPACE2},
    {TKEYCODE, 	"workspace3",		(void*)WKBD_WORKSPACE3},
    {TKEYCODE, 	"workspace4",		(void*)WKBD_WORKSPACE4},
    {TKEYCODE, 	"workspace5",		(void*)WKBD_WORKSPACE5},
    {TKEYCODE, 	"workspace6",		(void*)WKBD_WORKSPACE6},
    {TKEYCODE, 	"workspace7",		(void*)WKBD_WORKSPACE7},
    {TKEYCODE, 	"workspace8",		(void*)WKBD_WORKSPACE8},
    {TKEYCODE, 	"workspace9",		(void*)WKBD_WORKSPACE9},
};


/* sections */
static WSection Sections[] = {
    {"[preferences]", 	PreferenceOptions},
    {"[style]",		StyleOptions},
    {"[attributes]",	WindowOptions},
    {"[shortcuts]",	KbdShortcut},
    {NULL, 		NULL}
};



static int
separate(char *line, char *left, char *right, int separator)
{
    char *end, *tmp;
    int i;

    end=strchr(line, separator);
    if (!end) return 0;
    i = end-line;
    strncpy(left, line, i); i--;
    while (i>0 && isspace(left[i])) i--;
    left[i+1]=0;    
    tmp = end+1;
    while (*tmp && isspace(*tmp)) tmp++;
    strcpy(right, tmp);
    return 1;
}


static char *
str_upper(char *line)
{
    while (*line) {
	*line=toupper(*line);
	line++;
    }
    return line;
}



static void
setWindowAttribute(char *line, char *path)
{
    WOption *option;
    WResource *res;
    char wname[MAXLINE], value[MAXPATH];
    char *opt;
    int i, w, v, done=0;
    unsigned int data;
    
    i=0;
    w=0;
    v=0;
    while (!done) {
	switch (line[i]) {
	 case '\'':
	    wname[w++]=line[++i];
	    break;
	    
	 case ':':
	    wname[w++]=0;
	    i++;
	    while (isspace(line[i]) && line[i]!=0) i++;
	    while (line[i] && !isspace(line[i])) value[v++]=line[i++];
	    value[v]=0;
	    done = 1;
	    break;

	 case 0:
	    wWarning(_("%s: syntax error in line \"%s\""), path, line);
	    return;
	    
	 default:
	    wname[w++]=line[i];
	}
	i++;
    }
    
    /* separate the option from the window specification */
    opt = strrchr(wname, '.');
    if (!opt) {
	wWarning(_("%s: invalid window attribute specification in \"%s\""),
		path, line);
	return;
    }
    *opt = 0;
    opt++;
    
    
    if (wname[0]=='*') {
	/* this is a global default */
	res = &wResourceDefault;
    } else {
	res = table_sget(wResDatabase, wname);
	if (!res) {
	    /* create new entry */
	    res = wmalloc(sizeof(WResource));
	    memset(res, 0, sizeof(WResource));
	    table_sput(wResDatabase, wname, res);
	}
    }
    
    for (option=WindowOptions; option->type!=0; option++) {
	if (strcasecmp(option->name, opt)==0)
	  break;
    }
    switch (option->type) {
     case 0:
	wWarning(_("%s: unknown attribute in line \"%s\""), path, line);
	return;
	
     case TWSTRING: {
	char **path=(char**)res;
	 path+=(long)option->ptr;
	if (*path)
	  free(*path);
	*path = strdup(value);
	break;
     }

     case TWWATTRIB:
	data = (unsigned int)option->ptr;
	res->valid |= data;
	
	str_upper(value);
	if (value[1]==0) {
	    if (value[0]=='Y' || value[0]=='T' || value[0]=='1')
	      i = 1;
	    else if (value[0]=='N' || value[0]=='F' || value[0]=='0')
	      i = 0;
	    else {
		wWarning(_("%s: syntax error in line \"%s\""), path, line);
		break;
	    }
	} else {
	    if (strcmp(value, "YES")==0 || strcmp(value, "TRUE")==0)
	      i = 1;
	    else if (strcmp(value, "NO")==0 || strcmp(value, "FALSE")==0)
	      i = 0;
	    else {
		wWarning(_("%s: syntax error in line \"%s\""), path, line);
		break;
	    }
	}
	if (i) {
	    res->value |= data;
	} else {
	    res->value &= ~data;
	}
	break;
    }
}


static int
getmod(char *key)
{
    if (strcasecmp(key, "SHIFT")==0)
      return ShiftMask;
    else if (strcasecmp(key, "CONTROL")==0)
      return ControlMask;
    else if (strcasecmp(key, "ALT")==0)
      return Mod1Mask;
    else if (strcasecmp(key, "META")==0)
      return Mod2Mask;
    else if (strcasecmp(key, "SUPER")==0)
      return Mod3Mask;
    else if (strcasecmp(key, "HYPER")==0)
      return Mod4Mask;
    else
      return -1;
}

static void
parseShortcut(int command, char *key, char *path)
{
    WShortKey *shortcut;
    KeySym ksym;
    char *k;
    
    shortcut = wmalloc(sizeof(WShortKey));
    memset(shortcut, 0, sizeof(WShortKey));
    shortcut->modifier = 0;

    k = strrchr(key, '+');
    if (!k) {
	ksym = XStringToKeysym(key);
	if (ksym==NoSymbol || ksym==0) {
	    wWarning(_("%s:invalid kbd shortcut specification \"%s\""), path,
		     key);
	    wfree(shortcut);
	    return;
	}
	shortcut->keycode = XKeysymToKeycode(dpy, ksym);
    } else {
	ksym = XStringToKeysym(k+1);
	if (ksym==NoSymbol || ksym==0 
	    || (ksym >=XK_Shift_L && ksym<=XK_Hyper_R)) {
	    wWarning(_("%s:invalid kbd shortcut specification \"%s\""), path,
		     key);
	    wfree(shortcut);
	    return;
	}
	*k=0;
	shortcut->keycode = XKeysymToKeycode(dpy, ksym);
	k = strtok(key, "+");
	while (k) {
	    int mod;
	    mod = getmod(k);
	    if (mod<0) {
		wWarning(_("%s:invalid kbd shortcut specification \"%s\""), 
			 path, key);
		wfree(shortcut);
		return;
	    }
	    shortcut->modifier |= mod;
	    k = strtok(NULL, "+");
	}
    }
    shortcut->alternate = wKeyBindings[command];
    wKeyBindings[command] = shortcut;
}


static void
parseFile(FILE *file, char *path)
{
    char buffer[MAXLINE], left[MAXLINE], right[MAXLINE];
    char ebuffer[MAXLINE];
    char *line;
    WOption *section=NULL;
    WOption *option;
    WSection *tmpsec;
    int i, sec=0;
    
    while (!feof(file)) {
	int ok, lsize;

	ok = 0;
	if (!fgets(buffer, MAXLINE, file)) {
	    return;
	}
	/* trim blanks */
	line = cropline(buffer);
	if (*line==0) continue;
	
	lsize = strlen(line);
	while (!ok && !feof(file)) {
	    if (line[lsize-2]=='\\') {
		char *line2;
		int lsize2;
		fgets(ebuffer, MAXLINE, file);
		line2=cropline(ebuffer);
		lsize2=strlen(line2);
		if (lsize2+lsize>MAXLINE) {
		    wWarning(_("maximal line size exceeded in menu config: %s"),
			     line);
		    ok=2;
		} else {
		    line[lsize-2]=0;
		    lsize+=lsize2-2;
		    strcat(line, line2);
		}
	    } else {
		ok=1;
	    }
	}
	if (ok==2)
	  continue;
	if (*line=='#' || (line[0]=='/' && line[1]=='/')) continue;
	if (*line=='[') {
	    /* change section */
	    for (tmpsec = Sections; tmpsec->name!=NULL; tmpsec++) {
		if (strcasecmp(tmpsec->name, line)==0) {
		    break;
		}
	    }
	    if (!tmpsec->name) {
		wWarning(_("%s: expected section identifier but got %s"),
			 path, line);
		return;
	    }
	    section = tmpsec->options;
	    if (section==WindowOptions)
	      sec=1;
	    else
	      sec=0;
	    continue;
	}
	/* wait for a section identifier */
	if (!section || !section->name) continue;

	if (sec) {
	    setWindowAttribute(line, path);
	    continue;
	}
	
	if (!separate(line, left, right, '=')) {
	    wWarning(_("%s: syntax error in line \"%s\""), path, line);
	    continue;
	}
	
	/* search the option */
	for (option = section; option->type!=0; option++) {
	    if (strcasecmp(option->name, left)==0)
	      break;
	}

	/* assign values */
	switch (option->type) {
	 case 0:
	    wWarning(_("%s: unknown option in line \"%s\""), path, line);
	    break;

	 case TBOOLEAN:
	    str_upper(right);
	    if (right[1]==0) {
		if (right[0]=='Y' || right[0]=='T' || right[0]=='1')
		  i = 1;
		else if (right[0]=='N' || right[0]=='F' || right[0]=='0')
		  i = 0;
		else {
		    wWarning(_("%s: syntax error in line \"%s\""), path, line);
		    break;
		}
	    } else {
		if (strcmp(right, "YES")==0 || strcmp(right, "TRUE")==0)
		  i = 1;
		else if (strcmp(right, "NO")==0 || strcmp(right, "FALSE")==0)
		  i = 0;
		else {
		    wWarning(_("%s: syntax error in line \"%s\""), path, line);
		    break;
		}
	    }
	    {
		char *val=option->ptr;
		*val=i;
	    }
	    break;
	    
	 case TSTRING: {
	     char **tmp=option->ptr;
	    if (*tmp) {
		free(*tmp);
	    }
	    *tmp = strdup(right);
	    break;
	 }
	    
	    
	 case TDISPLAY:
	    if (strcasecmp(right, "CENTER")==0)
	      i = WDIS_CENTER;
	    else if (strcasecmp(right, "CORNER")==0)
	      i = WDIS_TOPLEFT;
	    else if (strcasecmp(right, "FLOATING")==0)
	      i = WDIS_FRAME_CENTER;
	    else if (strcasecmp(right, "LINE")==0)
	      i = WDIS_NEW;
	    else {
		wWarning(_("%s: invalid geometry display type \"%s\". Should"\
	 		"be center, corner, floating or line"), path, right);
		break;
	    }
	    {
		char *val=option->ptr;
		*val = i;
	    }
	    break;

	 case TFOCUS:
	    if (strcasecmp(right, "MANUAL")==0 
		|| strcasecmp(right, "CLICKTOFOCUS")==0)
	      i = WKF_CLICK;
	    else if (strcasecmp(right, "AUTO")==0
		     || strcasecmp(right, "FOCUSFOLLOWSMOUSE")==0)
	      i = WKF_POINTER;
	    else if (strcasecmp(right, "SEMIAUTO")==0 
		     || strcasecmp(right, "SLOPPYFOCUS")==0
		     || strcasecmp(right, "SLOPPY")==0)
	      i = WKF_SLOPPY;
	    else {
		wWarning(_("%s: invalid focus mode \"%s.\" Should be manual"\
			", auto or semiauto"), path, right);
		break;
	    }
	    {
		char *val = option->ptr;
		*val = i;
	    }
	    break;
	    
	 case TPLACEMENT:
	    if (strcasecmp(right, "AUTO")==0)
	      i = WPM_AUTO;
	    else if (strcasecmp(right, "CASCADE")==0)
	      i = WPM_CASCADE;
	    else if (strcasecmp(right, "MANUAL")==0)
	      i = WPM_MANUAL;
	    else if (strcasecmp(right, "SMART")==0)
	      i = WPM_SMART;
	    else if (strcasecmp(right, "RANDOM")==0)
	      i = WPM_RANDOM;
	    else {
		wWarning(_("%s: invalid window placement mode \"%s\". "\
			"Should be cascade, smart, random or manual"), path, right);
		break;
	    }
	    {
		char *val = option->ptr;
		*val = i;
	    }
	    break;

	 case TTEXTURE:
	    if (strcasecmp(right, "SOLID")==0)
	      i = WTEX_SOLID;
	    else if (strcasecmp(right, "VGRADIENT")==0)
	      i = WTEX_VGRADIENT;
	    else if (strcasecmp(right, "HGRADIENT")==0)
	      i = WTEX_HGRADIENT;
	    else if (strcasecmp(right, "DGRADIENT")==0)
	      i = WTEX_DGRADIENT;
	    else {
		wWarning(_("%s: invalid texture type \"%s\" Should be solid, "\
			"dgradient, vgradient or hgradient"), path, right);
		break;
	    }
	    {
		char *val = option->ptr;
		*val = i;
	    }
	    break;
	    
	 case T2COLORS: 
	    {
		W2Color *color = option->ptr;
		char *tmp;
		
		if (!color->color1)
		  wfree(color->color1);
		if (!color->color2)
		  wfree(color->color2);
		color->color1 = wmalloc(strlen(right));
		color->color2 = wmalloc(strlen(right));
		tmp = strchr(right, ',');
		if (!tmp) {
		    wfree(color->color1);
		    wfree(color->color2);
		    color->color1 = NULL;
		    color->color2 = NULL;
		    wWarning(_("%s: invalid gradient spec. \"%s\""), 
			     path, right);
		    break;
		}
		*tmp = 0;
		strcpy(color->color1, cropline(right));
		strcpy(color->color2, cropline(++tmp));
		break;
	    }

	 case TKEYCODE:
	    parseShortcut((int)option->ptr, right, path);
	    break;

	 case TINTEGER: {
	    int a;
	     if (sscanf(right, "%i", &a)!=1) {
		 wWarning(_("%s:could not convert \"%s\" to integer"),
			  path, right);
		 break;
	     }
	    *(int*)option->ptr = a;
	 }
	    break;
	    
	 case TCOORDINATE:
	    {
		WCoord *coord = option->ptr;
		int x, y;
		if (sscanf(right, "%i,%i", &x, &y)!=2) {
		    wWarning(_("%s: invalid coordinate \"%s\""), path, right);
		    break;
		}
		coord->x = x;
		coord->y = y;
	    }
	    break;
	    
	 case TJUSTIFICATION:
	    if (strcasecmp(right, "LEFT")==0)
	      i = WTJ_LEFT;
	    else if (strcasecmp(right, "CENTER")==0)
	      i = WTJ_CENTER;
	    else if (strcasecmp(right, "RIGHT")==0)
	      i = WTJ_RIGHT;
	    else {
		wWarning(_("%s: invalid justification type \"%s\". Should be"\
			" left, center or right"), path, right);
		break;
	    }
	    {
		char *val = option->ptr;
		*val = i;
	    }
	    break;

#ifdef DEBUG
	 default:
	    printf("Invalid option type in parseFile()\n");
#endif
	}
    }
}




static void
resourceRead(char *path)
{
    FILE *file=NULL;
#ifdef USECPP
    char command[MAXLINE];
    char *args = MakeCPPArgs(path);

    if (!args) {
	wWarning(_("could not make arguments for preference file preprocessor"));
    } else {
	sprintf(command, "%s %s %s", CPP_PATH, args, path);
	wfree(args);
	file = popen(command, "r");
	if (!file) {
	    wSysError(_("could not open preference file preprocessor"));
	}
    }
#endif /* USECPP */
    if (!file) {
	file = fopen(path,"r");
	if (!file) {
	    wSysError(_("could not open configuration file: \"%s\""), path);
	} else {
	    parseFile(file, path);
	}
	fclose(file);
	file = NULL;
    }
#ifdef USECPP
    if (file) {
	parseFile(file, path);
	pclose(file);
    }
#endif
}



void
wResourceInitialize()
{
    char *path;
    
    path=find_file(DEF_CONFIG_PATHS, DEF_PREFERENCES);

    /* preference defaults */
    wPreferences.colormap_follows_mouse = 1;
    wPreferences.root_menu_pos.x = -1;
    wPreferences.root_menu_pos.y = -1;
    
    memset(&wResourceDefault, 0, sizeof(WResource));
    /* default global defaults */
    wResourceDefault.valid = ~0;
    
    wResourceDefault.value =(WA_TITLEBAR|WA_RESIZABLE|WA_CLOSABLE
			     |WA_BROKEN_CLOSE|WA_MINIATURIZABLE|WA_SHADEABLE
			     |WA_FOCUSABLE);

    if (path) {
	wResDatabase = wmalloc(sizeof(HashTable));
	table_init(wResDatabase);

	resourceRead(path);
	if (wResourceDefault.icon_file) {
	    char *file;
	    file = wResourceDefault.icon_file;
	    wResourceDefault.icon_file=find_file(PixmapPath, file);
	    if (!wResourceDefault.icon_file) {
		wWarning(_("could not find default icon \"%s\""), file);
	    }
	    free(file);
	}
	free(path);
    } else
      wWarning(_("could not find a preference file (%s) in \"%s\""),
	       DEF_PREFERENCES, DEF_CONFIG_PATHS);
    if (wPreferences.icon_back_file) {
	char *tmp;
	
	tmp = wPreferences.icon_back_file;
	wPreferences.icon_back_file = find_file(PixmapPath, tmp);
	if (!wPreferences.icon_back_file) {
	    wWarning(_("could not find icon background \"%s\""), tmp);
	}
	free(tmp);
    }
}



/*
 *---------------------------------------------------------------------- 
 * wResourceGet--
 * 	Get the attributes set by the user in the resource database 
 * (our own DB, not X's)
 * 
 * Returns:
 * 	The resource structure or
 * 	NULL if it's not defined for the window
 * 
 * Notes:
 * 	Order of precedence is (lower to higher):
 * 		window class
 * 		window instance name
 * 		window name (title)
 * 		window class.instance
 *---------------------------------------------------------------------- 
 */
WResource *
wResourceGet(char *name, char *instance, char *wclass)
{
    WResource *res;
    char tmp[MAXLINE];

    if (!wResDatabase) 
      return NULL;

    if (instance && wclass) {
	strcpy(tmp, instance);
	strcat(tmp, ".");
	strcat(tmp, wclass);
	res = table_sget(wResDatabase, tmp);
	if (res)
	  return res;
    }

    if (name) {
	res = table_sget(wResDatabase, name);
	if (res)
	  return res;
    }

    if (instance) {
	res = table_sget(wResDatabase, instance);
	if (res)
	  return res;
    }

    if (wclass) {
	res = table_sget(wResDatabase, wclass);
	if (res)
	  return res;
    }
    return NULL;
}
