/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* db.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */

#include "conf.h"
#include "teeny.h"
#include "teenydb.h"
#include "ptable.h"
#include "externs.h"

/* main database access routines. based on andrew's old code. */

static struct dsc *dsc_alloc _ANSI_ARGS_((void));
static void dsc_free _ANSI_ARGS_((struct dsc *));
static void grow_index _ANSI_ARGS_((void));

/* the actual database. */
struct dsc **main_index = NULL;

extern int errno;

/* does the object exist? */
int exists_object(obj)
    int obj;
{
  return(_exists_object(obj));
}

/* retrieve a string from an object. */
int get_str_elt(obj, code, ret)
    int obj, code;
    char **ret;
{
  register struct obj_data *theobj;

  if(!_exists_object(obj))
    return(-1);

  if((code == NAME) && (DSC_TYPE(main_index[obj]) == TYP_PLAYER)) {
    *ret = ptable_lookup(obj);
    if(*ret != (char *)NULL)
      return(0);
  }

  theobj = lookup_obj(obj);
  if(theobj == (struct obj_data *)NULL)
    return(-1);

  if(code == NAME) {
    *ret = theobj->name;
  } else {
    logfile(LOG_ERROR, "get_str_elt: invalid element code (%d)\n", code);
    return(-1);
  }
  return(0);
}

/* retrieve an integer array from an object. */
int get_array_elt(obj, code, ret)
    int obj, code, **ret;
{
  if(!_exists_object(obj))
    return(-1);

  if(code != DESTS) {
    logfile(LOG_ERROR, "get_array_elt: bad element code (%d)\n", code);
    return(-1);
  }

  /* only exits have dests! */
  if(DSC_TYPE(main_index[obj]) == TYP_EXIT) {
    *ret = DSC_DESTS(main_index[obj]);
    return(0);
  } else
    return(-1);
}

/* retrieve flags from an object. */
int get_flags_elt(obj, code, ret)
    int obj, code, *ret;
{
  register int i;

  if(!_exists_object(obj))
    return(-1);

  if(code != FLAGS) {
    logfile(LOG_ERROR, "get_flags_elt: bad element code (%d)\n", code);
    return(-1);
  }

  for(i = 0; i < FLAGS_LEN; i++)
    ret[i] = (DSC_FLAGS(main_index[obj]))[i];
  return(0);
}

/* retrieve an integer from an object. */
int get_int_elt(obj, code, ret)
    int obj, code;
    int *ret;
{
  register struct obj_data *theobj;

  if(!_exists_object(obj))
    return(-1);

  /* check the index first. */
  switch(code) {
  case NEXT:
    *ret = DSC_NEXT(main_index[obj]);
    break;
  case OWNER:
    *ret = DSC_OWNER(main_index[obj]);
    break;
  case PARENT:
    *ret = DSC_PARENT(main_index[obj]);
    break;
  case HOME:
    /* exits do not have homes! */
    if(DSC_TYPE(main_index[obj]) == TYP_EXIT)
      return(-1);
    *ret = DSC_HOME(main_index[obj]);
    break;
  default:
    /* now lookup the object. */
    theobj = lookup_obj(obj);
    if(theobj == (struct obj_data *)NULL)
      return(-1);

    switch(code) {
    case QUOTA:
      *ret = theobj->quota;
      break;
    case LOC:
      *ret = theobj->loc;
      break;
    case CONTENTS:
      *ret = theobj->contents;
      break;
    case EXITS:
      *ret = theobj->exits;
      break;
    case ROOMS:
      *ret = theobj->rooms;
      break;
    case TIMESTAMP:
      *ret = theobj->timestamp;
      break;
    case CREATESTAMP:
      *ret = theobj->created;
      break;
    case USES:
      *ret = theobj->usecnt;
      break;
    case CHARGES:
      *ret = theobj->charges;
      break;
    case SEMAPHORES:
      *ret = theobj->semaphores;
      break;
    case COST:
      *ret = theobj->cost;
      break;
    case QUEUE:
      *ret = theobj->queue;
      break;
    default:
      logfile(LOG_ERROR, "get_int_elt: invalid element code (%d)\n", code);
      return(-1);
    }
  }
  return(0);
}

