/*
 * Copyright (c) 1998 by The XFree86 Project, Inc.
 *
 * 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
 * THE XFREE86 PROJECT 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 the XFree86 Project shall
 * not be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization from the
 * XFree86 Project.
 */

/* $XFree86: xc/lib/Xaw/Actions.c,v 3.17 2003/03/25 04:18:10 dawes Exp $ */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xmd.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/CoreP.h>
#include <X11/Constraint.h>
#include <X11/Xmu/CharSet.h>
#include <X11/Xmu/SysUtil.h>
#include <X11/Xfuncs.h>
#include "Private.h"

#ifdef __UNIXOS2__
static char dummy;
#endif

#ifndef OLDXAW

/*
 * Definitions
 */
#define ERROR   -2
#define END     -1
#define BOOLEAN 0
#define AND     '&'
#define OR      '|'
#define XOR     '^'
#define NOT     '~'
#define LP      '('
#define RP      ')'

/*
 * Types
 */
/* boolean expressions */
typedef struct _XawEvalInfo {
  Widget widget;
  XawActionResList *rlist;
  XawActionVarList *vlist;
  XawParseBooleanProc parse_proc;
  XEvent *event;
  char *cp, *lp;
  int token;
  Bool value;
} XawEvalInfo;

/* resources */
typedef struct _XawActionRes {
  XrmQuark qname;
  XrmQuark qtype;
  Cardinal size;
} XawActionRes;

struct _XawActionResList {
  WidgetClass widget_class;
  XawActionRes **resources;
  Cardinal num_common_resources;
  Cardinal num_constraint_resources;
};

/* variables */
typedef struct _XawActionVar {
  XrmQuark qname;
  XrmQuark qvalue;
} XawActionVar;

struct _XawActionVarList {
  Widget widget;
  Cardinal num_variables;
  XawActionVar **variables;
};

/*
 * Private methods
 */
/* expressions */
static int get_token(XawEvalInfo*);
static Bool expr(XawEvalInfo*);
static Bool and(XawEvalInfo*);
static Bool prim(XawEvalInfo*);

/* resources */
static String XawConvertActionRes(XawActionResList*, Widget w, String);

static String _XawEscapeActionVarValue(String);
static String _XawUnescapeActionVarValue(String);
static XawActionResList *_XawCreateActionResList(WidgetClass);
static XawActionResList *_XawFindActionResList(WidgetClass);
static void _XawBindActionResList(XawActionResList*);
static XawActionRes *_XawFindActionRes(XawActionResList*, Widget, String);
static int qcmp_action_resource_list(_Xconst void*, _Xconst void*);
static int bcmp_action_resource_list(_Xconst void*, _Xconst void*);
static int qcmp_action_resource(_Xconst void*, _Xconst void*);
static int bcmp_action_resource(_Xconst void*, _Xconst void*);

/* variables */
static String XawConvertActionVar(XawActionVarList*, String);
static void XawDeclareActionVar(XawActionVarList*, String, String);

static XawActionVarList *_XawCreateActionVarList(Widget);
static XawActionVarList *_XawFindActionVarList(Widget);
static XawActionVar *_XawCreateActionVar(XawActionVarList*, String);
static XawActionVar *_XawFindActionVar(XawActionVarList*, String);
static void _XawDestroyActionVarList(Widget, XtPointer, XtPointer);

/*
 * Initialization
 */
/* resources */
static XawActionResList **resource_list;
static Cardinal num_resource_list;

/* variables */
static XawActionVarList **variable_list;
static Cardinal num_variable_list;

/*
 * Implementation
 */
/*
 * Start of Boolean Expression Evaluation Implementation Code
 */
