/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* gdbm.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_MALLOC_H
#include <malloc.h>
#endif			/* HAVE_MALLOC_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 <gdbm.h>

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

/* TeenyMUD GNU dbm interface. */

static GDBM_FILE gfd;
static datum key, cont;
static int *obj_work = (int *)NULL;
static int work_siz = 0;

static void gdbm_panic _ANSI_ARGS_((char *));
INLINE static char *gdbm_pack_data _ANSI_ARGS_((char *, char *, int));
static char *gdbm_pack_lock _ANSI_ARGS_((char *, struct boolexp *));
INLINE static char *gdbm_pack_string _ANSI_ARGS_((char *, char *));
static void gdbm_emergency_growbuf _ANSI_ARGS_((void));
INLINE static char *gdbm_unpack_data _ANSI_ARGS_((char *, char *, int));
static char *gdbm_unpack_lock _ANSI_ARGS_((struct boolexp **, char *));
INLINE static char *gdbm_unpack_string _ANSI_ARGS_((char **, char *));

extern gdbm_error gdbm_errno;

#define ALIGN(p)        (((int)p) % sizeof(int) == 0 ? ((int)p) : \
                         ((int)p) + sizeof(int) - ((int)p) % sizeof(int))

/* This is simply a wrapper around panic(). */
static void gdbm_panic(mesg)
    char *mesg;
{
  panic("GDBM panic: %s\n", mesg);
}

int dbmfile_open(name, flags)
    char *name;
    int flags;
{
  int cache = 10;
  int gflags = 0;

  if (flags & TEENY_DBMFAST)
    gflags |= GDBM_FAST;
  if (flags & TEENY_DBMREAD) {
    gflags |= GDBM_READER;
  } else {
    gflags |= GDBM_WRITER;
  }

  if((gfd = gdbm_open(name, MEDBUFFSIZ, gflags, 0600, gdbm_panic)) == NULL) {
    logfile(LOG_ERROR, "dbmfile_open: couldn't open %s, %s.\n", name, 
    	    gdbm_strerror(gdbm_errno));
    return(-1);
  }
  if(gdbm_setopt(gfd, GDBM_CACHESIZE, &cache, sizeof(int)) == -1) {
    logfile(LOG_ERROR, "dbmfile_open: couldn't set cache size, %s.\n",
    	    gdbm_strerror(gdbm_errno));
    return(-1);
  }
  return(0);
}

int dbmfile_init(name, flags)
    char *name;
    int flags;
{
  int cache = 10;
  int gflags = GDBM_NEWDB;

  if (flags & TEENY_DBMFAST)
    gflags |= GDBM_FAST;

  if((gfd = gdbm_open(name, MEDBUFFSIZ, gflags, 0600, gdbm_panic)) == NULL) {
    logfile(LOG_ERROR, "dbmfile_init: couldn't open %s, %s.\n", name,
    	    gdbm_strerror(gdbm_errno));
    return(-1);
  }
  if(gdbm_setopt(gfd, GDBM_CACHESIZE, &cache, sizeof(int)) == -1) {
    logfile(LOG_ERROR, "dbmfile_init: couldn't set cache size, %s.\n",
    	    gdbm_strerror(gdbm_errno));
    return(-1);
  }
  return(0);
}

void dbmfile_close()
{
  gdbm_close(gfd);
}

INLINE static char *gdbm_pack_data(dest, source, size)
    char *dest, *source;
    int size;
{
  bcopy((VOID *)source, (VOID *)dest, size);
  return((char *)(dest + size));
}

static char *gdbm_pack_lock(dest, src)
    char *dest;
    struct boolexp *src;
{
  if(src == (struct boolexp *)NULL) {
    short type = BOOLEXP_END;		/* i just *had* to use short... */

    return(gdbm_pack_data(dest, (char *)&type, sizeof(short)));
  }

