/*
 * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * CONECTIVA LINUX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of Conectiva Linux shall
 * not be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization from
 * Conectiva Linux.
 *
 * Author: Paulo César Pereira de Andrade <pcpa@conectiva.com.br>
 *
 * $XFree86: xc/programs/Xserver/hw/xfree86/xf86cfg/options.c,v 1.12 2001/11/03 20:32:31 paulo Exp $
 */

#include "options.h"
#include "xf86config.h"
#include <X11/Xresource.h>
#include <X11/Shell.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/SimpleMenP.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Viewport.h>
#include <ctype.h>

/*
 * Prototypes
 */
static void PopdownCallback(Widget, XtPointer, XtPointer);
static void SelectOptionCallback(Widget, XtPointer, XtPointer);
static void AddOption(Widget, XtPointer, XtPointer);
static void RemoveOption(Widget, XtPointer, XtPointer);
static void UpdateOption(Widget, XtPointer, XtPointer);
static void UpdateOptionList(void);
#ifdef USE_MODULES
static void AddDriverOption(Widget, XtPointer, XtPointer);
static void SelectModuleCallback(Widget, XtPointer, XtPointer);
static void SelectModuleOptionCallback(Widget, XtPointer, XtPointer);
static void ModuleOptionsPopdown(Widget, XtPointer, XtPointer);
#endif
static Bool EnumDatabase(XrmDatabase*, XrmBindingList, XrmQuarkList,
			 XrmRepresentation*, XrmValue*, XPointer);

/*
 * Initialization
 */
Widget optionsShell;
static XF86OptionPtr *options;
static Widget add, remov, update, list, name, value;
static char *option_str;
static int option_index, popped = False;
static char *Options = "lib/X11/Options";
XrmDatabase options_xrm;
struct {
    char *string;
    int offset;
} rebuild_xrm;
#ifdef USE_MODULES
static Widget modList, optList, desc, modOptionsShell, labelType;
static char *module_sel;
static char *types[] = {
    "none", "integer", "(non null) string", "string", "real",
    "boolean", "frequency",
};
#endif

/*
 * Implementation
 */
#ifdef USE_MODULES
static int
qcmp_str(_Xconst void *a, _Xconst void *b)
{
    return (strcmp(*(char**)a, *(char**)b));
}