Bool
XawParseBoolean(Widget w, String param, XEvent *event, Bool *succed)
{
  char *tmp = param;
  int value;

  if (!param)
    return (False);

  value = (int)strtod(param, &tmp);
  if (*tmp == '\0')
    return (value);

  if (XmuCompareISOLatin1(param, "true") == 0
      || XmuCompareISOLatin1(param, "yes") == 0
      || XmuCompareISOLatin1(param, "on") == 0
      || XmuCompareISOLatin1(param, "in") == 0
      || XmuCompareISOLatin1(param, "up") == 0)
    return (True);
  else if (XmuCompareISOLatin1(param, "false") == 0
	  || XmuCompareISOLatin1(param, "no") == 0
	  || XmuCompareISOLatin1(param, "off") == 0
	  || XmuCompareISOLatin1(param, "out") == 0
	  || XmuCompareISOLatin1(param, "down") == 0)
      ;
  else if (XmuCompareISOLatin1(param, "my") == 0
	   || XmuCompareISOLatin1(param, "mine") == 0)
    return (event->xany.window == XtWindow(w));
  else if (XmuCompareISOLatin1(param, "faked") == 0)
    return (event->xany.send_event != 0);
  else
    *succed = False;

  return (False);
}

Bool
XawBooleanExpression(Widget w, String param, XEvent *event)
{
  XawEvalInfo info;
  Bool retval;

  if (!param)
    return (False);

  info.widget = w;

  info.rlist = XawGetActionResList(XtClass(w));
  info.vlist = XawGetActionVarList(w);

  /*
   * Verify widget class, in case we will allow the parse proc procedure
   * as a widget class element, or if we allow overriding the default
   * parse boolean proc.
   */
  info.parse_proc = XawParseBoolean;

  info.event = event;
  info.cp = info.lp = param;

#ifdef DIAGNOSTIC
  fprintf(stderr, "(*) Parsing expression \"%s\"\n", param);
#endif

  (void)get_token(&info);
  if (info.token == ERROR)
    return (False);
  retval = expr(&info);

  return (info.token != ERROR ? retval : False);
}

static int
get_token(XawEvalInfo *info)
{
  int ch;
  char *p, name[256];

  info->lp = info->cp;

  /*COSTCOND*/
  while (1)	/* eat white spaces */
    {
      ch = *info->cp++;
      if (isspace(ch))
	continue;
      break;
    }

  switch (ch)
    {
    case AND: case OR: case XOR: case NOT: case LP: case RP:
      return (info->token = ch);
    }

  /* It's a symbol name, resolve it. */
  if (ch == XAW_PRIV_VAR_PREFIX || isalnum(ch) || ch == '_' || ch == '\\')
    {
      Bool succed = True;

      p = info->cp - 1;

      while ((ch = *info->cp) && (isalnum(ch) || ch == '_'))
	++info->cp;

      strncpy(name, p, XawMin((int)sizeof(name) - 1,
			      (unsigned)(info->cp - p)));
      name[XawMin((int)sizeof(name) -1, info->cp - p)] = '\0';

      if (name[0] == XAW_PRIV_VAR_PREFIX)
	{
	  String value = XawConvertActionVar(info->vlist, name);

	  info->value = info->parse_proc(info->widget, value, info->event,
					 &succed) & 1;
	}
      else
	{
	  info->value = info->parse_proc(info->widget, name, info->event,
					 &succed) & 1;
	  if (!succed)
	    {
	      String value =
		XawConvertActionRes(info->rlist, info->widget,
				    name[0] == '\\' ? &name[1] : name);
	      /* '\\' may have been used to escape a resource name.
	       */

	      succed = True;
	      info->value = info->parse_proc(info->widget, value, info->event,
					     &succed) & 1;
	      if (!succed)
		{
		  /* not a numeric value or boolean string */
		  info->value = True;
		  succed = True;
		}
	    }
	}
      if (succed)
	return (info->token = BOOLEAN);
    }
  else if (ch == '\0')
    return (info->token = END);

  {
    char msg[256];

    XmuSnprintf(msg, sizeof(msg),
		"evaluate(): bad token \"%c\" at \"%s\"", ch, info->cp - 1);

    XtAppWarning(XtWidgetToApplicationContext(info->widget), msg);
  }

  return (info->token = ERROR);
}

