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

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

#include <stdio.h>
#ifdef STRING_H
#include <string.h>
#else
#include <strings.h>
#endif				/* STRING_H */
#include <sys/time.h>
#include <ctype.h>
#ifdef STDDEF_H
#include <stddef.h>
#else
#define size_t unsigned
#endif				/* STDDEF_H */

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

/*
 * This implements the mainline of the TeenyMUD database manager, for doing
 * bulk operations standalone on a TeenyMUD database.
 * 
 */

char           *strstr_CI();
int             sel_handle();
int             pur_handle();
int             cho_handle();
int             sum_handle();
int             fix_handle();
int             lists_handle();

void            warning();
void            fatal();

struct {
  char           *name;
  int             (*handler) ();
}               cmdtab[] = {
  {
    "select", sel_handle
  },
  {
    "purge", pur_handle
  },
  {
    "chown", cho_handle
  },
  {
    "summarize", sum_handle
  },
  {
    "fix-exits", fix_handle
  },
  {
    "fix-lists", lists_handle
  },
  {
    NULL, NULL
  }
};

/* This is the RPN expression we'll apply to the db */

static atom     expr[MAXEXPR];

/* The current time, expressed as minute since the epoch. */

int             currenttime;

/* Do it to it */

main(argc, argv)
  int             argc;

  char           *argv[];

{
  char            cmd[1024];
  char           *command, *p;
  int             i;
  int             ret;
  int             required;
  struct timeval  tv;

  if (argc != 3) {
    printf("Usage: %s <indexfile> <chunkfile>\n",
	   argv[0]);
    exit(1);
  }
  (void) gettimeofday(&tv, (struct timezone *) 0);
  currenttime = tv.tv_sec / 60;

  /* Open up the database */

  initialize_cache(10240L);	/* Smallish cache. */
  open_chunkfile(argv[2]);
  printf("Reading descriptors...\n");
  read_descriptors(argv[1]);

  /* Now loop busily around until we quit. */

  while (1) {
    printf("dbm>");
    get_cmd(cmd);

    /* Strip out the first token. */

    p = cmd;
    while (isspace(*p))
      p++;
    if (!*p)
      continue;
    command = p;
    while (!isspace(*p) && *p)
      p++;
    if (*p)
      *p++ = '\0';
    while (isspace(*p))
      p++;

    /* command points at the first token, p points    */
    /* at the rest of the string, if any. Try to find */
    /* The command.   */

    if (strcmp(command, "quit") == 0)
      break;

    for (i = 0; cmdtab[i].name != NULL; i++) {
      if (strcmp(cmdtab[i].name, command) == 0) {
	ret = (cmdtab[i].handler) (p, expr, &required);
	if (ret == -2) {
	  required = 0;
	  expr[0].type = STOP;
	}
	break;
      }
    }
    if (cmdtab[i].name == NULL) {
      printf("No such command.\n");
    }
  }

  /* Bring the db back into synch, and quit. */

  cache_flush();
  if (write_descriptors(argv[1]) == -1) {
    warning("Final db dump", "write_descriptors() failed.");
  }
}


/*
 * Routine to evalute an expression on a database object. Returns 1 if the
 * object meets the conditions, 0 otherwise.
 * 
 */

static int      eval_stack[512];/* Screw counting. Make the bugger *big* */

eval_expr(obj, exp, required)
  int             obj;

  atom           *exp;

  int             required;

{
  int             sp;
  int             op1, op2;
  char           *name;
  int             owner;
  int             flags;
  int             timestamp;

  /* Go and get all the required stuff for this expression */

  if (required & REQ_NAME) {
    if (get_str_elt(obj, NAME, &name) == -1) {
      printf("Bad db ref on object %d\n", obj);
      return (0);
    }
  }
  if (required & REQ_FLAG) {
    if (get_int_elt(obj, FLAGS, &flags) == -1) {
      printf("Bad db ref on object %d\n", obj);
      return (0);
    }
  }
  if (required & REQ_OWNER) {
    if (get_int_elt(obj, OWNER, &owner) == -1) {
      printf("Bad db ref on object %d\n", obj);
      return (0);
    }
  }
  if (required & REQ_TIME) {
    if (get_int_elt(obj, TIMESTAMP, &timestamp) == -1) {
      printf("Bad db ref on object %d\n", obj);
      return (0);
    }
  }
  sp = 0;

  while (exp->type != STOP) {
    switch (exp->type) {
    case OR:
      op1 = eval_stack[--sp];
      op2 = eval_stack[--sp];
      eval_stack[sp++] = op1 || op2;
      break;
    case AND:
      op1 = eval_stack[--sp];
      op2 = eval_stack[--sp];
      eval_stack[sp++] = op1 && op2;
      break;
    case NOT:
      op1 = eval_stack[--sp];
      eval_stack[sp++] = !op1;
      break;
    case DBM_NUMBER:
      eval_stack[sp++] = (obj == (exp->data).number);
      break;
    case DBM_FLAG:
      eval_stack[sp++] = (flags & (exp->data).flag);
      break;
    case DBM_OWNER:
      eval_stack[sp++] = (owner == (exp->data).owner);
      break;
    case DBM_NAME:
      if (strstr_CI(name, ((exp->data).name)) != NULL)
	eval_stack[sp++] = 1;
      else
	eval_stack[sp++] = 0;
      break;
    case DBM_TIME:
      if ((exp->data).time < 0) {	/* Unused < X minutes */
	eval_stack[sp++] = ((currenttime - timestamp)
			    < -((exp->data).time));
      } else {			/* Unused > X minutes */
	eval_stack[sp++] = ((currenttime - timestamp)
			    > (exp->data).time);
      }
      break;
    case DBM_TYPE:
      eval_stack[sp++] =
	((flags & TYPE_MASK) == (exp->data).flag);
      break;
    }
    exp++;
  }
  return (eval_stack[--sp]);
}