void
ModuleOptionsPopup(Widget w, XtPointer user_data, XtPointer call_data)
{
    xf86cfgModuleOptions *info = module_options;

    if (modOptionsShell == NULL) {
	char **ops;
	int nops;
	Widget pane, form, viewport, bottom, popdown;

	modOptionsShell = XtCreatePopupShell("moduleOptions",
					     transientShellWidgetClass,
					     optionsShell, NULL, 0);

	pane = XtCreateManagedWidget("pane", panedWidgetClass,
				     modOptionsShell, NULL, 0);

	form = XtCreateManagedWidget("descriptions", formWidgetClass,
				     pane, NULL, 0);
	labelType = XtCreateManagedWidget("labelType", labelWidgetClass,
					  form, NULL, 0);
	XtCreateManagedWidget("module", labelWidgetClass, form, NULL, 0);
	viewport = XtCreateManagedWidget("viewM", viewportWidgetClass,
					 form, NULL, 0);
	ops = NULL;
	nops = 0;
	while (info) {
	    ++nops;
	    ops = (char**)XtRealloc((XtPointer)ops, sizeof(char*) * nops);
	    ops[nops - 1] = XtNewString(info->name);
	    info = info->next;
	}
	if (nops == 0) {
	    ops = (char**)XtMalloc(sizeof(char*));
	    ops[0] = XtNewString("");
	    nops = 1;
	}
	else
	    qsort(ops, nops, sizeof(char*), qcmp_str);
	modList = XtVaCreateManagedWidget("modL", listWidgetClass,
					  viewport, XtNlist, ops,
					  XtNnumberStrings, nops,
					  NULL, 0);
	XtAddCallback(modList, XtNcallback, SelectModuleCallback, NULL);
	XtCreateManagedWidget("option", labelWidgetClass, form, NULL, 0);
	viewport = XtCreateManagedWidget("viewO", viewportWidgetClass,
					 form, NULL, 0);
	ops = (char**)XtMalloc(sizeof(char*));
	ops[0] = XtNewString("");
	optList = XtVaCreateManagedWidget("optL", listWidgetClass,
					  viewport, XtNlist, ops,
					  XtNnumberStrings, 1, NULL, 0);
	XtAddCallback(optList, XtNcallback, SelectModuleOptionCallback, NULL);
	desc = XtVaCreateManagedWidget("desc", asciiTextWidgetClass,
				       form, XtNeditType, XawtextRead,
				       NULL, 0);

	bottom = XtCreateManagedWidget("bottom", formWidgetClass,
				       pane, NULL, 0);
	popdown = XtVaCreateManagedWidget("popdown", commandWidgetClass,
					bottom, NULL, 0);
	XtAddCallback(popdown, XtNcallback, ModuleOptionsPopdown, NULL);
	XtRealizeWidget(modOptionsShell);
	XSetWMProtocols(DPY, XtWindow(modOptionsShell), &wm_delete_window, 1);

	info = module_options;
    }

    if (module_sel && *module_sel) {
	XawListReturnStruct list;	/* hack to call ballbacks */
	char **strs;
	int nstrs, idx = 0;

	XtVaGetValues(modList, XtNlist, &strs, XtNnumberStrings, &nstrs, NULL);
	for (idx = nstrs - 1; idx > 0; idx--)
	    if (strcmp(module_sel, strs[idx]) == 0)
		break;
	while (info) {
	    if (strcmp(module_sel, info->name) == 0)
		break;
	    info = info->next;
	}
	if (info) {
	    list.string = info->name;
	    list.list_index = idx;
	    XawListHighlight(modList, idx);
	    SelectModuleCallback(modList, NULL, (XtPointer)&list);
	}
	if (option_str && *option_str) {
	    OptionInfoPtr opts = info->option;

	    idx = 0;
	    while (opts && opts->name) {
		if (strcmp(opts->name, option_str) == 0)
		    break;
		++idx;
		++opts;
	    }

	    if (opts && opts->name) {
		list.string = (char *)opts->name;
		list.list_index = idx;
		XawListHighlight(optList, idx);
		SelectModuleOptionCallback(optList, NULL, (XtPointer)&list);
	    }
	}
    }
    XtPopup(modOptionsShell, XtGrabNone);
}

/*ARGSUSED*/
static void
ModuleOptionsPopdown(Widget w, XtPointer user_data, XtPointer call_data)
{
    XtPopdown(modOptionsShell);
}

/*ARGSUSED*/
void
ModuleOptionsCancelAction(Widget w, XEvent *event,
			  String *params, Cardinal *num_params)
{
    ModuleOptionsPopdown(w, NULL, NULL);
}
#endif

void
CreateOptionsShell(void)
{
    optionsShell = XtCreatePopupShell("options", transientShellWidgetClass,
				      toplevel, NULL, 0);
}