  dest = gdbm_pack_data(dest, (char *)&(src->type), sizeof(short));
  switch(src->type) {
  case BOOLEXP_AND:
  case BOOLEXP_OR:
    dest = gdbm_pack_lock((char *)ALIGN(dest), src->sub2);
  case BOOLEXP_NOT:
    dest = gdbm_pack_lock((char *)ALIGN(dest), src->sub1);
    break;
  case BOOLEXP_CONST:
    dest = gdbm_pack_data((char *)ALIGN(dest), (char *)&((src->dat).thing),
    			  sizeof(int));
    break;
  case BOOLEXP_FLAG:
    dest = gdbm_pack_data((char *)ALIGN(dest), (char *)((src->dat).flags),
    			  sizeof(int) * FLAGS_LEN);
    break;
  case BOOLEXP_ATTR:
    dest = gdbm_pack_string((char *)ALIGN(dest), (src->dat).atr[0]);
    dest = gdbm_pack_string((char *)ALIGN(dest), (src->dat).atr[1]);
    break;
  default:
    logfile(LOG_ERROR, "gdbm_pack_lock: bad boolexp type (%d)\n", src->type);
    dest[-1] = BOOLEXP_END;
  }
  return(dest);
}

INLINE static char *gdbm_pack_string(dest, src)
    register char *dest, *src;
{
  register int len;

  if(src == (char *)NULL){
    dest[0] = '\0';
    return((char *)(dest + 1));
  }

#ifdef notyet
  if(mudconf.enable_compress) {
    register char *csrc;

    csrc = compress(src);
    len = strlen(csrc)+1;
    bcopy((VOID *)csrc, (VOID *)dest, len);
    ty_free(csrc);
  } else {
    len = strlen(src)+1;
    bcopy((VOID *)src, (VOID *)dest, len);
  }
#else
  len = strlen(src)+1;
  bcopy((VOID *)src, (VOID *)dest, len);
#endif
  return((char *)(dest + len));
}

static void gdbm_emergency_growbuf()
{
  obj_work = (int *) ty_realloc(obj_work, work_siz + MEDBUFFSIZ,
  			        "gdbm_emergency_growbuf");
  work_siz += MEDBUFFSIZ;
}

int disk_freeze(data)
    struct obj_data *data;
{
  char *ptr;
  int *iptr;
  struct attr *attrs;
  int obj;
  register int idx;

  obj = data->objnum;

  if (!(DSC_FLAG2(main_index[obj]) & IN_MEMORY)) {
    logfile(LOG_ERROR,
	    "disk_freeze: attempt to freeze non-resident object #%d\n", obj);
    return(-1);
  }

  if ((work_siz == 0) && DSC_SIZE(main_index[obj]) < 10240) {
    /* initialize our buffer */
    obj_work = (int *) ty_malloc(10240L, "disk_freeze.obj_work");
    work_siz = 10240L;
  }
  if (DSC_SIZE(main_index[obj]) > work_siz) {   /* grow it */
    ty_free((VOID *) obj_work);
    obj_work = (int *) ty_malloc(DSC_SIZE(main_index[obj]) + MEDBUFFSIZ,
                                  "disk_freeze.obj_work");
    work_siz = DSC_SIZE(main_index[obj]) + MEDBUFFSIZ;
  }
  bzero((VOID *)obj_work, work_siz);

  /* malloc'd memory should always be aligned, damnit. */
  iptr = obj_work;
  *iptr++ = data->quota;
  *iptr++ = data->loc;
  *iptr++ = data->contents;
  *iptr++ = data->exits;
  *iptr++ = data->rooms;
  *iptr++ = data->timestamp;
  *iptr++ = data->created;
  *iptr++ = data->usecnt;
  *iptr++ = data->charges;
  *iptr++ = data->semaphores;
  *iptr++ = data->cost;
  *iptr++ = data->queue;