static Bool
expr(XawEvalInfo *info)
{
  Bool left = and(info);

  for (;;)
    switch (info->token)
      {
      case OR:
	(void)get_token(info);
	left |= and(info);
	break;
      case XOR:
	(void)get_token(info);
	left ^= and(info);
	break;
      default:
	return (left);
      }
  /* NOTREACHED */
}

static Bool
and(XawEvalInfo *info)
{
  Bool left = prim(info);

  for (;;)
    switch (info->token)
      {
      case AND:
	(void)get_token(info);
	left &= prim(info);
	break;
      default:
	return (left);
      }
  /* NOTREACHED */
}

static Bool
prim(XawEvalInfo *info)
{
  Bool e;

  switch (info->token)
    {
    case BOOLEAN:
      e = info->value;
      (void)get_token(info);
      return (e);
    case NOT:
      (void)get_token(info);
      return (!prim(info));
    case LP:
      (void)get_token(info);
      e = expr(info);
      if (info->token != RP)
	{
	  char msg[256];

	  info->token = ERROR;
	  XmuSnprintf(msg, sizeof(msg),
		      "evaluate(): expecting ), at \"%s\"", info->lp);
	  XtAppWarning(XtWidgetToApplicationContext(info->widget), msg);
	  return (False);
	}
      (void)get_token(info);
      return (e);
    case END:
      return (True);
    default:
      {
	char msg[256];

	info->token = ERROR;
	XmuSnprintf(msg, sizeof(msg),
		    "evaluate(): sintax error, at \"%s\"", info->lp);
	XtAppWarning(XtWidgetToApplicationContext(info->widget), msg);
      } return (False);
    }
  /* NOTREACHED */
}

/*
 * Start of Resources Implementation Code
 */
void
XawSetValuesAction(Widget w, XEvent *event,
		   String *params, Cardinal *num_params)
{
  Arg *arglist;
  Cardinal num_args, count;
  XawActionResList *rlist;
  XawActionVarList *vlist;
  XawActionRes *resource;
  XrmValue from, to;
  String value;
  char  c_1;
  short c_2;
  int   c_4;
#ifdef LONG64
  long  c_8;
#endif

  if (!(*num_params & 1))
    {
      XawPrintActionErrorMsg("set-values", w, params, num_params);
      return;
    }

  if (!XawBooleanExpression(w, params[0], event))
    return;

  rlist = XawGetActionResList(XtClass(w));
  vlist = XawGetActionVarList(w);

  num_args = 0;
  arglist = (Arg *)XtMalloc(sizeof(Arg) * ((*num_params) >> 1));

  for (count = 1; count < *num_params; count += 2)
    {
      if ((resource = _XawFindActionRes(rlist, w, params[count])) == NULL)
	{
	  char msg[256];

	  XmuSnprintf(msg, sizeof(msg),
		      "set-values(): bad resource name \"%s\"",
		   params[count]);
	  XtAppWarning(XtWidgetToApplicationContext(w), msg);
          continue;
	}
      value = XawConvertActionVar(vlist, params[count + 1]);
      from.size = strlen(value) + 1;
      from.addr = value;
      to.size = resource->size;
      switch (to.size)
	{
	case 1: to.addr = (XPointer)&c_1; break;
	case 2: to.addr = (XPointer)&c_2; break;
	case 4: to.addr = (XPointer)&c_4; break;
#ifdef LONG64
	case 8: to.addr = (XPointer)&c_8; break;
#endif
	default:
	  {
	    char msg[256];

	    XmuSnprintf(msg, sizeof(msg),
			"set-values(): bad resource size for \"%s\"",
			params[count]);
	    XtAppWarning(XtWidgetToApplicationContext(w), msg);
	  } continue;
	}

      if (strcmp(XtRString, XrmQuarkToString(resource->qtype)) == 0)
#ifdef LONG64
	c_8 = (long)from.addr;
#else
	c_4 = (int)from.addr;
#endif
      else if (!XtConvertAndStore(w, XtRString, &from,
				  XrmQuarkToString(resource->qtype), &to))
	continue;

      switch (to.size)
	{
	case 1:
	  XtSetArg(arglist[num_args], XrmQuarkToString(resource->qname), c_1);
	  break;
	case 2:
	  XtSetArg(arglist[num_args], XrmQuarkToString(resource->qname), c_2);
	  break;
	case 4:
	  XtSetArg(arglist[num_args], XrmQuarkToString(resource->qname), c_4);
	  break;
#ifdef LONG64
	case 8:
	  XtSetArg(arglist[num_args], XrmQuarkToString(resource->qname), c_8);
	  break;
#endif
	}
      ++num_args;
    }

  XtSetValues(w, arglist, num_args);
  XtFree((char *)arglist);
}