#ifdef USE_MODULES
void
OptionsPopup(XF86OptionPtr *opts, char *driver, OptionInfoPtr drv_opts)
#else
void
OptionsPopup(XF86OptionPtr *opts)
#endif
{
    static int first = 1;
#ifdef USE_MODULES
    static Widget button, menu;
    static char label[256], menuName[16];
    Widget sme;
    char buf[256];
    int i = 0;
    Arg args[1];
    static int menuN;
#endif

    option_str = NULL;
    options = opts;
    if (first) {
	Widget pane, form, viewport, bottom, popdown;

	first = 0;

	if (optionsShell == NULL)
	    CreateOptionsShell();
	pane = XtCreateManagedWidget("pane", panedWidgetClass,
				     optionsShell, NULL, 0);

	form = XtCreateManagedWidget("commands", formWidgetClass,
				     pane, NULL, 0);
	add = XtCreateManagedWidget("add", commandWidgetClass,
				    form, NULL, 0);
	XtAddCallback(add, XtNcallback, AddOption, NULL);
	remov = XtCreateManagedWidget("remove", commandWidgetClass,
				      form, NULL, 0);
	XtAddCallback(remov, XtNcallback, RemoveOption, NULL);
	update = XtCreateManagedWidget("update", commandWidgetClass,
				       form, NULL, 0);
	XtAddCallback(update, XtNcallback, UpdateOption, NULL);
#ifdef USE_MODULES
	if (!nomodules) {
	    Widget command;

	    command = XtCreateManagedWidget("help", commandWidgetClass,
					    form, NULL, 0);
	    XtAddCallback(command, XtNcallback, ModuleOptionsPopup, NULL);
	}
#endif
	form = XtCreateManagedWidget("form", formWidgetClass,
				     pane, NULL, 0);
	XtVaCreateManagedWidget("label1", labelWidgetClass, form,
				XtNlabel, " Option \"",
				NULL, 0);
	name = XtVaCreateManagedWidget("name", asciiTextWidgetClass, form,
				       XtNeditType, XawtextEdit,
				       NULL, 0);
	XtVaCreateManagedWidget("label2", labelWidgetClass,
				form,
				XtNlabel, "\" \"",
				NULL, 0);
	value = XtVaCreateManagedWidget("value", asciiTextWidgetClass, form,
					XtNeditType, XawtextEdit,
					NULL, 0);
	XtVaCreateManagedWidget("label3", labelWidgetClass, form,
				XtNlabel, "\" ",
				NULL, 0);
	viewport = XtCreateManagedWidget("viewport", viewportWidgetClass,
					 form, NULL, 0);
	list = XtCreateManagedWidget("list", listWidgetClass,
				     viewport, NULL, 0);
	XtAddCallback(list, XtNcallback, SelectOptionCallback, NULL);
	bottom = XtCreateManagedWidget("bottom", formWidgetClass,
				       pane, NULL, 0);
#ifdef USE_MODULES
	if (!nomodules)
	    button = XtCreateManagedWidget("driverOpts", menuButtonWidgetClass,
					    bottom, NULL, 0);
#endif
	popdown = XtVaCreateManagedWidget("popdown", commandWidgetClass,
					bottom, NULL, 0);
#ifdef USE_MODULES
	if (!nomodules)
	    XtVaSetValues(popdown, XtNfromHoriz, button, NULL, 0);
#endif

	XtAddCallback(popdown, XtNcallback, PopdownCallback, NULL);
	XtRealizeWidget(optionsShell);
	XSetWMProtocols(DPY, XtWindow(optionsShell), &wm_delete_window, 1);

#ifdef USE_MODULES
	if (!nomodules) {
	    char *str;

	    XtSetArg(args[0], XtNlabel, &str);
	    XtGetValues(button, args, 1);
	    XmuSnprintf(label, sizeof(label), "%s", str);
	}
#endif
    }

#ifdef USE_MODULES
    if (!nomodules) {
	if (menu)
	    XtDestroyWidget(menu);
	XmuSnprintf(menuName, sizeof(buf), "optionM%d", menuN);
	menuN = !menuN;
	menu = XtCreatePopupShell(menuName, simpleMenuWidgetClass, button,
				  NULL, 0);
	XtVaSetValues(button, XtNmenuName, menuName, NULL, 0);
	if (drv_opts) {
	    int len, longest = 0;
	    char fmt[32];

	    for (i = 0; drv_opts[i].name != NULL; i++) {
		len = strlen(drv_opts[i].name);
		if (len > longest)
		    longest = len;
	    }
	    XmuSnprintf(fmt, sizeof(fmt), "%c-%ds  %%s", '%', longest);
	    for (; drv_opts->name != NULL; drv_opts++) {
		char *type;

		if (drv_opts->type >= OPTV_NONE && drv_opts->type <= OPTV_FREQ)
		    type = types[drv_opts->type];
		else
		    type = "UNKNOWN";

		XmuSnprintf(buf, sizeof(buf), fmt, drv_opts->name, type);
		sme = XtVaCreateManagedWidget(drv_opts->name, smeBSBObjectClass,
					      menu, XtNlabel, buf, NULL, 0);
		XtAddCallback(sme, XtNcallback, AddDriverOption, (XtPointer)drv_opts);
	    }
	}
	if (i) {
	    xf86cfgModuleOptions *mod = module_options;

	    while (mod) {
		if (strcmp(mod->name, driver) == 0) {
		    /* don't assign to driver, as it may be a temp string */
		    module_sel = mod->name;
		    break;
		}
		mod = mod->next;
	    }
	    XmuSnprintf(buf, sizeof(buf), "%s%s", label, driver);
	    XtSetArg(args[0], XtNlabel, buf);
	    XtSetValues(button, args, 1);
	    XtMapWidget(button);
	}
	else
	    XtUnmapWidget(button);
    }
#endif

    UpdateOptionList();
    popped = True;
    XtPopup(optionsShell, XtGrabExclusive);

    while (popped)
	XtAppProcessEvent(XtWidgetToApplicationContext(optionsShell), XtIMAll);
}