  /* attributes */
  *iptr++ = data->attr_total;
  ptr = (char *)iptr;
  for(idx = 0; idx < ATTR_WIDTH; idx++) {
    for(attrs = data->attributes[idx]; attrs != (struct attr *)NULL;
  	attrs = attrs->next) {
      ptr = gdbm_pack_data((char *)ALIGN(ptr), (char *)&attrs->type,
      			   sizeof(int));
      ptr = gdbm_pack_string((char *)ALIGN(ptr), attrs->name);
      if ((ptr - (char *)obj_work) > (work_siz - MEDBUFFSIZ))
        gdbm_emergency_growbuf();
      switch(attrs->type) {
      case ATTR_STRING:
        ptr = gdbm_pack_string((char *)ALIGN(ptr), (attrs->dat).str);
	break;
      case ATTR_LOCK:
        ptr = gdbm_pack_lock((char *)ALIGN(ptr), (attrs->dat).lock);
	break;
      }
      ptr = gdbm_pack_data((char *)ALIGN(ptr), (char *)&attrs->flags,
    			   sizeof(int));
      if ((ptr - (char *)obj_work) > (work_siz = MEDBUFFSIZ))
        gdbm_emergency_growbuf();
    }
  }

  /* strings and such. */
  ptr = gdbm_pack_string((char *)ALIGN(ptr), data->name);

  /* set it up. */
  key.dptr = (char *) &obj;
  key.dsize = sizeof(int);
  cont.dptr = (char *) obj_work;
  cont.dsize = ptr - (char *)obj_work;

  /* store it. */
  if(gdbm_store(gfd, key, cont, GDBM_REPLACE) != 0) {
    logfile(LOG_ERROR, "disk_freeze: failed to store object #%d, %s.\n", obj,
    	    gdbm_strerror(gdbm_errno));
    mudstat.cache_errors++;
    return(-1);
  }

  /* all done! */
  DSC_FLAG2(main_index[obj]) &= ~IN_MEMORY;
  free_obj(data);

  return(0);
}

INLINE static char *gdbm_unpack_data(dest, source, size)
    char *dest, *source;
    int size;
{
  bcopy((VOID *)source, (VOID *)dest, size);
  return((char *)(source + size));
}

static char *gdbm_unpack_lock(dest, source)
    struct boolexp **dest;
    char *source;
{
  short type;

  source = gdbm_unpack_data((char *)&type, source, sizeof(short));
  switch(type) {
  case BOOLEXP_END:
    *dest = (struct boolexp *)NULL;
    return(source);
  case BOOLEXP_AND:
  case BOOLEXP_OR:
    *dest = boolexp_alloc();
    (*dest)->type = type;

    source = gdbm_unpack_lock(&(*dest)->sub2, (char *)ALIGN(source));
    return(gdbm_unpack_lock(&(*dest)->sub1, (char *)ALIGN(source)));
  case BOOLEXP_NOT:
    *dest = boolexp_alloc();
    (*dest)->type = type;

    return(gdbm_unpack_lock(&(*dest)->sub1, (char *)ALIGN(source)));
  case BOOLEXP_CONST:
    *dest = boolexp_alloc();
    (*dest)->type = type;

    return(gdbm_unpack_data((char *)&((*dest)->dat).thing,
    	   (char *)ALIGN(source), sizeof(int)));
  case BOOLEXP_FLAG:
    *dest = boolexp_alloc();
    (*dest)->type = type;

    return(gdbm_unpack_data((char *)((*dest)->dat).flags, (char *)ALIGN(source),
    	   sizeof(int) * FLAGS_LEN));
  case BOOLEXP_ATTR:
    *dest = boolexp_alloc();
    (*dest)->type = type;

    source = gdbm_unpack_string(&(((*dest)->dat).atr[0]),
    				(char *)ALIGN(source));
    return(gdbm_unpack_string(&(((*dest)->dat).atr[1]), (char *)ALIGN(source)));
  default:
    logfile(LOG_ERROR, "gdbm_unpack_lock: bad boolexp type (%c)\n", *source);
    *dest = (struct boolexp *)NULL;
    return(source);
  }
}

INLINE static char *gdbm_unpack_string(dest, source)
    char **dest, *source;
{
  register char *ptr = source;

  if(ptr[0] == '\0') {
    *dest = (char *)NULL;
    return((char *)(source + 1));
  }

  while(ptr[0] != '\0')
    ptr++;
  ptr++;

#ifdef notyet
  *dest = uncompress(source);
#else
  *dest = ty_strdup(source, "gdbm_unpack_string.ret");
#endif
  return(ptr);
}

