pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
/* boolexp.c */

#include "copyrite.h"
#include "config.h"

#include <ctype.h>
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif

#include "mushdb.h"
#include "match.h"
#include "externs.h"
#include "conf.h"
#include "lock.h"
#include "intrface.h"
#include "parse.h"
#include "confmagic.h"

int sizeof_boolexp _((struct boolexp * b));
int eval_boolexp _((dbref player, struct boolexp * b, dbref target, int nrecurs, lock_type ltype));
static void skip_whitespace _((void));
static struct boolexp *test_atr _((char *s, char c));
static struct boolexp *parse_boolexp_L _((void));
static struct boolexp *parse_boolexp_O _((void));
static struct boolexp *parse_boolexp_C _((void));
static struct boolexp *parse_boolexp_I _((void));
static struct boolexp *parse_boolexp_A _((void));
static struct boolexp *parse_boolexp_F _((void));
static struct boolexp *parse_boolexp_T _((void));
static struct boolexp *parse_boolexp_E _((void));
struct boolexp *parse_boolexp _((dbref player, const char *buf));
static int check_attrib_lock _((dbref player, dbref target, const char *atrname, const char *str));

int
sizeof_boolexp(b)
    struct boolexp *b;
{
  if (!b || b == TRUE_BOOLEXP)
    return 0;
  switch (b->type) {
  case BOOLEXP_CONST:
    return sizeof(struct boolexp);
  case BOOLEXP_IND:
  case BOOLEXP_NOT:
  case BOOLEXP_IS:
  case BOOLEXP_CARRY:
  case BOOLEXP_OWNER:
    return sizeof(struct boolexp) + sizeof_boolexp(b->sub1);
  case BOOLEXP_AND:
  case BOOLEXP_OR:
    return sizeof(struct boolexp) +
     sizeof_boolexp(b->sub1) + sizeof_boolexp(b->sub2);
  case BOOLEXP_ATR:
  case BOOLEXP_EVAL:
    return sizeof(struct boolexp) + sizeof(struct boolatr) +
     strlen(b->atr_lock->name) + 1 +
     strlen(b->atr_lock->text) + 1;
  }
  /* Broken lock */
  return sizeof(struct boolexp);
}

int
eval_boolexp(player, b, target, nrecurs, ltype)
    dbref player;		/* The player trying to pass */
    struct boolexp *b;		/* The boolexp */
    dbref target;		/* The object with the lock */
    int nrecurs;		/* Recursion depth */
    lock_type ltype;
{
  ATTR *a;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];

  if (!GoodObject(player))
    return 0;

  if (nrecurs > MAX_DEPTH) {
    notify(player, "Sorry, broken lock!");
    return 0;
  }
  if (!b)
    return 1;
  if (b == TRUE_BOOLEXP) {
    return 1;
  } else {
    switch (b->type) {
    case BOOLEXP_IND:
      if (b->sub1->type != BOOLEXP_CONST)
	return 0;
      /* We only allow evaluation of indirect locks if target can run
       * the lock on the referenced object.
       */
      if (!Can_Run_Lock(target, b->sub1->thing, ltype))
	return 0;
      nrecurs++;
      return (eval_boolexp(player, getlock(b->sub1->thing, ltype),
			   b->sub1->thing, nrecurs, ltype));
    case BOOLEXP_AND:
      return (eval_boolexp(player, b->sub1, target, nrecurs, ltype)
	      && eval_boolexp(player, b->sub2, target, nrecurs, ltype));
    case BOOLEXP_OR:
      return (eval_boolexp(player, b->sub1, target, nrecurs, ltype)
	      || eval_boolexp(player, b->sub2, target, nrecurs, ltype));
    case BOOLEXP_NOT:
      return !eval_boolexp(player, b->sub1, target, nrecurs, ltype);
    case BOOLEXP_CONST:
      return (b->thing == player
	      || member(b->thing, db[player].contents));
    case BOOLEXP_IS:
      return (b->sub1->thing == player);
    case BOOLEXP_CARRY:
      return (member(b->sub1->thing, db[player].contents));
    case BOOLEXP_OWNER:
      return (Owner(b->sub1->thing) == Owner(player));
    case BOOLEXP_ATR:
      a = atr_complete_match(player, b->atr_lock->name, target);
      if (!a)
	return 0;
      strcpy(tbuf1, uncompress(b->atr_lock->text));
      strcpy(tbuf2, uncompress(a->value));
      return local_wild_match(tbuf1, tbuf2);
    case BOOLEXP_EVAL:
      strcpy(tbuf1, uncompress(b->atr_lock->text));
      return check_attrib_lock(player, target, b->atr_lock->name, tbuf1);
    default:
      do_log(LT_ERR, 0, 0, "Bad boolexp type %d in object #%d",
	     b->type, b->thing);
      report();
      return 0;
    }				/* switch */
    /* should never be reached */
    do_log(LT_ERR, 0, 0, "Broken lock type %s in object called by #%d",
	   ltype, player);
    return 0;
  }				/* else */
}

