/
teeny/db/
teeny/dbm/
teeny/docs/
teeny/includes/
teeny/misc/
teeny/news/
teeny/text/
/* dbmcmds.c */

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

#include <stdio.h>
#include <ctype.h>

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

/*
 * Implements the other db manager commands. 
 */


struct nuked
{
  int             obj;
  struct nuked   *next;
};

int
lists_handle(z, ex, req)
  char                 *z;
  char                 *ex;
  char                 *req;
{
  register int          i;
  int           loc;
  int           flags;

  /* first we nuke all the lists in the db. what fun.. */

  printf("Spamming...\n");
  for(i = 0; i < db_top(); i++){
    if(!exists_object(i))
      continue;
    if(set_int_elt(i, EXITS, -1) == -1)
      printf("Object #%d has a bad exits pointer.\n", i);
    if(set_int_elt(i, CONTENTS, -1) == -1)
      printf("Object #%d has a bad contents pointer.\n", i);
    if(set_int_elt(i, NEXT, -1) == -1)
      printf("Object #%d has a bad next pointer.\n", i);
  }

  /* now we rebuild everything. loc had better have been correct... */

  printf("Rebuilding...\n");
  for(i = 0; i < db_top(); i++){
    if(!exists_object(i))
      continue;
    if(get_int_elt(i, LOC, &loc) == -1){
      printf("Object #%d has a bad location reference. Destroy it.\n", i);
      continue;
    }
    if(!exists_object(loc) || isexit(loc)){
      printf("Object #%d has an illegal location: #%d.\n", i, loc);
      loc = 0;
      if(set_int_elt(i, LOC, loc) == -1)
        printf("Object #%d has a bad location pointer.\n", i);
    } 
    if(get_int_elt(i, FLAGS, &flags) == -1){
      printf("Object #%d has a bad flags reference. Destroy it.\n", i);
      continue;
    }
    switch(flags & TYPE_MASK){
      case TYP_THING:
      case TYP_PLAYER:
        list_add(i, loc, 1);
        break;
      case TYP_EXIT:
        list_add(i, loc, 0);
        break;
      default:
        break;
    }
  }

  printf("Done.\n");
}
 

int 
pur_handle(p, ex, req)
  char           *p;
  atom           *ex;
  int            *req;
{
  int             i;
  int             flags;
  int             ptr;		/* home, dest or dropto */

  struct nuked   *nuked_list = NULL;
  struct nuked   *tmp, *nuked_this;

  printf("Purging.\n");

  /* Make one pass over the db killing stuff, and keeping track */
  /* of what's been killed so far.                              */

  for (i = 0; i < db_top(); i++)
  {
    if (exists_object(i) && eval_expr(i, ex, *req))
    {
      recycle_obj(i, &nuked_list);
      nuked_this = (struct nuked *)
	  ty_malloc(sizeof(struct nuked), "purge");
      nuked_this->next = nuked_list;
      nuked_this->obj = i;
      nuked_list = nuked_this;
    }
  }

  /* Another pass over the DB, checking destinations, and */
  /* Handling them as best we can. Reset homes to 0 and   */
  /* simply unlink exits/rooms pointed at nuked things.   */

  for (i = 0; i < db_top(); i++)
  {
    if (!exists_object(i))
      continue;

    /* This depends heavily on the fact that */
    /* Destination, Home, and DropTo are all */
    /* The same element of an object.        */

    if (get_int_elt(i, DESTINATION, &ptr) == -1)
      goto dropthru;

    /* See if the destination has been nuked. */

    for (tmp = nuked_list; tmp != NULL; tmp = tmp->next)
    {
      if (tmp->obj == ptr)
	break;
    }
    if (tmp == NULL)
      continue;

    /* If we get here, the dest HAS been nuked. */

    if (get_int_elt(i, FLAGS, &flags) == -1)
      goto dropthru;

    switch (flags & TYPE_MASK)
    {
    case TYP_PLAYER:
      if (set_int_elt(i, DESTINATION, STARTING_LOC) == -1)
	goto dropthru;
      break;
    case TYP_THING:
      {
	int             home, owner;

	if (get_int_elt(i, OWNER, &owner) == -1)
	  goto dropthru;
	if (get_int_elt(owner, HOME, &home) == -1)
	  goto dropthru;
	if (set_int_elt(i, HOME, home) == -1)
	  goto dropthru;
      }
      break;
    case TYP_ROOM:
    case TYP_EXIT:
      if (set_int_elt(i, DESTINATION, -1) == -1)
	goto dropthru;
      break;
    }
  }
  free_nuked_list(nuked_list);
  return (0);
dropthru:
  printf("Bad DB reference in 2nd pass at object %d\n", i);
  free_nuked_list(nuked_list);
  return (-1);
}

free_nuked_list(nk)
  struct nuked   *nk;
{
  struct nuked   *tmp;

  if (nk == NULL)
  {
    return;
  }
  do
  {
    tmp = nk->next;
    free((char *) nk);
    nk = tmp;
  } while (tmp != NULL);
}