static void
UpdateOptionList(void)
{
    Arg args[2];
    char **ops, **oldops;
    int nops, oldnops;
    XF86OptionPtr opt;

    ops = NULL;
    nops = 0;
    XawListUnhighlight(list);
    XtSetArg(args[0], XtNlist, &oldops);
    XtSetArg(args[1], XtNnumberStrings, &oldnops);
    XtGetValues(list, args, 2);
    opt = *options;
    while (opt != NULL) {
	if (nops % 16 == 0)
	    ops = (char**)XtRealloc((XtPointer)ops, (nops + 16) *
				    sizeof(char*));
	ops[nops++] = XtNewString(opt->opt_name);
	opt = (XF86OptionPtr)(opt->list.next);
    }
    if (nops == 0) {
	ops = (char**)XtMalloc(sizeof(char*));
	ops[0] = XtNewString("");
	nops = 1;
    }
    XtSetArg(args[0], XtNlist, ops);
    XtSetArg(args[1], XtNnumberStrings, nops);
    XtSetValues(list, args, 2);
    if (oldnops > 0 &&
	(oldnops != 1 || XtName(list) != oldops[0])) {
	while (--oldnops >= 0)
	    XtFree(oldops[oldnops]);
	XtFree((XtPointer)oldops);
    }

    XtSetArg(args[0], XtNstring, "");
    XtSetValues(name, args, 1);
    XtSetValues(value, args, 1);

    /* force relayout */
    XtUnmanageChild(list);
    XtManageChild(list);

    XtSetSensitive(remov, False);
    XtSetSensitive(update, False);
}

/*ARGSUSED*/
static void
PopdownCallback(Widget w, XtPointer user_data, XtPointer call_data)
{
    XtPopdown(optionsShell);
    popped = False;
}

/*ARGSUSED*/
void
OptionsCancelAction(Widget w, XEvent *event,
		    String *params, Cardinal *num_params)
{
    PopdownCallback(w, NULL, NULL);
}

/*ARGSUSED*/
static void
SelectOptionCallback(Widget w, XtPointer user_data, XtPointer call_data)
{
    Arg args[1];
    XF86OptionPtr option;
    XawListReturnStruct *info = (XawListReturnStruct *)call_data;

    option_str = info->string;
    option_index = info->list_index;
    if ((option = xf86findOption(*options, info->string)) != NULL) {
	XtSetArg(args[0], XtNstring, option->opt_name);
	XtSetValues(name, args, 1);
	XtSetArg(args[0], XtNstring,
		 option->opt_val != NULL ? option->opt_val : "");
	XtSetValues(value, args, 1);
    }
    XtSetSensitive(remov, True);
    XtSetSensitive(update, True);
}

/*ARGSUSED*/
static void
AddOption(Widget w, XtPointer user_data, XtPointer call_data)
{
    Arg args[1];
    char *nam, *val;

    XtSetArg(args[0], XtNstring, &nam);
    XtGetValues(name, args, 1);
    XtSetArg(args[0], XtNstring, &val);
    XtGetValues(value, args, 1);
    if (xf86findOption(*options, nam) != NULL || strlen(nam) == 0)
	/* XXX xf86addNewOption will trash the option linked list if
	 * the options being added already exists.
	 */
	return;
    *options = xf86addNewOption(*options, XtNewString(nam),
				val && strlen(val) ? XtNewString(val) : NULL);
    UpdateOptionList();
}

#ifdef USE_MODULES
/*ARGSUSED*/
static void
AddDriverOption(Widget w, XtPointer user_data, XtPointer call_data)
{
    Arg args[1];
    OptionInfoPtr opt = (OptionInfoPtr)user_data;
    XF86OptionPtr option;

    option_str = (char *)opt->name;
    XtSetArg(args[0], XtNstring, opt->name);
    XtSetValues(name, args, 1);
    if ((option = xf86findOption(*options, opt->name)) == NULL)
	XtSetArg(args[0], XtNstring, "");
    else
	XtSetArg(args[0], XtNstring, option->opt_val);
    XtSetValues(value, args, 1);
}