/* If the parser returns TRUE_BOOLEXP, you lose */
/* TRUE_BOOLEXP cannot be typed in by the user; use @unlock instead */
static const char *parsebuf;
static dbref parse_player;

static void
skip_whitespace()
{
  while (*parsebuf && isspace(*parsebuf))
    parsebuf++;
}


#ifdef CAN_NEWSTYLE
static struct boolexp *
test_atr(char *s, char c)
#else
static struct boolexp *
test_atr(s, c)
    char *s;
    char c;
#endif
{
  struct boolexp *b;
  char tbuf1[BUFFER_LEN];

  strcpy(tbuf1, s);
  for (s = tbuf1; *s && (*s != c); s++) ;
  if (!*s)
    return (0);
  *s++ = 0;
  if (strlen(tbuf1) == 0)
    return (0);
  b = alloc_bool();

  if (c == ':')
    b->type = BOOLEXP_ATR;
  else
    b->type = BOOLEXP_EVAL;

  /* !!! Possible portability problem!!!!! */
  b->atr_lock = alloc_atr(tbuf1, s);
  return (b);
}

/* L -> (E); L -> object identifier */
static struct boolexp *
parse_boolexp_L()
{
  struct boolexp *b;
  char *p;
  char tbuf1[BUFFER_LEN];

  skip_whitespace();
  switch (*parsebuf) {
  case '(':
    parsebuf++;
    b = parse_boolexp_E();
    skip_whitespace();
    if (b == TRUE_BOOLEXP || *parsebuf++ != ')') {
      free_boolexp(b);
      return TRUE_BOOLEXP;
    } else {
      return b;
    }
    /* break; */
  default:
    /* must have hit an object ref */
    /* load the name into our buffer */
    p = tbuf1;
    while (*parsebuf
	   && *parsebuf != AND_TOKEN
	   && *parsebuf != OR_TOKEN
	   && *parsebuf != ')') {
      *p++ = *parsebuf++;
    }
    /* strip trailing whitespace */
    *p-- = '\0';
    while (isspace(*p))
      *p-- = '\0';

    /* check for an attribute */
    b = test_atr(tbuf1, ':');
    if (b)
      return (b);

    /* check for an eval */
    b = test_atr(tbuf1, '/');
    if (b)
      return b;

    b = alloc_bool();
    b->type = BOOLEXP_CONST;

    /* do the match */
    b->thing = match_result(parse_player, tbuf1, TYPE_THING, MAT_EVERYTHING);

    if (b->thing == NOTHING) {
      notify(parse_player,
	     tprintf("I don't see %s here.", tbuf1));
      free_bool(b);
      return TRUE_BOOLEXP;
    } else if (b->thing == AMBIGUOUS) {
      notify(parse_player,
	     tprintf("I don't know which %s you mean!", tbuf1));
      free_bool(b);
      return TRUE_BOOLEXP;
    } else {
      return b;
    }
    /* break */
  }
}