void
XawGetValuesAction(Widget w, XEvent *event,
		   String *params, Cardinal *num_params)
{     
  XawActionResList *rlist;
  XawActionVarList *vlist;
  String value;
  Cardinal count;

  if (!(*num_params & 1)) 
    {
      XawPrintActionErrorMsg("get-values", w, params, num_params);
      return;
    }
  if (!XawBooleanExpression(w, params[0], event))
    return;

  rlist = XawGetActionResList(XtClass(w));
  vlist = XawGetActionVarList(w);

  for (count = 1; count < *num_params; count += 2)
    {
      if ((value = XawConvertActionRes(rlist, w, params[count + 1])) == NULL)
	continue;
      XawDeclareActionVar(vlist, params[count], value);
    }
}

void
XawDeclareAction(Widget w, XEvent *event,
		 String *params, Cardinal *num_params)
{     
  XawActionVarList *vlist;
  Cardinal count;

  if (!(*num_params & 1))
    {
      XawPrintActionErrorMsg("declare", w, params, num_params);
      return;
    }
  if (!XawBooleanExpression(w, params[0], event))
    return;

  vlist = XawGetActionVarList(w);

  for (count = 1; count < *num_params; count += 2)
    XawDeclareActionVar(vlist, params[count], params[count + 1]);
}

void
XawCallProcAction(Widget w, XEvent *event,
		  String *params, Cardinal *num_params)
{
  String *args;
  Cardinal num_args;

  if (*num_params < 2)
    {
      XawPrintActionErrorMsg("call-proc", w, params, num_params);
      return;
    }

  if (*num_params && !XawBooleanExpression(w, params[0], event))
    return;

  if (*num_params > 2)
    {
      args = &params[2];
      num_args = *num_params - 2;
    }
  else
    {
      args = NULL;
      num_args = 0;
    }

  XtCallActionProc(w, params[1], event, args, num_args);
}

