/* disk.c */
#include "copyright.h"
#include "config.h"
#include <stdio.h>
#ifdef STRING_H
#include <string.h>
#else
#include <strings.h>
#endif /* STRING_H */
#include <gdbm.h>
#include "teeny.h"
#include "db.h"
/*
* Interfaces TeenyMUSK to Gnu DBM. loosly based upon the old TeenyMUD disk.c
*
*/
char *snarf_str_field();
int *snarf_lock();
char *stuff_lock();
char *stuff_str_field();
GDBM_FILE chunkfd;
static char obj_id[128];
static int *obj_work = NULL;
static long work_siz = 0;
/*
* Write the object specified by the descriptor out to disk. Will not bother
* to freeze an object that isn't dirty.
*
*/
void disk_freeze(d)
struct dsc *d;
{
int *p;
char *q;
struct obj_data *dat;
int total_size, obj;
long ret;
datum key, cont;
if (!ResidentP(d)) {
warning("disk_freeze", "attempt to freeze non-resident object");
return;
}
dat = DSC_DATA(d);
obj = DSC_NUMBER(d);
/* Is the data for this thing dirty? If not, patch up its */
/* descriptor to point at the disk, and free the memory */
if (!DirtyP(d)) {
DSC_FLAGS(d) &= ~IN_MEMORY;
free_obj(dat);
return;
}
/* Step one. Write the thing into our workspace. */
if (work_siz == 0) { /* initialize our buffer */
obj_work = (int *) ty_malloc(10240 / sizeof(int), "disk_freeze");
work_siz = 10240 / sizeof(int);
}
if (DSC_SIZE(d) > work_siz) { /* grow it */
int *tmp;
tmp = (int *) realloc(obj_work, (DSC_SIZE(d) + 32));
if (tmp == NULL) {
log_error("disk_freeze: realloc of work buffer failed (%ld bytes), exiting\n", DSC_SIZE(d) + 32);
exit(-1);
}
obj_work = tmp;
work_siz = DSC_SIZE(d) + 32;
}
p = obj_work;
*p++ = dat->pennies;
*p++ = dat->loc;
*p++ = dat->contents;
*p++ = dat->exits;
*p++ = dat->rooms;
*p++ = dat->timestamp;
/* Stuff variable length integer arrays. */
/* NOTE: Order is important. These arrays *will* be correctly */
/* aligned, since they fall immediately after a mess of other */
/* integers. Effectively, we start with a huge array of ints. */
p = (int *) stuff_lock(p, dat->lock);
p = (int *) stuff_lock(p, dat->elock);
q = stuff_lock(p, dat->destinations);
/* Stuff variable length strings. Now alignment goes to hell */
/* Strings are zero terminated, so we don't care!!! */
q = stuff_str_field(q, dat->suc);
q = stuff_str_field(q, dat->osuc);
q = stuff_str_field(q, dat->fail);
q = stuff_str_field(q, dat->ofail);
q = stuff_str_field(q, dat->drop);
q = stuff_str_field(q, dat->odrop);
q = stuff_str_field(q, dat->desc);
q = stuff_str_field(q, dat->enter);
q = stuff_str_field(q, dat->oenter);
q = stuff_str_field(q, dat->oxent);
q = stuff_str_field(q, dat->leave);
q = stuff_str_field(q, dat->oleave);
q = stuff_str_field(q, dat->oxlea);
q = stuff_str_field(q, dat->idesc);
q = stuff_str_field(q, dat->odesc);
q = stuff_str_field(q, dat->site);
q = stuff_str_field(q, dat->password);
q = stuff_str_field(q, dat->kill);
q = stuff_str_field(q, dat->okill);
q = stuff_str_field(q, dat->otel);
q = stuff_str_field(q, dat->oxtel);
q = stuff_str_field(q, dat->efail);
q = stuff_str_field(q, dat->oefail);
/* Step two. How big is this bugger? Write it out to disk */
total_size = q - (char *) obj_work;
if (total_size != DSC_SIZE(d)) {
warning("disk_freeze", "computed disk size != stated");
}
(void) sprintf(obj_id, "#%d", obj);
key.dptr = obj_id;
key.dsize = strlen(obj_id) + 1;
cont.dptr = (char *) obj_work;
cont.dsize = total_size;
ret = gdbm_store(chunkfd, key, cont, GDBM_REPLACE);
if (ret != 0) {
warning("disk_freeze", "failed write");
}
/* Update descriptor */
DSC_FLAGS(d) &= ~IN_MEMORY;
/* Free up the memory used. */
free_obj(dat);
}
void free_obj(obj)
struct obj_data *obj;
{
ty_free((char *) (obj->lock));
ty_free((char *) (obj->elock));
ty_free((char *) (obj->destinations));
ty_free(obj->suc);
ty_free(obj->osuc);
ty_free(obj->fail);
ty_free(obj->ofail);
ty_free(obj->drop);
ty_free(obj->odrop);
ty_free(obj->desc);
ty_free(obj->enter);
ty_free(obj->oenter);
ty_free(obj->leave);
ty_free(obj->oleave);
ty_free(obj->idesc);
ty_free(obj->odesc);
ty_free(obj->site);
ty_free(obj->kill);
ty_free(obj->okill);
ty_free(obj->password);
ty_free(obj->otel);
ty_free(obj->oxtel);
ty_free(obj->oxent);
ty_free(obj->oxlea);
ty_free(obj->efail);
ty_free(obj->oefail);
ty_free((char *) obj);
}
/*
* Copies one of our internal arrays around. They start with a count of
* number of ints, and are followed by that many ints. Yow.
*
*/
char *
stuff_lock(dst, src)
int *dst, *src;
{
int count;
if (src == NULL) {
*dst++ = -1; /* Indicate empty lock */
return ((char *) dst);
}
count = *dst++ = *src++; /* The very first one is how many follow */
while (count > 0) {
*dst++ = *src++;
count--;
}
return ((char *) dst);
}
/*
* Really just copies a string around, and returns a pointer to the first
* byte after the string.
*
*/
char *
stuff_str_field(dst, src)
char *dst, *src;
{
int len;
if (src == NULL) {
/* Shove in a zero length string */
*dst++ = '\0';
return (dst);
}
len = strlen(src);
strcpy(dst, src);
return (dst + len + 1); /* plus one for the \0 */
}
/*
* Read the object specified by number in from disk.
*/
struct obj_data *
disk_thaw(d)
struct dsc *d;
{
struct obj_data *new_obj;
int len, obj;
char *q;
int *p;
datum key, cont;
extern void free();
if (ResidentP(d)) {
warning("disk_thaw", "attempt to re-thaw thawed object");
return (DSC_DATA(d));
}
obj = DSC_NUMBER(d);
(void) sprintf(obj_id, "#%d", obj);
key.dptr = obj_id;
key.dsize = strlen(obj_id) + 1;
cont = gdbm_fetch(chunkfd, key);
if (cont.dptr == NULL) {
warning("disk_thaw", "couldn't thaw chunk");
return (NULL);
}
/* OK. It's in memory. Snarf the data out */
len = cont.dsize;
new_obj = (struct obj_data *) ty_malloc(sizeof(struct obj_data)
,"disk_thaw");
p = (int *) (cont.dptr);
/* Fix up internal object data fields. */
new_obj->size = cont.dsize;
new_obj->descriptor = d;
new_obj->pennies = *p++;
new_obj->loc = *p++;
new_obj->contents = *p++;
new_obj->exits = *p++;
new_obj->rooms = *p++;
new_obj->timestamp = *p++;
/* Variable length field integer field. */
new_obj->lock = snarf_lock(&p);
new_obj->elock = snarf_lock(&p);
new_obj->destinations = snarf_lock(&p);
q = (char *) p;
len -= (q - (char *) (cont.dptr)); /* Subtract what we've chopped off */
/* Variable length string fields. */
new_obj->suc = snarf_str_field(&q, &len);
new_obj->osuc = snarf_str_field(&q, &len);
new_obj->fail = snarf_str_field(&q, &len);
new_obj->ofail = snarf_str_field(&q, &len);
new_obj->drop = snarf_str_field(&q, &len);
new_obj->odrop = snarf_str_field(&q, &len);
new_obj->desc = snarf_str_field(&q, &len);
new_obj->enter = snarf_str_field(&q, &len);
new_obj->oenter = snarf_str_field(&q, &len);
new_obj->oxent = snarf_str_field(&q, &len);
new_obj->leave = snarf_str_field(&q, &len);
new_obj->oleave = snarf_str_field(&q, &len);
new_obj->oxlea = snarf_str_field(&q, &len);
new_obj->idesc = snarf_str_field(&q, &len);
new_obj->odesc = snarf_str_field(&q, &len);
new_obj->site = snarf_str_field(&q, &len);
new_obj->password = snarf_str_field(&q, &len);
new_obj->kill = snarf_str_field(&q, &len);
new_obj->okill = snarf_str_field(&q, &len);
new_obj->otel = snarf_str_field(&q, &len);
new_obj->oxtel = snarf_str_field(&q, &len);
new_obj->efail = snarf_str_field(&q, &len);
new_obj->oefail = snarf_str_field(&q, &len);
/* Update the descriptor */
DSC_DATA(d) = new_obj;
DSC_FLAGS(d) |= IN_MEMORY;
DSC_FLAGS(d) &= ~DIRTY;
free((char *) cont.dptr);
return (new_obj);
}
/*
* Copy an array of our integers into new fresh memory.
*
* src had damn well better be aligned enough.
*
*/
int *
snarf_lock(src)
int **src;
{
int len;
int *p;
len = *(*src); /* First thing is how many there are to
* follow */
if (len == -1) { /* No lock, empty */
(*src)++;
return (NULL);
}
len++; /* Add one for the length field, of course */
p = (int *) ty_malloc((len * sizeof(int)), "snarf_lock");
/* Ignore returned value, we care not. */
(void) stuff_lock(p, *src);
*src += len;
return (p);
}
/*
* Grabs a variable length string field out of a buffer. String should be
* zero terminated.
*/
char *
snarf_str_field(src, remaining)
char **src;
int *remaining;
{
int len;
char *ret;
if (*remaining <= 0) {
warning("snarf_str_field", "disk data out of synch");
return (NULL);
}
if (**src == '\0') {
(*src)++;
(*remaining)--;
return (NULL);
}
len = strlen(*src) + 1;
ret = ty_malloc(len, "snarf_str_field");
*remaining -= len;
strcpy(ret, *src);
(*src) += len;
return (ret);
}
/*
* Open a chunkfile.
*/
void db_fatal();
void open_chunkfile(name)
char *name;
{
datum ver, key;
static char tmp[] = "version";
if ((chunkfd = gdbm_open(name, 0, GDBM_WRITER, 0600, db_fatal)) == NULL) {
fatal("open_chunkfile", "could not open chunk file");
}
key.dptr = tmp;
key.dsize = strlen(tmp) + 1;
ver = gdbm_fetch(chunkfd, key);
if (ver.dptr == NULL || strcmp(ver.dptr, version)) {
fatal("open_chunkfile", "database version does not match server version");
}
(void) free((char *) ver.dptr);
}
void init_chunkfile(name)
char *name;
{
datum ver, key;
static char tmp[] = "version";
if ((chunkfd = gdbm_open(name, 0, GDBM_NEWDB, 0600, db_fatal)) == NULL) {
fatal("init_chunkfile", "could not open chunk file");
}
key.dptr = tmp;
key.dsize = strlen(tmp) + 1;
ver.dptr = version;
ver.dsize = strlen(version) + 1;
if (gdbm_store(chunkfd, key, ver, GDBM_REPLACE) != 0)
warning("init_chunkfile", "couldn't store version");
}
void db_fatal(p)
char *p;
{
log_error("FATAL: GDBM died with message: %s\n", p);
exit(-1);
}
void close_chunkfile()
{
gdbm_close(chunkfd);
}
void disk_delete(num)
int num;
{
char tmp[64];
datum key;
(void) sprintf(tmp, "#%d", num);
key.dptr = tmp;
key.dsize = strlen(tmp) + 1;
(void) gdbm_delete(chunkfd, key);
}
/*
* Misc. flag checkers and such.
*/
extern struct dsc **main_index;
extern int total_objects;
int is_flag_set(obj, code)
int obj;
int code;
{
if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) {
log_error("is_flag_set: bad obj passed as an argument, %d\n", obj);
return 0;
}
return ((DSC_FLAGS(main_index[obj]) & code));
}
int isplayer(obj)
int obj;
{
if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) {
warning("isplayer", "bad obj passed as an argument");
return 0;
}
return (PlayerP(main_index[obj]));
}
int isroom(obj)
int obj;
{
if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) {
warning("isroom", "bad obj passed as an argument");
return 0;
}
return (RoomP(main_index[obj]));
}
int isexit(obj)
int obj;
{
if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) {
warning("isexit", "bad obj passed as an argument");
return 0;
}
return (ExitP(main_index[obj]));
}
int isthing(obj)
int obj;
{
if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) {
warning("isthing", "bad obj passed as an argument");
return 0;
}
return (ThingP(main_index[obj]));
}
int isgarbage(obj)
int obj;
{
return (obj >= 0 && obj < total_objects && main_index[obj] == NULL);
}
/* Utility routines with nowhere else to go. */
/*
* Drops an element from a list. You'd better be sure the element is actually
* on the list.
*/
void list_drop(elt, place, code)
int elt;
int place;
int code;
{
int current, last, next;
int listcode;
switch (code) {
case CONTENTS_LIST:
listcode = CONTENTS;
break;
case EXITS_LIST:
listcode = EXITS;
break;
case ROOMS_LIST:
listcode = ROOMS;
break;
default:
warning("list_drop", "bad list code");
return;
}
if (get_int_elt(place, listcode, ¤t) == -1) {
warning("list_drop", "bad list reference.");
return;
}
last = -1;
while (current != elt && current != -1) {
last = current;
if (get_int_elt(current, NEXT, ¤t) == -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;
}
}
}
/*
* Adds a thing to the top of the contents or exits list of the specified
* place.
*/
void list_add(thing, place, code)
int thing;
int place;
int code;
{
int list;
int listcode;
switch (code) {
case CONTENTS_LIST:
listcode = CONTENTS;
break;
case EXITS_LIST:
listcode = EXITS;
break;
case ROOMS_LIST:
listcode = ROOMS;
break;
default:
warning("list_add", "bad list code");
return;
}
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;
}
}