int 
cho_handle(p, ex, req)
  char           *p;
  atom           *ex;
  int            *req;
{
  int             i, newowner;

  while (isspace(*p))
    p++;
  if (*p++ != '#')
  {
    printf("Invalid object number for new owner.\n");
    return (0);
  }
  newowner = atoi(p);

  printf("Chowning selected objects to #%d\n", newowner);

  for (i = 0; i < db_top(); i++)
  {
    if (exists_object(i) && eval_expr(i, ex, *req))
    {
      if (set_int_elt(i, OWNER, newowner) == -1)
      {
	printf("Bad object ref at %d\n", i);
	return (-1);
      }
    }
  }
  return (0);
}

int 
sum_handle(p, ex, req)
  char           *p;
  atom           *ex;
  int            *req;

{
  int             i;
  char           *name;
  int             owner;
  char           *ownername;
  int             flags;
  char           *q, ch = '\0', work[BUFFSIZ];

  for (i = 0; i < db_top(); i++)
  {
    if (exists_object(i) && eval_expr(i, ex, *req))
    {

      /* Summarize the data for this guy */

      if (get_str_elt(i, NAME, &name) == -1)
      {
	printf("DB error at object #%d\n", i);
	continue;
      }
      if (get_int_elt(i, OWNER, &owner) == -1)
      {
	printf("DB error at object #%d\n", i);
	continue;
      }
      if (get_str_elt(owner, NAME, &ownername) == -1)
      {
	printf("DB error at object #%d\n", owner);
	continue;
      }
      if (get_int_elt(i, FLAGS, &flags) == -1)
      {
	printf("DB error at object #%d\n", i);
	continue;
      }
      /* We've got the data. Write it out. */

      if (isplayer(i))
      {
	for (q = name; *q && *q != ' '; q++);
	ch = *q;
	*q = '\0';
      }
      printf("(#%d)  Name: %s\n", i, name);
      if (ch)
	*q = ch;

      if (isplayer(owner))
      {
	for (q = ownername; *q && *q != ' '; q++);
	ch = *q;
	*q = '\0';
      }
      switch (flags & TYPE_MASK)
      {
      case TYP_EXIT:
	strcpy(work, "E/");
	break;
      case TYP_ROOM:
	strcpy(work, "R/");
	break;
      case TYP_PLAYER:
	strcpy(work, "P/");
	break;
      case TYP_THING:
	strcpy(work, "T/");
	break;
      default:
	strcpy(work, "U/");
      }
      if (flags & WIZARD)
	strcat(work, "W");
      if (flags & TEMPLE)
	strcat(work, "T");
      if (flags & LINK_OK)
	strcat(work, "L");
      if (flags & JUMP_OK)
	strcat(work, "J");
      if (flags & ABODE)
	strcat(work, "A");
      if (flags & HAVEN)
	strcat(work, "H");
      if (flags & DARK)
	strcat(work, "D");

      printf("Flags: %s(%d)  Owner: %s\n", work, flags,
	     ownername);
      if (ch)
	*q = ch;
    }
  }
}

/*
 * First pass of recycling. This does one object. 
 */

recycle_obj(obj, nuke_list)
  int             obj;
  struct nuked  **nuke_list;	/* So we can add things to it. */

{
  int             loc, flags, next, list;
  struct nuked   *tmp;

  if (obj == 0)
  {
    printf("Cannot recycle object Zero.\n");
    return;
  }
#if STARTING_LOC != 0
  if (obj == STARTING_LOC)
  {
    printf("Cannot recycle player start.\n");
    return;
  }
#endif				/* STARTING_LOC != 0 */
  if (get_int_elt(obj, FLAGS, &flags) == -1)
    goto bomb;
  if (get_int_elt(obj, LOC, &loc) == -1)
    goto bomb;

  switch (flags & TYPE_MASK)
  {

  case TYP_PLAYER:		/* Get the player off the contents list. */
    /* Otherwise it's just like a room. */
    list_drop(obj, loc, 1);
  case TYP_ROOM:

    /* Destroy all the exits in room/carried by player */

    if (get_int_elt(obj, EXITS, &list) == -1)
      goto bomb;
    while (list != -1)
    {
      if (get_int_elt(list, NEXT, &next) == -1)
	goto bomb;

      tmp = (struct nuked *) ty_malloc(sizeof(struct nuked),
				       "recycle_obj");
      tmp->next = *nuke_list;
      *nuke_list = tmp;
      tmp->obj = list;

      destroy_obj(list);
      list = next;
    }

    /* Try to send room contents/player inventory home. */

    if (get_int_elt(obj, CONTENTS, &list) == -1)
      goto bomb;
    while (list != -1)
    {
      if (get_int_elt(list, NEXT, &next) == -1)
	goto bomb;
      send_home(list, obj);
      list = next;
    }

    /* Now nuke everything left -- i.e. stuff homed here */

    if (get_int_elt(obj, CONTENTS, &list) == -1)
      goto bomb;
    while (list != -1)
    {
      if (get_int_elt(list, NEXT, &next) == -1)
	goto bomb;

      tmp = (struct nuked *) ty_malloc(sizeof(struct nuked),
				       "recycle_obj");
      tmp->next = *nuke_list;
      *nuke_list = tmp;
      tmp->obj = list;

      destroy_obj(list);
      list = next;
    }

    /* nuke the room/player itself. */

    destroy_obj(obj);
    break;
  case TYP_EXIT:
    list_drop(obj, loc, 0);
    destroy_obj(obj);
    break;
  case TYP_THING:
    list_drop(obj, loc, 1);
    destroy_obj(obj);
    break;
  default:
    printf("Eeek! Unknown object type!\n");
    return;
  }
  return;
bomb:
  printf("Something bad happened. Good luck.\n");
  return;
}