static String
XawConvertActionRes(XawActionResList *list, Widget w, String name)
{
  XawActionRes *resource;
  XrmValue from, to;
  Arg arg;
  char  c_1;
  short c_2;
  int   c_4;   
#ifdef LONG64
  long  c_8;
#endif

  if ((resource = _XawFindActionRes(list, w, name)) == NULL)
    {
      char msg[256];

      XmuSnprintf(msg, sizeof(msg),
		  "convert(): bad resource name \"%s\"", name);
      XtAppWarning(XtWidgetToApplicationContext(w), msg);
      return (NULL);
    }

  from.size = resource->size;
  switch (from.size)
    {
    case 1:
      XtSetArg(arg, XrmQuarkToString(resource->qname),
	       from.addr = (XPointer)&c_1);
      break;
    case 2:
      XtSetArg(arg, XrmQuarkToString(resource->qname),
               from.addr = (XPointer)&c_2);
      break;
    case 4:
      XtSetArg(arg, XrmQuarkToString(resource->qname),
               from.addr = (XPointer)&c_4);
      break;
#ifdef LONG64
    case 8:
      XtSetArg(arg, XrmQuarkToString(resource->qname),
	       from.addr = (XPointer)&c_8);
      break;
#endif
    default:
      {
        char msg[256];

        XmuSnprintf(msg, sizeof(msg),
		    "convert(): bad resource size for \"%s\"", name);
	XtAppWarning(XtWidgetToApplicationContext(w), name);
      } return (NULL);
    }

  XtGetValues(w, &arg, 1);
  to.size = sizeof(String);
  to.addr = NULL;

  if (strcmp(XtRString, XrmQuarkToString(resource->qtype)) == 0)
    to.addr = *(char **)from.addr;
  else if (!XtConvertAndStore(w, XrmQuarkToString(resource->qtype),
			      &from, XtRString, &to))
    return (NULL);

  return ((String)to.addr);
}

void
XawPrintActionErrorMsg(String action_name, Widget w,
		       String *params, Cardinal *num_params)
{
  char msg[1024];
  unsigned int size, idx;

  size = XmuSnprintf(msg, sizeof(msg), "%s(): bad number of parameters.\n\t(",
		     action_name);

  idx = 0;
  while (idx < *num_params - 1 && size < sizeof(msg))
    size += XmuSnprintf(&msg[size], sizeof(msg) - size, "%s, ",
			params[idx++]);
  if (*num_params)
    XmuSnprintf(&msg[size], sizeof(msg) - size, "%s)", params[idx]);
  else
    XmuSnprintf(&msg[size], sizeof(msg) - size, ")");
  XtAppWarning(XtWidgetToApplicationContext(w), msg);
}

XawActionResList *
XawGetActionResList(WidgetClass wc)
{
  XawActionResList *list;

  list = _XawFindActionResList(wc);

  if (!list)
    list = _XawCreateActionResList(wc);

  return (list);
}

static int
qcmp_action_resource_list(register _Xconst void *left,
			  register _Xconst void *right)
{   
  return ((char *)((*(XawActionResList **)left)->widget_class) -
          (char *)((*(XawActionResList **)right)->widget_class));
}

static XawActionResList *
_XawCreateActionResList(WidgetClass wc)
{
  XawActionResList *list;

  list = (XawActionResList *)XtMalloc(sizeof(XawActionResList));
  list->widget_class = wc;
  list->num_common_resources = list->num_constraint_resources = 0;
  list->resources = NULL;

  if (!resource_list)
    {
      num_resource_list = 1;
      resource_list = (XawActionResList **)XtMalloc(sizeof(XawActionResList*));
      resource_list[0] = list;
    }
  else
    {
      ++num_resource_list;
      resource_list = (XawActionResList **)XtRealloc((char *)resource_list,
						     sizeof(XawActionResList*)
						     * num_resource_list);
      resource_list[num_resource_list - 1] = list;
      qsort(resource_list, num_resource_list, sizeof(XawActionResList*),
	    qcmp_action_resource_list);
    }

  _XawBindActionResList(list);

  return (list);
}

static int
bcmp_action_resource_list(register _Xconst void *wc,
			  register _Xconst void *list)
{
  return ((char *)wc - (char *)((*(XawActionResList **)list)->widget_class));
}

static XawActionResList *
_XawFindActionResList(WidgetClass wc)
{  
  XawActionResList **list;

  if (!resource_list)
    return (NULL);

  list = (XawActionResList **)bsearch(wc, resource_list,
				      num_resource_list,
				      sizeof(XawActionResList*),
				      bcmp_action_resource_list);

  return (list ? *list : NULL);
}