/*ARGSUSED*/
static void
SelectModuleCallback(Widget w, XtPointer user_data, XtPointer call_data)
{
    xf86cfgModuleOptions *mod = module_options;
    XawListReturnStruct *info = (XawListReturnStruct *)call_data;

    while (mod) {
	if (strcmp(mod->name, info->string) == 0)
	    break;
	mod = mod->next;
    }

    if (mod) {
	Arg args[2];
	char **list = NULL, **old;
	OptionInfoPtr opts = mod->option;
	int num = 0, oldnum;

	module_sel = mod->name;
	XtSetArg(args[0], XtNlist, &old);
	XtSetArg(args[1], XtNnumberStrings, &oldnum);
	XtGetValues(optList, args, 2);
	while (opts && opts->name) {
	    ++num;
	    list = (char**)XtRealloc((XtPointer)list, sizeof(char*) * num);
	    list[num - 1] = XtNewString(opts->name);
	    ++opts;
	}
	if (num == 0) {
	    list = (char**)XtMalloc(sizeof(char*));
	    list[0] = XtNewString("");
	    num = 1;
	}
	XtSetArg(args[0], XtNlist, list);
	XtSetArg(args[1], XtNnumberStrings, num);
	XtSetValues(optList, args, 2);
	while (--oldnum >= 0)
	    XtFree(old[oldnum]);
	XtFree((XtPointer)old);

	XtVaSetValues(desc, XtNstring, "", NULL);
	XawListUnhighlight(optList);

	/* force relayout */
	XtUnmanageChild(optList);
	XtManageChild(optList);
    }
}

static void
SelectModuleOptionCallback(Widget w, XtPointer user_data, XtPointer call_data)
{
    xf86cfgModuleOptions *mod = module_options;
    XawListReturnStruct *info = (XawListReturnStruct *)call_data;
    char *description = NULL, *type = "undefined";
    char label[256];

    if (module_sel && info->string)
	description = GetOptionDescription(module_sel, info->string);
    if (description == NULL)
	description = "** NO DESCRIPTION AVAILABLE **";

    XtVaSetValues(desc, XtNstring, description, NULL);

    while (mod) {
	if (strcmp(module_sel, mod->name) == 0)
	    break;
	mod = mod->next;
    }
    if (mod) {
	OptionInfoPtr opts = mod->option;

	while (opts && opts->name) {
	    if (strcasecmp(opts->name, info->string) == 0)
		break;
	    ++opts;
	}
	if (opts && opts->name && opts->type >= OPTV_NONE &&
	    opts->type <= OPTV_FREQ)
	    type = types[opts->type];
    }

    XmuSnprintf(label, sizeof(label), "%s.%s (%s)", module_sel, info->string,
	        type);
    XtVaSetValues(labelType, XtNlabel, label, NULL);
}
#endif

/*ARGSUSED*/
static void
RemoveOption(Widget w, XtPointer user_data, XtPointer call_data)
{
    Arg args[1];
    char *str;

    XtSetArg(args[0], XtNstring, &str);
    XtGetValues(name, args, 1);
    xf86removeOption(options, str);
    UpdateOptionList();
}

/*ARGSUSED*/
static void
UpdateOption(Widget w, XtPointer user_data, XtPointer call_data)
{
/*    xf86removeOption(options, option_str);
    AddOption(w, user_data, call_data);
    UpdateOptionList();*/

    Arg args[1];
    char *nam, *val;
    XF86OptionPtr option;

    XtSetArg(args[0], XtNstring, &nam);
    XtGetValues(name, args, 1);
    XtSetArg(args[0], XtNstring, &val);
    XtGetValues(value, args, 1);
    if ((option = xf86findOption(*options, option_str)) == NULL)
	return;
    XtFree(option->opt_name);
    option->opt_name = option_str = XtNewString(nam);
    XtFree(option->opt_val);
    if (val && strlen(val))
	option->opt_val = XtNewString(val);
    else
	option->opt_val = NULL;

    UpdateOptionList();
    XawListHighlight(list, option_index);
    XtSetArg(args[0], XtNstring, option->opt_name);
    XtSetValues(name, args, 1);
    XtSetArg(args[0], XtNstring, option->opt_val);
    XtSetValues(value, args, 1);

    XtSetSensitive(remov, True);
    XtSetSensitive(update, True);
}