struct obj_data *disk_thaw(obj)
    int obj;
{
  struct obj_data *ret;
  int *iptr;
  char *ptr;
  register int attr_total, hashval;
  struct attr *curr;

  if (DSC_FLAG2(main_index[obj]) & IN_MEMORY) {
    logfile(LOG_ERROR, "disk_thaw: attempt to re-thaw thawed object #%d\n",
	    obj);
    return (DSC_DATA(main_index[obj]));
  }

  /* set it up. */
  key.dptr = (char *) &obj;
  key.dsize = sizeof(int);

  /* fetch it. */
  cont = gdbm_fetch(gfd, key);
  if(cont.dptr == (char *)NULL) {
    logfile(LOG_ERROR, "disk_thaw: couldn't fetch object #%d, %s.\n", obj,
    	    gdbm_strerror(gdbm_errno));
    return((struct obj_data *)NULL);
  }

  ret = (struct obj_data *) ty_malloc(sizeof(struct obj_data),
  					"disk_thaw.ret");
  ret->objnum = obj;

  /* gdbm data should be aligned. */
  iptr = (int *)cont.dptr;
  ret->quota = *iptr++;
  ret->loc = *iptr++;
  ret->contents = *iptr++;
  ret->exits = *iptr++;
  ret->rooms = *iptr++;
  ret->timestamp = *iptr++;
  ret->created = *iptr++;
  ret->usecnt = *iptr++;
  ret->charges = *iptr++;
  ret->semaphores = *iptr++;
  ret->cost = *iptr++;
  ret->queue = *iptr++;

  /* attributes. */
  bzero((VOID *)ret->attributes, sizeof(struct attr *) * ATTR_WIDTH);
  ret->attr_total = *iptr++;
  ptr = (char *)iptr;
  attr_total = ret->attr_total;

  while(attr_total) {
    curr = (struct attr *)ty_malloc(sizeof(struct attr), "disk_thaw.curr");

    ptr = gdbm_unpack_data((char *)&curr->type, (char *)ALIGN(ptr),
    			   sizeof(short));
    ptr = gdbm_unpack_string(&curr->name, (char *)ALIGN(ptr));
    switch(curr->type) {
    case ATTR_STRING:
      ptr = gdbm_unpack_string(&(curr->dat).str, (char *)ALIGN(ptr));
      break;
    case ATTR_LOCK:
      ptr = gdbm_unpack_lock(&(curr->dat).lock, (char *)ALIGN(ptr));
      break;
    }
    ptr = gdbm_unpack_data((char *)&curr->flags, (char *)ALIGN(ptr),
      			   sizeof(int));

    hashval = attr_hash(curr->name);
    curr->next = ret->attributes[hashval];
    if(ret->attributes[hashval] == (struct attr *)NULL)
      curr->prev = curr;
    else {
      curr->prev = (ret->attributes[hashval])->prev;
      (ret->attributes[hashval])->prev = curr;
    }
    ret->attributes[hashval] = curr;

    attr_total--;
  }

  /* strings and such. */
  ptr = gdbm_unpack_string(&ret->name, (char *)ALIGN(ptr));

  DSC_DATA(main_index[obj]) = ret;
  DSC_FLAG2(main_index[obj]) |= IN_MEMORY;
  DSC_FLAG2(main_index[obj]) &= ~DIRTY;

  /* don't make any assumptions about free() */
  free(cont.dptr);
  return(ret);
}

void disk_delete(data)
    struct obj_data *data;
{
  key.dptr = (char *) &data->objnum;
  key.dsize = sizeof(int);

  if(gdbm_exists(gfd, key)) {
    if(gdbm_delete(gfd, key) != 0) {
      logfile(LOG_ERROR, "disk_delete: couldn't delete object #%d, %s.\n",
	      data->objnum, gdbm_strerror(gdbm_errno));
    }
  }
}