int 
fix_handle()
{
  int             i, list, loc, flags;
  int             total = 0;

  printf("Scanning the database...\n");

  for (i = 0; i < db_top(); i++)
  {
    if (!exists_object(i))
      continue;

    /* loop through the exits list first.. */
    if (get_int_elt(i, EXITS, &list) == -1)
      goto bomb;
    while (list != -1)
    {
      if (get_int_elt(list, LOC, &loc) == -1)
	goto bomb;
      if (loc != i)
      {
	if (set_int_elt(list, LOC, i) == -1)
	  goto bomb;
	total++;
      }
      if (get_int_elt(list, NEXT, &list) == -1)
	goto bomb;
    }

    /* now do the contents list. this is slightly more complex.. */
    if (get_int_elt(i, CONTENTS, &list) == -1)
      goto bomb;
    while (list != -1)
    {
      int             next;

      if (get_int_elt(list, NEXT, &next) == -1)
	goto bomb;
      if (get_int_elt(list, FLAGS, &flags) == -1)
	goto bomb;
      if ((flags & TYPE_MASK) == TYP_EXIT)
      {
	if (get_int_elt(list, LOC, &loc) == -1)
	  goto bomb;
	if (loc != i)
	{
	  if (set_int_elt(list, LOC, i) == -1)
	    goto bomb;
	}
	total++;
	list_drop(list, i, 1);
	list_add(list, i, 0);
	if (set_int_elt(list, DESTINATION, -1) == -1)
	  goto bomb;
      }
      list = next;
    }

    /* all done with that sucker... */
  }

  printf("Scan done. %d exits fixed.\n", total);
  return (0);

bomb:

  printf("Database corrupt. Scan aborted.\n");
  return (-1);
}

/* 
 * Utility funtions. 
 *
 */


/* Drop an object from either a contents list or an exits list */

void 
list_drop(elt, place, code)
  int             elt, place, code;
{
  int             current, last, next;
  int             listcode;

  if (code == 1)
  {
    listcode = CONTENTS;
  } else
  {
    listcode = EXITS;
  }

  if (get_int_elt(place, listcode, &current) == -1)
  {
    warning("list_drop", "bad list reference.");
    return;
  }
  last = -1;
  while (current != elt && current != -1)
  {
    last = current;
    if (get_int_elt(current, NEXT, &current) == -1)
    {
      warning("list_drop", "bad ref inside list");
      return;
    }
  }

  /* Post Mortem. */

  /* grab the next thing on the list anyway. */

  if (get_int_elt(current, NEXT, &next) == -1)
  {
    warning("list_drop", "bad ref inside list");
    return;
  }
  if (last == -1)
  {				/* Was 1st thing on list */
    if (set_int_elt(place, listcode, next) == -1)
    {
      warning("list_drop", "bad list reference.");
      return;
    }
  } else
  {
    if (set_int_elt(last, NEXT, next) == -1)
    {
      warning("list_drop", "bad list reference.");
      return;
    }
  }
}

/* Sends an object home */

send_home(obj, loc)
  int             obj;
  int             loc;
{
  int             home, next;

  if (get_int_elt(obj, HOME, &home) == -1)
  {
    warning("sendhome", "could not get home");
    return;
  }
  list_drop(obj, loc, 1);	/* Drop it from contents list here */

  if (!exists_object(home))
  {
    home = STARTING_LOC;	/* Fake it, eh? */
  }
  if (set_int_elt(obj, LOC, home) == -1)
  {
    warning("sendhome", "could not set location");
    return;
  }
  if (get_int_elt(home, CONTENTS, &next) == -1)
  {
    warning("sendhome", "could not get contents");
    return;
  }
  if (set_int_elt(obj, NEXT, next) == -1)
  {
    warning("sendhome", "could not set next");
    return;
  }
  if (set_int_elt(home, CONTENTS, obj) == -1)
  {
    warning("sendhome", "could not set contents");
    return;
  }
}
void 
list_add(thing, place, code)
  int             thing;
  int             place;
  int             code;
{
  int             list;
  int             listcode;

  if (code == 1)
  {
    listcode = CONTENTS;
  } else
  {
    listcode = EXITS;
  }

  if (get_int_elt(place, listcode, &list) == -1)
  {
    warning("list_add", "can't get contents");
    return;
  }
  if (set_int_elt(thing, NEXT, list) == -1)
  {
    warning("list_add", "bad can't set NEXT");
    return;
  }
  if (set_int_elt(place, listcode, thing) == -1)
  {
    warning("list_add", "can't set contents");
    return;
  }
}