static int
qcmp_action_resource(register _Xconst void *left,
		     register _Xconst void *right)
{
  return (strcmp(XrmQuarkToString((*(XawActionRes **)left)->qname),
		 XrmQuarkToString((*(XawActionRes **)right)->qname)));
}

static void
_XawBindActionResList(XawActionResList *list)
{
  XtResourceList xt_list, cons_list;
  Cardinal i, num_xt, num_cons;

#ifdef DIAGNOSTIC
  fprintf(stderr, "(*) Creating resource list for class \'%s\'\n---------\n",
	  list->widget_class->core_class.class_name);
#endif

  XtGetResourceList(list->widget_class, &xt_list, &num_xt);
  XtGetConstraintResourceList(list->widget_class, &cons_list, &num_cons);
  list->num_common_resources = num_xt;
  list->num_constraint_resources = num_cons;

  list->resources = (XawActionRes **)
    XtMalloc(sizeof(XawActionRes*) * (num_xt + num_cons));

#ifdef DIAGNOSTIC
  fprintf(stderr, "Common resources\n---\n");
#endif

  for (i = 0; i < num_xt; i++)
    {
      list->resources[i] = (XawActionRes *)XtMalloc(sizeof(XawActionRes));
      list->resources[i]->qname =
	XrmPermStringToQuark(xt_list[i].resource_name);
      list->resources[i]->qtype =
	XrmPermStringToQuark(xt_list[i].resource_type);
      list->resources[i]->size = xt_list[i].resource_size;

#ifdef DIAGNOSTIC
      fprintf(stderr, "%-20s\t%-20s\t(%d)\n",
	      xt_list[i].resource_name,
	      xt_list[i].resource_type,
	      xt_list[i].resource_size);
#endif
    }

#ifdef DIAGNOSTIC
  fprintf(stderr, "---\nContraint resources\n---");
#endif

  for (; i < num_xt + num_cons; i++)
    {
      list->resources[i] = (XawActionRes *)XtMalloc(sizeof(XawActionRes));
      list->resources[i]->qname =
	XrmPermStringToQuark(cons_list[i - num_xt].resource_name);
      list->resources[i]->qtype =
	XrmPermStringToQuark(cons_list[i - num_xt].resource_type);
      list->resources[i]->size = cons_list[i - num_xt].resource_size;

#ifdef DIAGNOSTIC
      fprintf(stderr, "%-20s\t%-20s\t(%d)\n",
	      cons_list[i - num_xt].resource_name,
	      cons_list[i - num_xt].resource_type,
	      cons_list[i - num_xt].resource_size);
#endif
    }

#ifdef DIAGNOSTIC
  fprintf(stderr, "---\n");
#endif

  XtFree((char *)xt_list);
  if (cons_list)
    XtFree((char *)cons_list);

  qsort(list->resources, list->num_common_resources, sizeof(XawActionRes*),
	qcmp_action_resource);
  if (num_cons)
    qsort(&list->resources[num_xt], list->num_constraint_resources,
	  sizeof(XawActionRes*), qcmp_action_resource);
}

static int
bcmp_action_resource(register _Xconst void *string,
		     register _Xconst void *resource)
{
  return (strcmp((String)string,
		 XrmQuarkToString((*(XawActionRes **)resource)->qname)));
}   
    
static XawActionRes *
_XawFindActionRes(XawActionResList *list, Widget detail, String name)
{
  XawActionRes **res;

  if (!list->resources)
    return (NULL);

  res = (XawActionRes **)bsearch(name, list->resources,
				 list->num_common_resources,
				 sizeof(XawActionRes*), bcmp_action_resource);

  if (!res && XtParent(detail)
      && XtIsSubclass(XtParent(detail), constraintWidgetClass))
    {
      XawActionResList *cons = XawGetActionResList(XtClass(XtParent(detail)));

      if (cons)
	res = (XawActionRes **)
	  bsearch(name, &cons->resources[cons->num_common_resources],
		  cons->num_constraint_resources,
		  sizeof(XawActionRes*), bcmp_action_resource);
    }

  return (res ? *res : NULL);
}