/*ARGUSED*/
static Bool
EnumDatabase(XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
	     XrmRepresentation *type, XrmValue *value, XPointer closure)
{
    char *module = XrmQuarkToString(quarks[0]),
	 *option = XrmQuarkToString(quarks[1]);

    /* handle *.Option: value */
    if (module && option == NULL) {
	option = module;
	module = "*";
    }

    /*
     * NOTE: If the Options file is changed to support any other format than
     *
     *		Module.Option: description text
     *
     * this code will also need to be updated.
     */

    if (module) {
	XrmValue xrm;
	char *type, *value, query[256];

	XmuSnprintf(query, sizeof(query), "%s.%s", module, option);
	if (XrmGetResource(options_xrm, query, "Module.Option", &type, &xrm))
	    value = (char*)xrm.addr;
	else
	    value = NULL;

	if (value) {
	    char *norm;
	    unsigned char *ptr;
	    int position;
	    int length = strlen(module) + strlen(option) + strlen(value) + 4;

	    rebuild_xrm.string = XtRealloc(rebuild_xrm.string,
					   rebuild_xrm.offset + length);
	    position = rebuild_xrm.offset +
		       sprintf(rebuild_xrm.string + rebuild_xrm.offset, "%s.%s:",
			       module, option);

	    /* removes underlines and spaces */
	    norm = strchr(rebuild_xrm.string + rebuild_xrm.offset, '.') + 1;
	    for (; *norm; norm++) {
		if (*norm == '_' || *norm == ' ' || *norm == '\t') {
		    memmove(norm, norm + 1, strlen(norm) + 1);
		    --position;
		    --length;
		}
	    }

	    for (ptr = (unsigned char*)rebuild_xrm.string + rebuild_xrm.offset;
		 *ptr; ptr++)
		*ptr = tolower(*ptr);
	    sprintf(rebuild_xrm.string + position, "%s\n", value);
	    rebuild_xrm.offset += length - 1;
	}
    }

    return (False);
}

Bool
InitializeOptionsDatabase(void)
{
    static int first = 1;
    static Bool result = True;

    if (first) {
	XrmQuark names[2];
	XrmQuark classes[2];

	first = 0;
	XrmInitialize();
	if ((options_xrm = XrmGetFileDatabase(Options)) == (XrmDatabase)0) {
	    fprintf(stderr, "Cannot open '%s' database.\n", Options);
	    return (False);
	}

	/* rebuild database, using only lowercase characters */
	names[0] = classes[0] = names[1] = classes[1] = NULLQUARK;
	(void)XrmEnumerateDatabase(options_xrm, (XrmNameList)&names,
				   (XrmClassList)&classes, XrmEnumAllLevels,
				   EnumDatabase, NULL);

	/* free previous database, as it is not guaranteed to be
         * "case insensitive" */
	XrmDestroyDatabase(options_xrm);

	/* create case insensitive database by making everything lowercase */
	if (rebuild_xrm.string == NULL ||
	    (options_xrm = XrmGetStringDatabase(rebuild_xrm.string)) ==
	    (XrmDatabase)0) {
	    fprintf(stderr, "Cannot rebuild '%s' database.\n", Options);
	    XtFree(rebuild_xrm.string);
	    return (False);
	}
	XtFree(rebuild_xrm.string);
    }

    return (result);
}

char *
GetOptionDescription(char *module, char *option)
{
    char *type;
    XrmValue value;
    char query[256];
    unsigned char *ptr;

    InitializeOptionsDatabase();

    XmuSnprintf(query, sizeof(query), "%s.%s", module, option);
    ptr = (unsigned char*)strchr(query, '.') + 1;
    for (; *ptr; ptr++) {
	if (*ptr == '_' || *ptr == ' ' || *ptr == '\t')
	    memmove(ptr, ptr + 1, strlen((char*)ptr) + 1);
    }
    for (ptr = (unsigned char*)query; *ptr; ptr++)
	*ptr = tolower(*ptr);
    if (XrmGetResource(options_xrm, query, "Module.Option", &type, &value))
	return ((char*)value.addr);

    return (NULL);
}