/* set a string on an object. */
int set_str_elt(obj, code, value)
    int obj, code;
    char *value;
{
  register struct obj_data *theobj;
  register int newsize, valsize;
  register char **ptr;

  if(!_exists_object(obj))
    return(-1);
  theobj = lookup_obj(obj);
  if(theobj == (struct obj_data *)NULL)
    return(-1);

  if(code == NAME) {
    ptr = &(theobj->name);
  } else {
    logfile(LOG_ERROR, "set_str_elt: invalid element code (%d)\n", code);
    return(-1);
  }

  valsize = (value == (char *)NULL) ? 0 : strlen(value);
  if(*ptr == (char *)NULL) {
    newsize = DSC_SIZE(main_index[obj]) + valsize;
  } else {
    newsize = (DSC_SIZE(main_index[obj]) - strlen(*ptr)) + valsize;
  }
  mudstat.cache_usage =
  	(mudstat.cache_usage - DSC_SIZE(main_index[obj])) + newsize;
  DSC_SIZE(main_index[obj]) = newsize;
  DSC_FLAG2(main_index[obj]) |= DIRTY;

  /* is this a player? */
  if ((code == NAME) && (DSC_TYPE(main_index[obj]) == TYP_PLAYER)) {
    if((value != (char *)NULL) && value[0]) {
      ptable_delete(obj);
      ptable_add(obj, value);
    } else {
      ptable_delete(obj);
    }
  }

  ty_free((VOID *) (*ptr));
  if(value == (char *)NULL) {
    *ptr = (char *)NULL;
  } else {
    *ptr = (char *)ty_strdup(value, "set_str_elt.ptr");
  }
  return(0);
}

/* set an integer array on an object. */
int set_array_elt(obj, code, value)
    int obj, code, *value;
{
  int *newval;

  if(!_exists_object(obj))
    return(-1);

  if(code != DESTS) {
    logfile(LOG_ERROR, "set_array_elt: invalid element code (%d)\n", code);
    return(-1);
  }

  /* only exits have dests! */
  if(DSC_TYPE(main_index[obj]) != TYP_EXIT)
    return(-1);

  /* we handle memory management here, but assume value is formatted correct */
  if(value != (int *)NULL) {
    newval = (int *)ty_malloc((value[0] + 1) * sizeof(int),
    		              "set_array_elt.newval");
    bcopy((VOID *) value, (VOID *) newval, (value[0] + 1) * sizeof(int));
  } else
    newval = (int *)NULL;

  ty_free((VOID *) DSC_DESTS(main_index[obj]));
  DSC_DESTS(main_index[obj]) = newval;

  return(0);
}

/* set the flags on an object. */
int set_flags_elt(obj, code, value)
    int obj, code, *value;
{
  register int i;

  if(!_exists_object(obj))
    return(-1);

  if(code != FLAGS) {
    logfile(LOG_ERROR, "set_flags_elt: invalid element code (%d)\n", code);
    return(-1);
  }

  for(i = 0; i < FLAGS_LEN; i++)
    (DSC_FLAGS(main_index[obj]))[i] = value[i];
  return(0);
}

/* set an integer on an object. */
int set_int_elt(obj, code, value)
    int obj, code, value;
{
  register struct obj_data *theobj;

  if(!_exists_object(obj))
    return(-1);

  /* check the index first. */
  switch(code) {
  case NEXT:
    DSC_NEXT(main_index[obj]) = value;
    return(0);
  case OWNER:
    DSC_OWNER(main_index[obj]) = value;
    return(0);
  case PARENT:
    DSC_PARENT(main_index[obj]) = value;
    return(0);
  case HOME:
    /* exits do not have homes! */
    if(DSC_TYPE(main_index[obj]) == TYP_EXIT)
      return(-1);
    DSC_HOME(main_index[obj]) = value;
    return(0);
  default:
    /* look up the object. */
    theobj = lookup_obj(obj);
    if(theobj == (struct obj_data *)NULL)
      return(-1);

    switch(code) {
    case QUOTA:
      theobj->quota = value;
      break;
    case LOC:
      theobj->loc = value;
      break;
    case CONTENTS:
      theobj->contents = value;
      break;
    case EXITS:
      theobj->exits = value;
      break;
    case ROOMS:
      theobj->rooms = value;
      break;
    case TIMESTAMP:
      theobj->timestamp = value;
      break;
    case CREATESTAMP:
      theobj->created = value;
      break;
    case USES:
      theobj->usecnt = value;
      break;
    case CHARGES:
      theobj->charges = value;
      break;
    case SEMAPHORES:
      theobj->semaphores = value;
      break;
    case COST:
      theobj->cost = value;
      break;
    case QUEUE:
      theobj->queue = value;
      break;
    default:
      logfile(LOG_ERROR, "set_int_elt: invalid element code (%d)\n", code);
      return(-1);
    }
  }

  DSC_FLAG2(main_index[obj]) |= DIRTY;
  return(0);
}