/*
 * Start of Variables Implementation Code
 */
/* For speed, only does memory allocation when really required */
static String
_XawEscapeActionVarValue(String value)
{
  String escape;

  if (value[0] == '$' || value[0] == '\\')
    {
      escape = XtMalloc(strlen(value) + 2);
      escape[0] = '\\';
      strcpy(escape + 1, value);
      return (escape);
    }
  return (NULL);
}

/* For speed, only does memory allocation when really required */
static String
_XawUnescapeActionVarValue(String value)
{
  String unescape;

  if (value[0] == '\\')
    {
      unescape = XtMalloc(strlen(value));
      strcpy(unescape, value + 1);
      return (unescape);
    }
  return (NULL);
}

static void
XawDeclareActionVar(XawActionVarList *list, String name, String value)
{
  XawActionVar *variable;
  String escape = NULL;

  if (name[0] != XAW_PRIV_VAR_PREFIX)
    {
      char msg[256];

      XmuSnprintf(msg, sizeof(msg), "declare(): variable name must begin with "
		  "\'%c\', at %s = %s", XAW_PRIV_VAR_PREFIX, name, value);
      XtAppWarning(XtWidgetToApplicationContext(list->widget), msg);
      return;
    }
  variable = _XawFindActionVar(list, name);
  if (!variable)
    variable = _XawCreateActionVar(list, name);
  if (value)
    escape = _XawEscapeActionVarValue(value);

  if (variable->qvalue)
    {
      String val = escape ? escape : value;

      if (strcmp(XrmQuarkToString(variable->qvalue), val) == 0)
	{
	  if (escape)
	    XtFree(escape);
	  return;
	}
    }
  variable->qvalue = (escape ? XrmStringToQuark(escape) :
		      (value ? XrmStringToQuark(value) : NULLQUARK));
  if (escape)
    XtFree(escape);
}

static String
XawConvertActionVar(XawActionVarList *list, String name)
{
  XawActionVar *variable;
  String unescape;
  XrmQuark quark;

  if (name[0] != XAW_PRIV_VAR_PREFIX)
    return (name);

  variable = _XawFindActionVar(list, name);
  if (!variable || variable->qvalue == NULLQUARK)
    return (name);
  unescape = _XawUnescapeActionVarValue(XrmQuarkToString(variable->qvalue));
  if (unescape)
    {
      quark = XrmStringToQuark(unescape);
      XtFree(unescape);
    }
  else
    quark = variable->qvalue;

  return (XrmQuarkToString(quark));
}

XawActionVarList *
XawGetActionVarList(Widget w)
{
  XawActionVarList *list;

  list = _XawFindActionVarList(w);
  if (!list)
    list = _XawCreateActionVarList(w);

  return (list);
}

static int
qcmp_action_variable_list(register _Xconst void *left,
			  register _Xconst void *right)
{
  return ((char *)((*(XawActionVarList **)left)->widget) -
	  (char *)((*(XawActionVarList **)right)->widget));
}

static XawActionVarList *
_XawCreateActionVarList(Widget w)
{
  XawActionVarList *list;

#ifdef DIAGNOSTIC
  fprintf(stderr, "(*) Creating action variable list for widget %s (%p)\n",
	  XtName(w), w);
#endif

  list = (XawActionVarList *)XtMalloc(sizeof(XawActionVarList));
  list->widget = w;
  list->num_variables = 0;
  list->variables = NULL;

  if (!variable_list)
    {
      num_variable_list = 1;
      variable_list = (XawActionVarList **)XtMalloc(sizeof(XawActionVarList*));
      variable_list[0] = list;
    }
  else
    {
      ++num_variable_list;
      variable_list = (XawActionVarList **)
	XtRealloc((char *)variable_list,
		  sizeof(XawActionVarList *) * num_variable_list);
      variable_list[num_variable_list - 1] = list;
      qsort(variable_list, num_variable_list, sizeof(XawActionVarList*),
	    qcmp_action_variable_list);
    }

  XtAddCallback(w, XtNdestroyCallback, _XawDestroyActionVarList,
		(XtPointer)list);

  return (list);
}

