/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
*/
/*
 * AtFStk -- Attribute Filesystem Toolkit Library
 *
 * bind_rules.c -- AtFS toolkit library (Version Binding)
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: bind_rules.c[7.0] Thu Mar 10 12:23:24 1994 andy@cs.tu-berlin.de frozen $
 */

#include <ctype.h>
#include "atfs.h"
#include "sttk.h"
#include "atfstk.h"
#include "bind.h"

/*=========================
 *  rules list etc.
 *=========================*/

#define RULE_SEG_SIZE 64

LOCAL BindRule *bindRules;
LOCAL int      ruleCount=0, ruleListSize=0;

EXPORT char *(*atBindExpandMacro)();

EXPORT int atBindDisplayErrors = FALSE;

/*=========================
 *  atBindInitRules
 *=========================*/

LOCAL int rulesInitialized = FALSE;

EXPORT int atBindInitRules ()
{
  struct stat inodeBuf;
  char *envPtr, ruleFileName[PATH_MAX];

  if (rulesInitialized)
    return (TRUE);
  else
    rulesInitialized = TRUE;

  if (!(envPtr = getenv (ST_ENV)))
    envPtr = ST_ENV_DEFAULT;

  strcpy (ruleFileName, envPtr);
  strcat (ruleFileName, ST_ENV_SHAPELIB);
  strcat (ruleFileName, "/");
  strcat (ruleFileName, BIND_RULES_FILENAME);
    
  if (stat (ruleFileName, &inodeBuf) != -1)
    return (atBindRuleFile (ruleFileName));

  return (TRUE); /* no file found, this is ok */
}

/*=========================
 *  atBindShowRules
 *=========================*/

EXPORT int atBindShowRules ()
{
  int i;

  atBindInitRules ();

  stLog ("# The following rule section is a atBind rule database dump !", ST_LOG_MSG);

  for (i=0; i<ruleCount; i++)
    atRuleDisplay (&bindRules[i]);

  fflush (stdout);
  return (TRUE);
}

/*=========================
 *  atBindListRules
 *=========================*/

EXPORT char **atBindListRules ()
{
  int i;
  char **list;

  atBindInitRules ();

  if ((list = (char **)malloc ((ruleCount+1) * sizeof (char*))) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (NULL);
  }
  for (i=0; i<ruleCount; i++)
    list[i] = bindRules[i].name;
  list[i] = NULL;
  return (list);
}

/*=========================
 *  bindAddRule
 *=========================*/