/* destroy an object. */
void destroy_obj(obj)
    int obj;
{
  register struct obj_data *theobj;

  if(!_exists_object(obj))
    return;
  theobj = lookup_obj(obj);
  if(theobj == (struct obj_data *) NULL)
    return;

  /* remove from cache and disk. */
  cache_delete(theobj);
  disk_delete(theobj);

  /* free everything. */
  free_obj(theobj);		/* BOOM! */

  /* type specific stuff */
  switch(DSC_TYPE(main_index[obj])) {
  case TYP_PLAYER:
    ptable_delete(obj);
    break;
  case TYP_EXIT:
    ty_free((VOID *) DSC_DESTS(main_index[obj]));
    break;
  }

  dsc_free(main_index[obj]);
  mudstat.actual_objects--;
  mudstat.garbage_count++;
  main_index[obj] = (struct dsc *)NULL;
}

/* create a new object. */
int create_obj(type)
    int type;
{
  register struct obj_data *theobj;
  register struct dsc *thedsc;
  register int num;

  thedsc = dsc_alloc();
  theobj = (struct obj_data *)ty_malloc(sizeof(struct obj_data),
  					"create_obj.theobj");
  
  /* set it up. */
  DSC_DATA(thedsc) = theobj;
  DSC_SIZE(thedsc) = (14 * sizeof(int)) + 1;
  DSC_FLAG1(thedsc) = type;
  DSC_FLAG2(thedsc) = IN_MEMORY | DIRTY;
  DSC_OWNER(thedsc) = -1;
  DSC_PARENT(thedsc) = -1;
  DSC_NEXT(thedsc) = -1;

  /* exit? */
  if(type != TYP_EXIT) {
    DSC_HOME(thedsc) = -1;
  } else {
    DSC_DESTS(thedsc) = (int *)NULL;
  }

  bzero((VOID *)theobj->attributes, sizeof(struct attr *) * ATTR_WIDTH);
  theobj->attr_total = 0;
  theobj->name = (char *)NULL;
  theobj->contents = -1;
  theobj->exits = -1;
  theobj->rooms = (type == TYP_PLAYER) ? 0 : -1;
  theobj->quota = 0;
  theobj->loc = 0;
  theobj->timestamp = 0;
  theobj->created = 0;
  theobj->usecnt = 0;
  theobj->charges = -1;
  theobj->semaphores = 0;
  theobj->cost = 0;
  theobj->queue = 0;

  if(mudstat.garbage_count == 0) {
    if(mudstat.slack == 0)
      grow_index();
    mudstat.slack--;
    main_index[mudstat.total_objects] = thedsc;
    num = mudstat.total_objects;
    mudstat.total_objects++;
  } else {
    for(num = mudstat.total_objects - 1; num > 0; num--) {
      if(main_index[num] == (struct dsc *)NULL)
        break;
    }
    if((num == 0) || (main_index[num] != (struct dsc *)NULL)) {
      logfile(LOG_ERROR, "create_obj: garbage count out of sync\n");
      return(-1);
    }
    main_index[num] = thedsc;
    mudstat.garbage_count--;
  }
  mudstat.actual_objects++;

  theobj->objnum = num;
  cache_insert(theobj);

  stamp(num, STAMP_CREATED);
  return(num);
}

/* look an object up. */
struct obj_data *lookup_obj(obj)
    int obj;
{
  register struct obj_data *theobj;

  /* non-existance is an error. */
  if(!_exists_object(obj)) {
    mudstat.cache_errors++;
    return((struct obj_data *)NULL);
  }
  if(!(DSC_FLAG2(main_index[obj]) & IN_MEMORY)) {
    mudstat.cache_misses++;
    
    theobj = disk_thaw(obj);
    if(theobj == (struct obj_data *)NULL) {
      logfile(LOG_ERROR, "lookup_obj: thaw of #%d failed.\n", obj);
      mudstat.cache_errors++;
      return((struct obj_data *)NULL);
    }
    cache_insert(theobj);
  } else {
    mudstat.cache_hits++;
    theobj = DSC_DATA(main_index[obj]);
    cache_touch(theobj);
  }
  return(theobj);
}