/* O -> $Identifier ; O -> L */
static struct boolexp *
parse_boolexp_O()
{
  struct boolexp *b2;
  skip_whitespace();
  if (*parsebuf == OWNER_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_OWNER;
    if (((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
	(b2->sub1->type != BOOLEXP_CONST)) {
      free_boolexp(b2);
      return (TRUE_BOOLEXP);
    } else
      return (b2);
  }
  return (parse_boolexp_L());
}

/* C -> +Identifier ; C -> O */
static struct boolexp *
parse_boolexp_C()
{
  struct boolexp *b2;
  skip_whitespace();
  if (*parsebuf == IN_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_CARRY;
    if (((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
	(b2->sub1->type != BOOLEXP_CONST)) {
      free_boolexp(b2);
      return (TRUE_BOOLEXP);
    } else
      return (b2);
  }
  return (parse_boolexp_O());
}

/* I -> =Identifier ; I -> C */
static struct boolexp *
parse_boolexp_I()
{
  struct boolexp *b2;
  skip_whitespace();
  if (*parsebuf == IS_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_IS;
    if (((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
	(b2->sub1->type != BOOLEXP_CONST)) {
      free_boolexp(b2);
      return (TRUE_BOOLEXP);
    } else
      return (b2);
  }
  return (parse_boolexp_C());
}

/* A -> @L; A -> I */
static struct boolexp *
parse_boolexp_A()
{
  struct boolexp *b2;
  skip_whitespace();
  if (*parsebuf == AT_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_IND;
    if (((b2->sub1 = parse_boolexp_L()) == TRUE_BOOLEXP) ||
	(b2->sub1->type != BOOLEXP_CONST)) {
      free_boolexp(b2);
      return (TRUE_BOOLEXP);
    } else
      return (b2);
  }
  return (parse_boolexp_I());
}

/* F -> !F;F -> A */
static struct boolexp *
parse_boolexp_F()
{
  struct boolexp *b2;
  skip_whitespace();
  if (*parsebuf == NOT_TOKEN) {
    parsebuf++;
    b2 = alloc_bool();
    b2->type = BOOLEXP_NOT;
    if ((b2->sub1 = parse_boolexp_F()) == TRUE_BOOLEXP) {
      free_boolexp(b2);
      return (TRUE_BOOLEXP);
    } else
      return (b2);
  }
  return (parse_boolexp_A());
}


/* T -> F; T -> F & T */
static struct boolexp *
parse_boolexp_T()
{
  struct boolexp *b;
  struct boolexp *b2;
  if ((b = parse_boolexp_F()) == TRUE_BOOLEXP) {
    return b;
  } else {
    skip_whitespace();
    if (*parsebuf == AND_TOKEN) {
      parsebuf++;

      b2 = alloc_bool();
      b2->type = BOOLEXP_AND;
      b2->sub1 = b;
      if ((b2->sub2 = parse_boolexp_T()) == TRUE_BOOLEXP) {
	free_boolexp(b2);
	return TRUE_BOOLEXP;
      } else {
	return b2;
      }
    } else {
      return b;
    }
  }
}

/* E -> T; E -> T | E */
static struct boolexp *
parse_boolexp_E()
{
  struct boolexp *b;
  struct boolexp *b2;
  if ((b = parse_boolexp_T()) == TRUE_BOOLEXP) {
    return b;
  } else {
    skip_whitespace();
    if (*parsebuf == OR_TOKEN) {
      parsebuf++;

      b2 = alloc_bool();
      b2->type = BOOLEXP_OR;
      b2->sub1 = b;
      if ((b2->sub2 = parse_boolexp_E()) == TRUE_BOOLEXP) {
	free_boolexp(b2);
	return TRUE_BOOLEXP;
      } else {
	return b2;
      }
    } else {
      return b;
    }
  }
}

struct boolexp *
parse_boolexp(player, buf)
    dbref player;
    const char *buf;
{
  parsebuf = buf;
  parse_player = player;
  return parse_boolexp_E();
}

static int
check_attrib_lock(player, target, atrname, str)
    dbref player;
    dbref target;
    const char *atrname;
    const char *str;
{
  /* player is attempting to pass the lock on target, which has
   * an attribute lock of the form  "atrname/value".
   * This lock is passed if target performing get_eval(target/atrname)
   * would yield value. This does NOT do a wildcard
   * match. It is also case-sensitive for matching.
   */

  ATTR *a;
  char const *asave, *ap;
  char buff[BUFFER_LEN], *bp;
  char *preserve[10];

  if (!atrname || !*atrname || !str || !*str)
    return 0;

  /* fail if there's no matching attribute */
  a = atr_get(target, strupper(atrname));
  if (!a)
    return 0;
  if (!Can_Read_Attr(target, target, a))
    return 0;
  asave = safe_uncompress(a->value);

  /* perform pronoun substitution */
  save_global_regs("check_attrib_lock_save", preserve);
  bp = buff;
  ap = asave;
  process_expression(buff, &bp, &ap, target, player, player,
		     PE_DEFAULT, PT_DEFAULT, NULL);
  *bp = '\0';
  restore_global_regs("check_attrib_lock_save", preserve);
  free((Malloc_t) asave);

  return !strcmp(buff, str);
}