EXPORT int atBindAddRule (name, body, source, sourceLine)
     char *name;
     char *body;
     char *source;
     int  sourceLine;
{
  char *sourceBuf;

  atBindError = FALSE;
  atBindInitRules ();

  if (ruleCount == ruleListSize) {
    if (ruleListSize == 0)
      bindRules = (BindRule *)malloc ((size_t) RULE_SEG_SIZE * sizeof (BindRule));
    else
      bindRules = (BindRule *)realloc (bindRules,
		  (size_t) (ruleListSize + RULE_SEG_SIZE) * sizeof (BindRule));
    if (!bindRules) {
      atBindError = TRUE;
       strcpy (atBindErrorMsg, "not enough memory");
      return (FALSE);
    }
    ruleListSize += RULE_SEG_SIZE;
  }

  /* remove leading whitespace */
  while (isspace (*name)) name++;

  /* initialize name */
  if ((bindRules[ruleCount].name = malloc ((size_t) strlen (name)+1)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (FALSE);
  }
  strcpy (bindRules[ruleCount].name, name);

  
  /* initialize body */
  if ((bindRules[ruleCount].body = malloc ((size_t) strlen (body)+1)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (FALSE);
  }
  strcpy (bindRules[ruleCount].body, body);

  if ((sourceBuf = malloc ((size_t) strlen (source)+1)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (FALSE);
  }
  strcpy (sourceBuf, source);

  bindRules[ruleCount].fileName = sourceBuf;
  bindRules[ruleCount].srcLine = sourceLine;
  ruleCount++;

  return (atRuleScan (&bindRules[ruleCount-1]));
}

/*=========================
 *  atBindDelRule
 *=========================*/

EXPORT int atBindDelRule (name)
     char *name;
{
  int i;
  char *parenthPtr = strchr (name, '(');

  atBindInitRules ();

  if (parenthPtr)
    *parenthPtr = '\0';

  for (i=0; i<ruleCount; i++)
    if (!strcmp (bindRules[i].name, name))
      break;
  
  if (parenthPtr)
    *parenthPtr = '(';

  if (i == ruleCount) /* rule not found */
    return (FALSE);

  bindRules[i].name = NULL; /* free ? */
  bindRules[i].body = NULL; /* free ? */
  
  for (i++; i<ruleCount; i++)
    bindRules[i-1] = bindRules[i];

  ruleCount--;
  return (TRUE);
}

/*=========================
 *  atBindTestRule
 *=========================*/

EXPORT int atBindTestRule (name)
     char *name;
{
  int i;
  char *parenthPtr = strchr (name, '(');

  atBindInitRules ();

  if (parenthPtr)
    *parenthPtr = '\0';

  for (i=ruleCount-1; i>=0; i--) {
    if (!strcmp (bindRules[i].name, name)) {
      if (parenthPtr)
	*parenthPtr = '(';
      return (TRUE);
    }
  }
  if (parenthPtr)
    *parenthPtr = '(';
  return (FALSE);
}

/*=========================
 *  atBindRuleFile
 *=========================*/

#define nextLine(p) { while (*p && (*p++ != '\n')); lineNo++; }

LOCAL int scanFile (ruleBuf)
     char *ruleBuf;
{
  char *rulePtr = ruleBuf;
  int  lineNo = 1;

  if (!rulePtr || !*rulePtr)
    return (TRUE);

  /* ToDo: scan Shapefiles */

  while (*rulePtr) { 
    if (*rulePtr == '\\') {
      rulePtr++;
      rulePtr++;
      continue;
    }
    if (*rulePtr == '#') {
      while (*rulePtr) {
	if (*rulePtr == '\\') {
	  *rulePtr++ = ' ';
	  *rulePtr++ = ' ';
	  continue;
	}
	if (*rulePtr == '\n')
	  break;
	*rulePtr++ = ' ';
      }
    }
    else
      rulePtr++;
  }

  rulePtr = ruleBuf;
  while (1) {
    if (!*rulePtr)
      return (TRUE);

    if (ruleCount == ruleListSize) {
      if (ruleListSize == 0)
	bindRules = (BindRule *)malloc ((size_t) RULE_SEG_SIZE * sizeof (BindRule));
      else
	bindRules = (BindRule *)realloc (bindRules,
		    (size_t) (ruleListSize + RULE_SEG_SIZE) * sizeof (BindRule));
      if (!bindRules) {
	atBindError = TRUE;
	strcpy (atBindErrorMsg, "not enough memory");
	return (FALSE);
      }
      ruleListSize += RULE_SEG_SIZE;
    }

    /* lookout for rule name */
    if (isgraph (*rulePtr)) {
      bindRules[ruleCount].name = rulePtr;
      bindRules[ruleCount].srcLine = lineNo+1;
      while (*rulePtr && (*rulePtr != ':') && (*rulePtr != '\n'))
	rulePtr++;
      if (*rulePtr) {
	if (*rulePtr == '\n') {
	  *rulePtr++ = '\0';
	  lineNo++;
	}
	else {
	  *rulePtr++ = '\0';
	  nextLine (rulePtr);
	}
      }
      else {
	bindRules[ruleCount++].body = (char *)0;
	return (TRUE);
      }
    }
    else {
      nextLine (rulePtr);
      continue;
    }

    /* get rule body */
    if (!*rulePtr || (*rulePtr != '\t')) {
      bindRules[ruleCount++].body = (char *)0;
      continue;
    }

    bindRules[ruleCount++].body = rulePtr;

    while (*rulePtr == '\t')
      nextLine(rulePtr);

    if (!*rulePtr)
      return (TRUE);

    *(rulePtr-1) = '\0';
  }
}

EXPORT int atBindRuleFile (fileName)
     char *fileName;
{
  Af_key *ruleFileKey;
  FILE *ruleFile;
  char *ruleBuf, *fileNameBuf;
  int  i, retCode = TRUE;
  size_t fileSize;

  atBindError = FALSE;
  atBindInitRules ();

  if ((ruleFileKey = atBindVersion (fileName, "[]")) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "cannot find ");
    strcat (atBindErrorMsg, fileName);
    return (FALSE);
  }

  fileSize = af_retnumattr (ruleFileKey, AF_ATTSIZE);

  if ((ruleBuf = malloc (fileSize+1)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (FALSE);
  }

  if ((ruleFile = af_open (ruleFileKey, "r")) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "cannot open ");
    strcat (atBindErrorMsg, fileName);
    return (FALSE);
  }

  fread (ruleBuf, sizeof(char), fileSize, ruleFile);
  ruleBuf[fileSize] = '\0';

  af_close (ruleFile);

  if ((fileNameBuf = malloc ((size_t) strlen (fileName)+1)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (FALSE);
  }
  strcpy (fileNameBuf, fileName);

  i = ruleCount;
  retCode = scanFile (ruleBuf);
  for (; i<ruleCount; i++) {
    bindRules[i].fileName = fileNameBuf;
    if (!atRuleScan (&bindRules[i]))
      retCode = FALSE;
  }
  return (retCode);
}

/*=======================================
 *  rule processing and macro expansion
 *=======================================*/

typedef struct procRule ProcRule;

struct procRule {
  char        *targName;
  int         firstRule;
  int         ruleTabIdx;
  char        *ruleArgs[BIND_MAX_RULEARGS];
  Alternative *procAlt;
  int         firstAlt;
  Predicate   *procPred;
  int         firstPred;
  ProcRule    *next;
};

ProcRule *firstEval = NULL, *curEval = NULL;

#define needSpace(ns) if ((newSize += ns) >= maxSize) { \
			maxSize += (ns+255); \
			if ((newString = realloc (newString, maxSize)) == NULL) { \
			  atBindError = TRUE; \
			  strcpy (atBindErrorMsg, "not enough memory"); \
			  return (NULL); } }

LOCAL char *ruleExpandMacro (str, hitSet)
     char *str;
     Af_set *hitSet;
{
  char   *newString;
  char   *ptr = str, *newPtr;
  size_t newSize = 0, maxSize = strlen(str)+256;

  char   attrNameBuf[256], *attrNamePtr, *attrBuf, numBuf[16];
  int    attrLen, i, hitCount = af_nrofkeys(hitSet);


  if ((newString = malloc (maxSize)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (NULL);
  }
  
  newPtr = newString;
  
  while ((*newPtr = *ptr)) {
    needSpace (1);
    if (*ptr != '$') {
      newPtr++;
      ptr++;
      continue;
    }

    /* check if dollar sign was escaped */
    if ((ptr > str) && *(ptr-1) == '\\') {
      newPtr++;
      ptr++;
      continue;
    }

    ptr++;

    if (*ptr == '+') {
      int targNameLen = strlen(curEval->targName);
      ptr++;
      needSpace (targNameLen);
      strncpy (newPtr, curEval->targName, targNameLen);
      newPtr += targNameLen;
      continue;
    }
    if (*ptr == '=') {
      char numBuf[16];
      int  numLen;
      ptr++;
      sprintf (numBuf, "%d", hitCount);
      numLen = strlen (numBuf);
      needSpace (numLen);
      strncpy (newPtr, numBuf, numLen);
      newPtr += numLen;
      continue;
    }
    if (*ptr != '_') {
      newPtr++;
      continue;
    }

    ptr++;

    if (!strncmp (ptr, "hits", 4)) {
      ptr += 4;
      if (*ptr == '$') ptr++;
      sprintf (numBuf, "%d", hitCount);
      attrLen = strlen (numBuf);
      needSpace (attrLen);
      strncpy (newPtr, numBuf, attrLen);
      newPtr += attrLen;
      continue;
    }
    if (!strncmp (ptr, "rule", 4)) {
      ptr += 4;
      if (*ptr == '$') ptr++;
      attrLen = strlen (bindRules[curEval->ruleTabIdx].name);
      needSpace (attrLen);
      strncpy (newPtr, bindRules[curEval->ruleTabIdx].name, attrLen);
      newPtr += attrLen;
      continue;
    }
    if (!strncmp (ptr, "target", 6)) {
      ptr += 6;
      if (*ptr == '$') ptr++;
      attrLen = strlen (curEval->targName);
      needSpace (attrLen);
      strncpy (newPtr, curEval->targName, attrLen);
      newPtr += attrLen;
      continue;
    }

    /* else: regular attribute citation or rule parameter */
    attrNamePtr = attrNameBuf;
    while (*ptr && (*ptr != '$') && (*ptr != ',') && (*ptr != ')') && !isspace(*ptr))
      *attrNamePtr++ = *ptr++;
    *attrNamePtr = '\0';

    attrBuf = NULL;
    for (i=0; i<bindRules[curEval->ruleTabIdx].argNum; i++) {
      if (!strcmp (bindRules[curEval->ruleTabIdx].argList[i], attrNameBuf))
	attrBuf = curEval->ruleArgs[i];
    }

    if (!attrBuf && (hitCount == 1)) {
      Af_key aso;
      if (af_setgkey (hitSet, 0, &aso) == -1) {
	atBindError = TRUE;
	strcpy (atBindErrorMsg, af_errmsg ("ruleExpandMacro"));
	return (NULL);
      }
      attrBuf = af_retattr (&aso, attrNameBuf);
      af_dropkey (&aso);
    }
    if (attrBuf) {
      if (*ptr == '$') ptr++;
      attrLen = strlen (attrBuf);
      needSpace (attrLen);
      strncpy (newPtr, attrBuf, attrLen);
      newPtr += attrLen;
    }
    else {
      attrLen = strlen (attrNameBuf);
      needSpace (attrLen+2);
      strncpy (newPtr, "$_", 2);
      newPtr += 2;
      strncpy (newPtr, attrNameBuf, attrLen);
      newPtr += attrLen;
      if (*ptr == '$') {
	ptr++;
	needSpace (1);
	*newPtr++ = '$';
      }
    }
  }
  needSpace (1);
  *newPtr++ = '\0';
  return (newString);
}

LOCAL char *ruleExpandCommand (str)
     char *str;
{
  char   *newString, *newPtr, *endPtr, *ptr = str;
  char   resultBuf[10240];
  size_t newSize = 0, maxSize = 256;
  int    resultLen;
  FILE   *fileDes;

  if ((newString = malloc (maxSize)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (NULL);
  }

  newPtr = newString;

  while ((*newPtr = *ptr)) {
    needSpace (1);
    if (*ptr != '`') {
      newPtr++;
      ptr++;
      continue;
    }

    ptr++;
    if ((endPtr = strchr (ptr, '`')) == NULL) {
      newPtr++;
      continue;
    }
    *endPtr = '\0';

    if ((fileDes = popen (ptr, "r")) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "error during popen");
      return (NULL);
    }

    if (fgets (resultBuf, 10240, fileDes) == NULL) {
      resultBuf[0] = '\0';
    }
    resultBuf[10240-1] = '\0';
    pclose(fileDes);

    resultLen = strlen (resultBuf);
    needSpace (resultLen);
    strncpy (newPtr, resultBuf, resultLen);
    newPtr += resultLen;

    *endPtr = '`';
    ptr = endPtr+1;
  }

  return (newString);
}

/*======================================
 *  set new rules and restore old ones
 *======================================*/

EXPORT char *atCurRuleName ()
{
  return (bindRules[curEval->ruleTabIdx].name);
}

EXPORT int atBindSetRule (ruleName, targName, init)
     char *ruleName;
     char *targName;
     int  init;      /* initial rule setting if TRUE */
     /* Returns rule status 0 - ok, 1-32 - rule error code,
	                    -1 internal error, -2 not found */
{
  /* set "ruleName" as version bind rule to be applied.
     This initializes the global variable "curEval" subsequently
     accessed by atBindRuleAlt. It realizes a stack of rules,
     represented as chained list, with "firstEval" as anchor and
     "curEval" as top. This stack has two reasons. First, a recursive
     evaluation due to "condexist|not|uniq" may use different selection
     rules, second the "rule" attribute may temporarily switch to 
     another rule for the same name. In the first case, the "init"
     argument should be TRUE, indicating that this is the initial rule
     for the current selection. In the second case, "init" should be
     FALSE.
   */

  char *rulePtr, *myRuleName;
  ProcRule *prevEval = curEval;
  int i;

  if (firstEval) {
    if ((curEval->next = (ProcRule *)malloc ((size_t) sizeof (ProcRule))) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "not enough memory");
      return (-1);
    }
    curEval = curEval->next;
  }
  else {
    if ((firstEval = (ProcRule *)malloc ((size_t) sizeof (ProcRule))) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "not enough memory");
      return (-1);
    }
    curEval = firstEval;
  }

  if ((myRuleName = malloc ((size_t) strlen (ruleName)+1)) == NULL) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, "not enough memory");
    return (-1);
  }
  strcpy (myRuleName, ruleName);
  rulePtr = myRuleName;

  curEval->next = NULL;
  curEval->targName = targName;
  /* leave a marker on the stack, when a new evaluation starts. */
  curEval->firstRule = init;

  /* Cut off arguments (in parenthesis) from the rule name.
     When there are no arguments, "rulePtr" will be NULL.
     Otherwise, "rulePtr" points to the open parenthesis in "myRuleName"
     which is replaced by a \0 rtemporarily.
   */
  while (isgraph (*rulePtr) && (*rulePtr != '(')) rulePtr++;
  if (*rulePtr) {
    if (*rulePtr == '(')
      *rulePtr++ = '\0';
    else {
      while (isspace (*rulePtr)) *rulePtr++ = '\0';
      if (*rulePtr == '(')
	*rulePtr++ = '\0';
      else
	rulePtr = NULL;
    }
  }
  else
    rulePtr = NULL;

  for (curEval->ruleTabIdx=ruleCount-1;
       curEval->ruleTabIdx>=0; curEval->ruleTabIdx--) {
    if (!strcmp (bindRules[curEval->ruleTabIdx].name, myRuleName)) {
      curEval->procAlt = bindRules[curEval->ruleTabIdx].altList;
      curEval->firstAlt = TRUE;
      break;
    }
  }
  if (curEval->ruleTabIdx < 0) {
    curEval = prevEval;
    if (curEval) {
      free (curEval->next);
      curEval->next = NULL;
    }
    else {
      free (firstEval);
      firstEval = NULL;
    }
    return (-2);
  }

  /* expect bindRules[curEval->ruleTabIdx].argNum arguments */
  i=0;
  if (bindRules[curEval->ruleTabIdx].argNum && rulePtr) {
    while (1) {
      while (isspace (*rulePtr)) rulePtr++;
      curEval->ruleArgs[i++] = rulePtr;
      if (i == BIND_MAX_RULEARGS) break;
      while (isgraph (*rulePtr) && (*rulePtr != ',') && (*rulePtr != ')'))
	rulePtr++;
      while (isspace (*rulePtr)) *rulePtr++ = '\0';
      if (!*rulePtr) break;
      if (*rulePtr == ')') {
	*rulePtr = '\0';
	break;
      }
      *rulePtr++ = '\0';
    }
  }
  /* initialize remaining arguments, if there are any */
  for (;i < bindRules[curEval->ruleTabIdx].argNum; i++)
    curEval->ruleArgs[i] = "";

  return (bindRules[curEval->ruleTabIdx].status);
}