/* write out the database file, new style. */
int database_write(name)
    char *name;
{
  FILE *fp;
  register struct dsc *thedsc;
  register int i;
  int work[6 + FLAGS_LEN];		/* minimal write buffer */

  fp = fopen(name, "w");
  if (fp == (FILE *)NULL) {
    logfile(LOG_ERROR, "database_write: couldn't open %s for writing.\n", name);
    return(-1);
  }

  /* first things first. */ 
  work[0] = mudstat.actual_objects;
  work[1] = mudstat.garbage_count;
  work[2] = mudstat.total_objects;

  if(fwrite((char *)work, sizeof(int), 3, fp) < 3) {
    logfile(LOG_ERROR, "database_write: error writing file.\n");
    fclose(fp);
    return(-1);
  }

  /* write out the objects. */
  for (i = 0; i < mudstat.total_objects; i++) {
    if (_exists_object(i)) {
      thedsc = main_index[i];

      work[0] = i;              /* Obj # */
      work[1] = DSC_FLAG1(thedsc);
      work[2] = (DSC_FLAG2(thedsc) & ~INTERNAL_FLAGS);
      work[3] = DSC_SIZE(thedsc);
      work[4] = DSC_NEXT(thedsc);
      work[5] = DSC_OWNER(thedsc);
      work[6] = DSC_PARENT(thedsc);

      /* extra glue for destinations */
      if(DSC_TYPE(thedsc) != TYP_EXIT) {
        work[7] = DSC_HOME(thedsc);

	if(fwrite((char *)work, sizeof(work), 1, fp) < 1) {
	  logfile(LOG_ERROR, "database_write: error writing file.\n");
	  fclose(fp);
          return(-1);
        }
      } else {
        if(DSC_DESTS(thedsc) == (int *)NULL) {
	  work[7] = -1;

	  if(fwrite((char *)work, sizeof(work), 1, fp) < 1) {
	    logfile(LOG_ERROR, "database_write: error writing file.\n");
	    fclose(fp);
	    return(-1);
	  }
	} else {
          if(fwrite((char *)work, sizeof(int), 7, fp) < 7) {
	    logfile(LOG_ERROR, "database_write: error writing file.\n");
	    fclose(fp);
	    return(-1);
	  }

	  /* the first element of dests is how many follow. */
	  if(fwrite((char *)DSC_DESTS(thedsc), sizeof(int),
	  	(DSC_DESTS(thedsc)[0])+1, fp) < (DSC_DESTS(thedsc)[0])+1) {
	    logfile(LOG_ERROR, "database_write: error writing file.\n");
	    fclose(fp);
	    return(-1);
	  }
	}
      }    
    }
  }

  fclose(fp);
  return(0);
}

/* read in the new style database file. */
int database_read(name)
    char *name;
{
  FILE *fp;
  register int i;
  int objnum;
  register struct dsc *thedsc;
  int work[6 + FLAGS_LEN];
  int *dests;

  fp = fopen(name, "r");
  if (fp == (FILE *)NULL) {
    logfile(LOG_ERROR, "database_read: couldn't open descriptor file %s.\n",
	    name);
    return(-1);
  }

  /* Read in the initial stuff */
  if(feof(fp) || (fread((char *)work, sizeof(int), 3, fp) < 3)) {
    logfile(LOG_ERROR, "database_read: error reading descriptor file.\n");
    fclose(fp);
    return (-1);
  }
  mudstat.actual_objects = work[0];
  mudstat.garbage_count = work[1];
  mudstat.total_objects = work[2];

  /* initialize the database. */
  initialize_db(mudstat.total_objects + mudconf.slack);
  mudstat.slack = mudconf.slack;

  /* read in the objects, being smart about exit destinations. */
  for (i = mudstat.actual_objects; i > 0; i--) {
    thedsc = dsc_alloc();
    if (feof(fp) || (fread((char *)work, sizeof(work), 1, fp) < 1)) {
      logfile(LOG_ERROR, "database_read: error reading descriptor file.\n");
      fclose(fp);
      return (-1);
    }
    objnum = work[0];
    main_index[objnum] = thedsc;
    DSC_FLAG1(thedsc) = work[1];
    DSC_FLAG2(thedsc) = work[2];
    DSC_SIZE(thedsc) = work[3];
    DSC_NEXT(thedsc) = work[4];
    DSC_OWNER(thedsc) = work[5];
    DSC_PARENT(thedsc) = work[6];

    /* the might be an exit... */
    if(DSC_TYPE(thedsc) != TYP_EXIT) {
      DSC_HOME(thedsc) = work[7];
    } else {
      if(work[7] != -1) {
        /* first element is how many follow. */
        dests = (int *)ty_malloc((work[7] + 1) * sizeof(int),
				 "read_database.dests");
	dests[0] = work[7];
	if (feof(fp) || (fread((char *)dests + sizeof(int), sizeof(int),
			 dests[0], fp) < dests[0])) {
	  logfile(LOG_ERROR, "database_read: error reading descriptor file.\n");
	  fclose(fp);
	  ty_free((VOID *)dests);
	  return(-1);
	}

	DSC_DESTS(thedsc) = dests;
      } else {
        DSC_DESTS(thedsc) = (int *)NULL;
      }
    }
  }

  fclose(fp);
  return (0);
}