dump_expr(ex)
  atom           *ex;
{
  while (ex->type != STOP) {
    switch (ex->type) {
    case AND:
      printf("& ");
      break;
    case OR:
      printf("| ");
      break;
    case NOT:
      printf("!");
      break;
    case DBM_NUMBER:
      printf("#%d ", (ex->data).number);
      break;
    case DBM_FLAG:
      printf("flag=");
      switch ((ex->data).flag) {
      case GOD:
	putchar('G');
	break;
      case WIZARD:
	putchar('W');
	break;
      case ROBOT:
	putchar('R');
	break;
      case STICKY:
	putchar('S');
	break;
      case DARK:
	putchar('D');
	break;
      case LINK_OK:
	putchar('L');
	break;
      case HAVEN:
	putchar('H');
	break;
      case JUMP_OK:
	putchar('J');
	break;
      case ABODE:
	putchar('A');
	break;
      }
      putchar(' ');
      break;
    case DBM_TYPE:
      printf("type=");
      switch ((ex->data).flag) {
      case TYP_ROOM:
	putchar('R');
	break;
      case TYP_THING:
	putchar('T');
	break;
      case TYP_EXIT:
	putchar('E');
	break;
      case TYP_PLAYER:
	putchar('P');
	break;
      }
      putchar(' ');
      break;
    case DBM_TIME:
      printf("unused %d", (ex->data).time);
      break;
    case DBM_OWNER:
      printf("owner=%d ", (ex->data).owner);
      break;
    case DBM_NAME:
      printf("name=%s ", (ex->data).name);
      break;
    }
    ex++;
  }
  putchar('\n');
}

/*
 * Read in a (potentially long) command line.
 */

get_cmd(buff)
  char           *buff;
{
  char           *p = buff;
  char           *q;

  while (1) {
    gets(p);
    q = p;
    while (*q)
      q++;			/* Find EOLN */
    q--;
    while (isspace(*q))
      q--;			/* Back up to 1st non-space */
    if (*q == '\\') {
      p = q;
    } else {
      q[1] = '\0';
      break;
    }
  }
}

/*
 * We need to emulate some support routines for the DB library that the MUD
 * normally provides. These are them:
 * 
 */


/*
 * Converts ints to strings.
 * 
 */

char           *
                ty_itoa(p, i)
  char           *p;

  int             i;
{
  char           *ty_itoa_prim();

  if (i < 0) {
    i = -i;
    *p++ = '-';
  } else if (i == 0) {
    *p++ = '0';
    return (p);
  }
  return (ty_itoa_prim(p, i));
}

/* recursively does it to it. Hee! Russ would love me. */

char           *
                ty_itoa_prim(p, i)
  char           *p;

  int             i;
{
  if (i == 0) {
    return (p);
  } else {
    p = ty_itoa_prim(p, i / 10);
    *p++ = (char) (i % 10) + '0';
    return (p);
  }
}

char           *
                ty_malloc(n, p)
  int             n;

  char           *p;
{
  char           *foo;

  foo = malloc((size_t) n);
  if (foo == NULL) {
    fatal(p, "Out of memory");
  }
  return (foo);
}

void            ty_free(p)
  char           *p;
{
  if (p && *p)
    free(p);
}

/*
 * Write a timestamp on an object. In minutes since Jan 1 1970
 */

void            stamp(obj)
{
  struct timeval  foo;

  (void) gettimeofday(&foo, (struct timezone *) 0);

  if (set_int_elt(obj, TIMESTAMP, (int) (foo.tv_sec / 60)) == -1) {
    warning("timestamp", "failed write to object");
  }
}