dbm/
misc/
old-docs/
/* select.c */

#include "copyright.h"
#include "config.h"

#include <stdio.h>
#ifdef STRING_H
#include <string.h>
#else
#include <strings.h>
#endif				/* STRING_H */
#include <ctype.h>

#include "teeny.h"
#include "dbm.h"

/*
 * Implements the select command for the dbm.
 * 
 */

static char     toocomplex[] = "Expression too complex.\n";

int             stack[64];	/* We only stack operators, so don't need
				 * full atoms here. */

char           *get_tok();

int             sel_handle(str, ex, required)
  char           *str;
  atom           *ex;
  int            *required;
{
  int             wp, sp;
  int             tok;
  int             bad_ex;
  char           *p, foo;

  sp = 0;
  wp = 0;
  bad_ex = 0;
  *required = 0;
  ex[0].type = STOP;

  do {
    str = get_tok(str, &tok);

    switch (tok) {
    case BADTOK:
      printf("Bad token.\n");
      bad_ex = 1;
      break;
    case OPEN:
      if (sp < 63) {
	stack[sp++] = OPEN;
      } else {
	bad_ex = 1;
	printf(toocomplex);
	break;
      }
      break;
    case CLOSE:
      while (sp > 0 && stack[sp - 1] != OPEN && wp < (MAXEXPR - 1)) {
	ex[wp++].type = stack[--sp];
      }
      if (sp == 0 || stack[sp - 1] != OPEN) {
	bad_ex = 1;
	printf("Too complex, or unbalanced parens.\n");
	break;
      }
      sp--;
      break;
    case AND:
      while (sp > 0 && stack[sp - 1] == NOT && wp < (MAXEXPR - 1)) {
	ex[wp++].type = stack[--sp];
      }
      if (wp == (MAXEXPR - 1)) {
	printf(toocomplex);
	bad_ex = 1;
	break;
      }
      if (sp < 63) {
	stack[sp++] = AND;
      } else {
	printf(toocomplex);
	bad_ex = 1;
	break;
      }
      break;
    case OR:
      while (sp > 0 && (stack[sp - 1] == NOT
			|| stack[sp - 1] == AND) && wp < (MAXEXPR - 1)) {
	ex[wp++].type = stack[--sp];
      }
      if (wp == (MAXEXPR - 1)) {
	printf(toocomplex);
	bad_ex = 1;
	break;
      }
      if (sp < 63) {
	stack[sp++] = OR;
      } else {
	printf(toocomplex);
	bad_ex = 1;
	break;
      }
      break;
    case NOT:
      if (sp < 63) {
	stack[sp++] = NOT;
      } else {
	printf(toocomplex);
	bad_ex = 1;
	break;
      }
      break;
    case STOP:
      while (sp > 0 && wp < (MAXEXPR - 1)) {
	if (stack[sp - 1] != AND && stack[sp - 1] != OR
	    && stack[sp - 1] != NOT) {
	  bad_ex = 1;
	  break;
	}
	ex[wp++].type = stack[--sp];
      }
      if (wp == (MAXEXPR - 1)) {
	printf(toocomplex);
	bad_ex = 1;
	break;
      }
      ex[wp++].type = STOP;
      break;

      /* Cope with the various atomic formulae now */
      /* They all just get written into the expression. */

    case DBM_NAME:
      ex[wp].type = tok;
      *required |= REQ_NAME;
      while (isspace(*str))
	str++;
      if (*str != '=') {
	printf("Bad name specifier.\n");
	bad_ex = 1;
	break;
      }
      str++;
      while (isspace(*str))
	str++;
      p = str;
      if (*str == '\"') {	/* Quoted string */
	str++;
	p++;			/* Skip the " */
	while (*str != '\"' && *str)
	  str++;
      } else {			/* Single word */
	while (!isspace(*str) && *str)
	  str++;
      }
      foo = *str;
      *str = '\0';
      ex[wp].data.name = ty_malloc(strlen(p) + 1, "sel_handle");
      strcpy(ex[wp].data.name, p);
      *str = foo;
      if (*str == '\"')
	str++;			/* Skip trailing " */
      wp++;
      break;
    case DBM_TIME:
      ex[wp].type = tok;
      *required |= REQ_TIME;
      while (isspace(*str))
	str++;
      if (*str != '>' && *str != '<') {
	printf("Bad timestamp specifier.\n");
	bad_ex = 1;
	break;
      }
      foo = *str++;
      while (isspace(*str))
	str++;

      if (!isdigit(*str)) {
	printf("Bad time specifier.\n");
	bad_ex = 1;
	break;
      }
      ex[wp].data.time = atoi(str);
      while (isdigit(*str))
	str++;
      switch (*str++) {
      case 'd':
	ex[wp].data.time *= (24 * 60);
	break;
      case 'h':
	ex[wp].data.time *= 60;
	break;
      case 'm':
	/* Leave it be. */
	break;
      default:
	str--;
	printf("Bad time specifier.\n");
	bad_ex = 1;
	break;
      }
      if (!bad_ex) {
	/* We negate this to indicate unused < X min */
	if (foo == '<')
	  ex[wp].data.time = -(ex[wp].data.time);
	wp++;
      }
      break;
    case DBM_OWNER:
      ex[wp].type = tok;
      *required |= REQ_OWNER;
      while (isspace(*str))
	str++;
      if (*str != '=') {
	printf("Bad owner specifier.\n");
	bad_ex = 1;
	break;
      }
      str++;
      while (isspace(*str))
	str++;
      if (*str++ != '#' || !isdigit(*str)) {
	printf("Bad owner specifier.\n");
	bad_ex = 1;
	break;
      }
      ex[wp++].data.owner = atoi(str);
      while (isdigit(*str))
	str++;
      break;
    case DBM_NUMBER:
      ex[wp].type = tok;
      *required |= REQ_NUMBER;
      if (!isdigit(*str)) {
	printf("bad object number\n");
	bad_ex = 1;
	break;
      }
      ex[wp++].data.number = atoi(str);
      while (isdigit(*str))
	str++;
      break;
    case DBM_TYPE:
    case DBM_FLAG:
      ex[wp].type = tok;
      *required |= REQ_FLAG;
      while (isspace(*str))
	str++;
      if (*str != '=') {
	printf("Bad type/flag specifier.\n");
	bad_ex = 1;
	break;
      }
      str++;
      while (isspace(*str))
	str++;
      switch (*str) {
      case 'P':
	ex[wp++].data.flag = TYP_PLAYER;
	break;
      case 'T':
	ex[wp++].data.flag = TYP_THING;
	break;
      case 'E':
	ex[wp++].data.flag = TYP_EXIT;
	break;
      case 'R':
	ex[wp++].data.flag = TYP_ROOM;
	break;
      case 'W':
	ex[wp++].data.flag = WIZARD;
	break;
      case 'G':
	ex[wp++].data.flag = GOD;
	break;
      case 'r':		/* robot */
	ex[wp++].data.flag = ROBOT;
	break;
      case 'S':
	ex[wp++].data.flag = STICKY;
	break;
      case 'D':
	ex[wp++].data.flag = DARK;
	break;
      case 'L':
	ex[wp++].data.flag = LINK_OK;
	break;
      case 'H':
	ex[wp++].data.flag = HAVEN;
	break;
      case 'J':
	ex[wp++].data.flag = JUMP_OK;
	break;
      case 'A':
	ex[wp++].data.flag = ABODE;
	break;
      }
      str++;			/* Skip the flag */
      break;
    }
  } while (tok != STOP && !bad_ex);

  if (bad_ex) {
    printf("Bad expression.\n");
    return (-2);
  } else {
    return (0);
  }
}