/* This grows the index by the specified increment. */
static void grow_index()
{
  register int newsize =
	(mudstat.total_objects + mudconf.growth_increment + mudstat.slack);

  main_index = (struct dsc **)ty_realloc((VOID *)main_index,
		(size_t)(sizeof(struct dsc *) * newsize),
		"grow_index.main_index");
  while(newsize > mudstat.total_objects)
    main_index[--newsize] = (struct dsc *)NULL;
}

/* descriptor management code. */
static struct dsc *free_dscs = (struct dsc *)NULL;

void initialize_db(size)
    int size;
{
  register int idx = 0;

  free_dscs = (struct dsc *)ty_malloc(sizeof(struct dsc) * size,
				      "initialize_db.free_dscs");

  /* This loop is written like this for a *reason*. */
  while(idx < (size - 1)) {
    (free_dscs[idx].ptr).next = &free_dscs[idx + 1];
    idx++;
  }
  (free_dscs[idx].ptr).next = (struct dsc *)NULL;

  main_index = (struct dsc **)ty_malloc(sizeof(struct dsc *) * size,
					"initialize_db.main_index");
  bzero((VOID *)main_index, sizeof(struct dsc *) * size);
}

static struct dsc *dsc_alloc()
{
  register struct dsc *ret;

  if(free_dscs == (struct dsc *)NULL) {
    register int idx = 0;

    free_dscs = (struct dsc *)ty_malloc(sizeof(struct dsc) * 64,
					"dsc_alloc.free_dscs");
    while(idx < 63) {
      (free_dscs[idx].ptr).next = &free_dscs[idx + 1];
      idx++;
    }
    (free_dscs[idx].ptr).next = (struct dsc *)NULL;
  }

  ret = free_dscs;
  free_dscs = (free_dscs->ptr).next;

  bzero((VOID *)ret, sizeof(struct dsc));
  return(ret);
}

static void dsc_free(ptr)
    struct dsc *ptr;
{
  if(ptr != (struct dsc *)NULL) {
    (ptr->ptr).next = free_dscs;
    free_dscs = ptr;
  }
}

/* free all the data associated with the object. */
void free_obj(obj)
    struct obj_data *obj;
{
  ty_free((VOID *)obj->name);

  free_attributes(obj->attributes);
  ty_free((VOID *)obj);
}

/* Misc. flag checkers and such. */
static char _flagerr[] = "%s: bad obj passed as an argument, #%d\n";

INLINE int setFlag(obj, flag, word, macro)
    register int obj, flag, word;
    char *macro;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, macro, obj);
    return(-1);
  }
  (DSC_FLAGS(main_index[obj]))[word] |= flag;
  return(0);
}

INLINE int unsetFlag(obj, flag, word, macro)
    register int obj, flag, word;
    char *macro;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, macro, obj);
    return(-1);
  }
  (DSC_FLAGS(main_index[obj]))[word] &= ~flag;
  return(0);
}

INLINE int Flags(obj, flags, macro)
    register int obj, *flags;
    char *macro;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, macro, obj);
    return(0);
  }
  return(((flags[0] == 0) || (DSC_FLAG1(main_index[obj]) & flags[0]))
	 && ((flags[1] == 0) || (DSC_FLAG2(main_index[obj]) & flags[1])));
}

INLINE int Flag1(obj, flag, macro)
    register int obj, flag;
    const char *macro;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, macro, obj);
    return(0);
  }
  return(DSC_FLAG1(main_index[obj]) & flag);
}

INLINE int Flag2(obj, flag, macro)
    register int obj, flag;
    const char *macro;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, macro, obj);
    return(0);
  }
  return(DSC_FLAG2(main_index[obj]) & flag);
}

INLINE int Typeof(obj)
    register int obj;
{
  if (!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, "Typeof", obj);
    return (0);
  }
  return (DSC_TYPE(main_index[obj]));
}

/* yes, these are back. ARGH. */
INLINE void animate(obj)
    register int obj;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, "animate", obj);
  } else {
    DSC_FLAG2(main_index[obj]) |= ALIVE;
  }
}

INLINE void deanimate(obj)
    register int obj;
{
  if(!_exists_object(obj)) {
    logfile(LOG_ERROR, _flagerr, "deanimate", obj);
  } else {
    DSC_FLAG2(main_index[obj]) &= ~ALIVE;
  }
}

/* this routine shouldn't be called very much, it has no sanity checking. */
INLINE int getowner(obj)
    register int obj;
{
  return(DSC_OWNER(main_index[obj]));
}