static int
bcmp_action_variable_list(register _Xconst void *widget,
			  register _Xconst void *list)
{
  return ((char *)widget - (char *)((*(XawActionVarList **)list)->widget));
}

static XawActionVarList *
_XawFindActionVarList(Widget w)
{
  XawActionVarList **list;

  if (!num_variable_list)
    return (NULL);

  list = (XawActionVarList **)bsearch(w, variable_list, num_variable_list,
				      sizeof(XawActionVarList*),
				      bcmp_action_variable_list);

  return (list ? *list : NULL);
}

static int
qcmp_action_variable(register _Xconst void *left,
		     register _Xconst void *right)
{
  return (strcmp(XrmQuarkToString((*(XawActionVar **)left)->qname),
		 XrmQuarkToString((*(XawActionVar **)right)->qname)));
}

static XawActionVar *
_XawCreateActionVar(XawActionVarList *list, String name)
{
  XawActionVar *variable;

#ifdef DIAGNOSTIC
  fprintf(stderr, "(*) Creating action variable '%s' for widget %s (%p)\n",
	  name, XtName(list->widget), list->widget);
#endif

  variable = (XawActionVar *)XtMalloc(sizeof(XawActionVar));
  variable->qname = XrmStringToQuark(name);
  variable->qvalue = NULLQUARK;

  if (!list->variables)
    {
      list->num_variables = 1;
      list->variables = (XawActionVar **)XtMalloc(sizeof(XawActionVar*));
      list->variables[0] = variable;
    }
  else
    {
      ++list->num_variables;
      list->variables = (XawActionVar **)XtRealloc((char *)list->variables,
						   sizeof(XawActionVar *) *
						   list->num_variables);
      list->variables[list->num_variables - 1] = variable;
      qsort(list->variables, list->num_variables, sizeof(XawActionVar*),
	    qcmp_action_variable);
    }
  return (variable);
}

static int
bcmp_action_variable(register _Xconst void *string,
		     register _Xconst void *variable)
{
  return (strcmp((String)string,
		 XrmQuarkToString((*(XawActionVar **)variable)->qname)));
}

static XawActionVar *
_XawFindActionVar(XawActionVarList *list, String name)
{
  XawActionVar **var;

  if (!list->variables)
    return (NULL);

  var = (XawActionVar **)bsearch(name, list->variables, list->num_variables,
				 sizeof(XawActionVar*), bcmp_action_variable);

  return (var ? *var : NULL);
}

/*ARGSUSED*/
static void
_XawDestroyActionVarList(Widget w, XtPointer client_data, XtPointer call_data)
{
  XawActionVarList *list = (XawActionVarList *)client_data;
  Cardinal i;

  for (i = 0; i < num_variable_list; i++)
    if (variable_list[i] == list)
      break;
  if (i >= num_variable_list || list->widget != w
      || variable_list[i]->widget != w)
    {
      XtWarning("destroy-variable-list(): Bad widget argument.");
      return;
    }
  if (--num_variable_list > 0)
    {
      memmove(&variable_list[i], &variable_list[i + 1],
	    (num_variable_list - i) * sizeof(XawActionVarList *));
      variable_list = (XawActionVarList **)
	XtRealloc((char *)variable_list, sizeof(XawActionVarList *) *
		  num_variable_list);
    }
  else
    {
      XtFree((char *)variable_list);
      variable_list = NULL;
    }

  XtFree((char *)list->variables);
  XtFree((char *)list);
}

#endif /* OLDXAW */