/*
 * Snarf the next token out of the string, return a pointer to the next
 * character after it, and stuff the token into the provided int.
 * 
 */

char           *
                get_tok(p, tok)
  char           *p;
  int            *tok;
{
  char            foo, *q;

  *tok = BADTOK;

  while (isspace(*p))
    p++;

  switch (*p) {
  case '\0':
    *tok = STOP;
    break;
  case '&':
    *tok = AND;
    break;
  case '|':
    *tok = OR;
    break;
  case '!':
    *tok = NOT;
    break;
  case '(':
    *tok = OPEN;
    break;
  case ')':
    *tok = CLOSE;
    break;
  case '#':
    *tok = DBM_NUMBER;
    break;
  }

  /* 'Twasn't one of the one char jobs. */

  if (*tok == BADTOK) {		/* If no luck above */
    q = p;
    while (isalpha(*p))
      p++;
    foo = *p;
    *p = '\0';

    if (strcmp("name", q) == 0) {
      *tok = DBM_NAME;
    } else if (strcmp("flag", q) == 0) {
      *tok = DBM_FLAG;
    } else if (strcmp("unused", q) == 0) {
      *tok = DBM_TIME;
    } else if (strcmp("owner", q) == 0) {
      *tok = DBM_OWNER;
    } else if (strcmp("type", q) == 0) {
      *tok = DBM_TYPE;
    }
    *p = foo;
  } else {			/* Just skip over the one character */
    p++;
  }

  return (p);
}