EXPORT void atBindUnsetRule (abort)
     int abort;
     /* the abort argument says, if the whole selection is to be aborted
	-> (TRUE) all rules back to "firstRule" should be popped off the
	stack or just -> (FALSE) the last rule will be popped off.
      */
{
  if (curEval == firstEval) {
    free (firstEval);
    firstEval = curEval = NULL;
  }
  else {
    ProcRule *endPtr = firstEval, *basePtr = firstEval;
    while (endPtr->next->next) {
      endPtr = endPtr->next;
      if (endPtr->next->firstRule)
	basePtr = endPtr;
    }
    if (abort)
      curEval = basePtr;
    else
      curEval = endPtr;

    /* free rest of chained list */
    endPtr = curEval->next;
    while (endPtr) {
      basePtr = endPtr;
      endPtr = endPtr->next;
      free (basePtr);
    }
    curEval->next = NULL;
  }
}


EXPORT char *atBindRuleAlt (hitSet)
     Af_set *hitSet;
{
  char *tmpPattern;

  if (!curEval->firstAlt && curEval->procAlt)
    curEval->procAlt = curEval->procAlt->next;
  else
    curEval->firstAlt = FALSE;

  while (!curEval->procAlt && !curEval->firstRule) {
    /* if rule to be processed is done and there is a previous rule, restore it */
    atBindUnsetRule (FALSE);
    curEval->procAlt = curEval->procAlt->next;
  }

  if (curEval->procAlt) {
    curEval->procPred = curEval->procAlt->predList;
    curEval->firstPred = TRUE;
    if (curEval->procAlt->pattern) { 
      tmpPattern = curEval->procAlt->pattern;
      if (curEval->procAlt->info & BIND_PATTERN_RULE_MACRO)
	tmpPattern = ruleExpandMacro (tmpPattern, hitSet);
      if ((curEval->procAlt->info & BIND_PATTERN_SHAPE_MACRO) && (atBindExpandMacro))
	tmpPattern = atBindExpandMacro (tmpPattern);
      if (curEval->procAlt->info & BIND_PATTERN_SHELL_COMMAND)
	tmpPattern = ruleExpandCommand (tmpPattern);
      return (tmpPattern);
    }
    else
      return ("*");
  }
  else
    return (NULL);
}

