/* 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]));
}