EXPORT int atBindRulePred (arg1, arg2, hitSet)
     char **arg1;
     char **arg2;
     Af_set *hitSet;
{
  char *tmpArg;

  if (!curEval->firstPred && curEval->procPred)
    curEval->procPred = curEval->procPred->next;
  else
    curEval->firstPred = FALSE;

  if (curEval->procPred) {
    tmpArg = curEval->procPred->args[0];
    if (curEval->procPred->info & BIND_ARG1_RULE_MACRO)
      tmpArg = ruleExpandMacro (tmpArg, hitSet);
    if ((curEval->procPred->info & BIND_ARG1_SHAPE_MACRO) && (atBindExpandMacro))
      tmpArg = atBindExpandMacro (tmpArg);
    if (curEval->procPred->info & BIND_ARG1_SHELL_COMMAND)
      tmpArg = ruleExpandCommand (tmpArg);
    *arg1 = tmpArg;
    tmpArg = curEval->procPred->args[1];
    if (curEval->procPred->info & BIND_ARG2_RULE_MACRO)
      tmpArg = ruleExpandMacro (tmpArg, hitSet);
    if ((curEval->procPred->info & BIND_ARG2_SHAPE_MACRO) && (atBindExpandMacro))
      tmpArg = atBindExpandMacro (tmpArg);
    if (curEval->procPred->info & BIND_ARG2_SHELL_COMMAND)
      tmpArg = ruleExpandCommand (tmpArg);
    *arg2 = tmpArg;
    return (curEval->procPred->position);
  }
  else
    return (-1);
}
