untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
/*
        Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

/* configure all options BEFORE including system stuff. */
#include        "config.h"

#ifdef  DB_DBMFILE

#ifdef  OLDDBM
#ifdef WIN32
#include        "dbm.h"
#else
#include        <dbm.h>
#endif
#else
#ifdef WIN32
#include        "ndbm.h"
#else
#include        <ndbm.h>
#endif
#endif

#include        "mud.h"
#include        "sbuf.h"

/*
#define         DBMCHUNK_DEBUG
*/

/* default block size to use for bitmapped chunks */
#define DDDB_BLOCK      256


/* bitmap growth increment in BLOCKS not bytes (512 is 64 BYTES) */
#define DDDB_BITBLOCK   512


#define LOGICAL_BLOCK(off)      (off/bsiz)
#define BLOCK_OFFSET(block)     (block*bsiz)
#define BLOCKS_NEEDED(siz)      (siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz))



/*
dbm-based object storage routines. Somewhat trickier than the default
directory hash. a free space list is maintained as a bitmap of free
blocks, and an object-pointer list is maintained in a dbm database.
*/
struct hrec {
  off_t off;
  int siz;
};

static void dddb_mark (off_t lbn, int siz, int taken);
static void growbit (int maxblok);

static char *dbfile = DEFAULT_DBMCHUNKFILE;
static int bsiz = DDDB_BLOCK;
static int db_initted = 0;
static int last_free = 0;       /* last known or suspected free block */

#ifdef  OLDDBM
static int dbp = 0;
#else
static DBM *dbp = (DBM *) 0;
#endif

static FILE *dbf = (FILE *) 0;
static struct hrec hbuf;
static int startrav = 0;

static char *bitm = (char *) 0;
static int bitblox = 0;
static datum dat;
static datum key;


int dddb_init (void)
{
  static char *copen = "db_init cannot open ";
  char fnam[MAXPATHLEN];
  struct stat sbuf;

#ifdef  COMPRESS_OIF
  comp_init ();
  comp_on (1);
#endif

  /* now open chunk file */
  sprintf (fnam, "%s.db", dbfile);
  /* first try to open existing file then try opening new file */
  if ((dbf = fopen (fnam, "rb+")) == (FILE *) 0) {
    if ((dbf = fopen (fnam, "wb+")) == (FILE *) 0) {
      log_printf (copen, fnam, " ", (char *) -1, "\n", (char *) 0);
      return (1);
    }
  }


  /* open hash table */
#ifdef  OLDDBM
  if ((dbp = dbminit (dbfile)) < 0) {
    int fxp;

    /* dbm (old) is stupid about nonexistent files */
    if (errno != ENOENT) {
      log_printf (copen, dbfile, " ", (char *) -1, "\n", (char *) 0);
      return (1);
    }

    sprintf (fnam, "%s.dir", dbfile);
    fxp = open (fnam, O_CREAT | O_EXCL | O_WRONLY, S_IREAD | S_IWRITE);
    if (fxp < 0) {
      log_printf (copen, fnam, " ", (char *) -1, "\n", (char *) 0);
      return (1);
    }
    (void) close (fxp);

    sprintf (fnam, "%s.pag", dbfile);
    fxp = open (fnam, O_CREAT | O_EXCL | O_WRONLY, S_IREAD | S_IWRITE);
    if (fxp < 0) {
      log_printf (copen, fnam, " ", (char *) -1, "\n", (char *) 0);
      return (1);
    }
    (void) close (fxp);

    /* one MORE try */
    if ((dbp = dbminit (dbfile)) < 0) {
      log_printf (copen, dbfile, " ", (char *) -1, "\n", (char *) 0);
      return (1);
    }
  }
#else
  if ((dbp = dbm_open (dbfile, O_RDWR | O_CREAT, S_IREAD | S_IWRITE)) == (DBM *) 0) {
    log_printf (copen, dbfile, " ", (char *) -1, "\n", (char *) 0);
    return (1);
  }
#endif

  /* determine size of chunk file for allocation bitmap */
  sprintf (fnam, "%s.db", dbfile);
  if (stat (fnam, &sbuf)) {
    log_printf ("db_init cannot stat ", fnam, " ", (char *) -1, "\n",
      (char *) 0);
    return (1);
  }

  /* allocate bitmap */
  growbit (LOGICAL_BLOCK (sbuf.st_size) + DDDB_BITBLOCK);


#ifdef  OLDDBM
  key = firstkey ();
#else
  key = dbm_firstkey (dbp);
#endif
  while (key.dptr != (char *) 0) {
#ifdef  OLDDBM
    dat = fetch (key);
#else
    dat = dbm_fetch (dbp, key);
#endif
    if (dat.dptr == (char *) 0) {
      log_printf ("db_init index inconsistent\n", (char *) 0);
      return (1);
    }
    bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));    /* alignment */


    /* mark it as busy in the bitmap */
    dddb_mark (LOGICAL_BLOCK (hbuf.off), hbuf.siz, 1);


#ifdef  OLDDBM
    key = nextkey (key);
#else
    key = dbm_nextkey (dbp);
#endif
  }

  db_initted = 1;
  return (0);
}




int dddb_initted (void)
{
  return (db_initted);
}



int dddb_setbsiz (int nbsiz)
{
  return (bsiz = nbsiz);
}



int dddb_setfile (char *fil)
{
  char *xp;

  if (db_initted)
    return (1);

  /* KNOWN memory leak. can't help it. it's small */
  xp = (char *) malloc ((unsigned) strlen (fil) + 1);
  if (xp == (char *) 0)
    return (1);
  (void) strcpy (xp, fil);
  dbfile = xp;
  return (0);
}



int dddb_close (void)
{
  if (dbf != (FILE *) 0) {
    fclose (dbf);
    dbf = (FILE *) 0;
  }
#ifndef OLDDBM
  if (dbp != (DBM *) 0) {
    dbm_close (dbp);
    dbp = (DBM *) 0;
  }
#endif
  if (bitm != (char *) 0) {
    free ((mall_t) bitm);
    bitm = (char *) 0;
    bitblox = 0;
  }
  db_initted = 0;
  return (0);
}




/* grow the bitmap to given size */
static void growbit (int maxblok)
{
  int nsiz;
  char *nbit;

  /* round up to eight and then some */
  nsiz = (maxblok + 8) + (8 - (maxblok % 8));

  if (nsiz <= bitblox)
    return;

  /* this done because some old realloc()s are busted */
  nbit = (char *) malloc ((unsigned) (nsiz / 8));
  if (bitm != (char *) 0) {
    bcopy (bitm, nbit, (unsigned) ((bitblox / 8) - 1));
    free ((mall_t) bitm);
  }
  bitm = nbit;

  if (bitm == (char *) 0)
    fatal ("db_init cannot grow bitmap ", (char *) -1, "\n", (char *) 0);


  bzero (bitm + (bitblox / 8), ((nsiz / 8) - (bitblox / 8)) - 1);
  bitblox = nsiz - 8;
}



static void dddb_mark (off_t lbn, int siz, int taken)
{
  int bcnt;

  bcnt = BLOCKS_NEEDED (siz);

  /* remember a free block was here */
  if (!taken)
    last_free = lbn;

  while (bcnt--) {
    if (lbn >= bitblox - 32)
      growbit (lbn + DDDB_BITBLOCK);

    if (taken)
      bitm[lbn >> 3] |= (1 << (lbn & 7));
    else
      bitm[lbn >> 3] &= ~(1 << (lbn & 7));
    lbn++;
  }
}





static int dddb_alloc (int siz)
{
  int bcnt;                     /* # of blocks to operate on */
  int lbn;                      /* logical block offset */
  int tbcnt;
  int slbn;
  int overthetop = 0;

  lbn = last_free;
  bcnt = siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz);

  while (1) {
    if (lbn >= bitblox - 32) {
      /* only check here. can't break around the top */
      if (!overthetop) {
        lbn = 0;
        overthetop++;
      } else {
        growbit (lbn + DDDB_BITBLOCK);
      }
    }

    slbn = lbn;
    tbcnt = bcnt;

    while (1) {
      if ((bitm[lbn >> 3] & (1 << (lbn & 7))) != 0)
        break;

      /* enough free blocks - mark and done */
      if (--tbcnt == 0) {
        for (tbcnt = slbn; bcnt > 0; tbcnt++, bcnt--)
          bitm[tbcnt >> 3] |= (1 << (tbcnt & 7));

        last_free = lbn;
        return (slbn);
      }

      lbn++;
      if (lbn >= bitblox - 32)
        growbit (lbn + DDDB_BITBLOCK);
    }
    lbn++;
  }
}




Obj *dddb_get (char *nam)
{
  Obj *ret;

  if (!db_initted)
    return ((Obj *) 0);

  key.dptr = nam;
  key.dsize = strlen (nam) + 1;
#ifdef  OLDDBM
  dat = fetch (key);
#else
  dat = dbm_fetch (dbp, key);
#endif

  if (dat.dptr == (char *) 0)
    return ((Obj *) 0);
  bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));

  /* seek to location */
  if (fseek (dbf, (long) hbuf.off, 0))
    return ((Obj *) 0);

  /* if the file is badly formatted, ret == Obj * 0 */
  if ((ret = oiffromFILE (dbf, (char *) 0)) == (Obj *) 0)
    log_printf ("db_get: cannot decode ", nam, "\n", (char *) 0);
  return (ret);
}




int dddb_put (Obj * obj, char *nam)
{
  int nsiz;

  if (!db_initted)
    return (1);

  nsiz = oif_objsiz (obj, nam);

  key.dptr = nam;
  key.dsize = strlen (nam) + 1;

#ifdef  OLDDBM
  dat = fetch (key);
#else
  dat = dbm_fetch (dbp, key);
#endif

  if (dat.dptr != (char *) 0) {

    bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));    /* align */

    if (BLOCKS_NEEDED (nsiz) > BLOCKS_NEEDED (hbuf.siz)) {

#ifdef  DBMCHUNK_DEBUG
      printf ("put: %s old %d < %d - free %d\n", nam, hbuf.siz, nsiz,
        hbuf.off);
#endif
      /* mark free in bitmap */
      dddb_mark (LOGICAL_BLOCK (hbuf.off), hbuf.siz, 0);

      hbuf.off = BLOCK_OFFSET (dddb_alloc (nsiz));
      hbuf.siz = nsiz;
#ifdef  DBMCHUNK_DEBUG
      printf ("put: %s moved to offset %d, size %d\n", nam, hbuf.off,
        hbuf.siz);
#endif
    } else {
      hbuf.siz = nsiz;
#ifdef  DBMCHUNK_DEBUG
      printf ("put: %s replaced within offset %d, size %d\n", nam, hbuf.off,
        hbuf.siz);
#endif
    }
  } else {
    hbuf.off = BLOCK_OFFSET (dddb_alloc (nsiz));
    hbuf.siz = nsiz;
#ifdef  DBMCHUNK_DEBUG
    printf ("put: %s (new) at offset %d, size %d\n", nam, hbuf.off, hbuf.siz);
#endif
  }


  /* make table entry */
  dat.dptr = (char *) &hbuf;
  dat.dsize = sizeof (hbuf);

#ifdef  OLDDBM
  if (store (key, dat)) {
    log_printf ("db_put: can't store ", nam, " ", (char *) -1, "\n",
      (char *) 0);
    return (1);
  }
#else
  if (dbm_store (dbp, key, dat, DBM_REPLACE)) {
    log_printf ("db_put: can't dbm_store ", nam, " ", (char *) -1, "\n",
      (char *) 0);
    return (1);
  }
#endif

#ifdef  DBMCHUNK_DEBUG
  printf ("%s offset %d size %d\n", nam, hbuf.off, hbuf.siz);
#endif
  if (fseek (dbf, (long) hbuf.off, 0)) {
    log_printf ("db_put: can't seek ", nam, " ", (char *) -1, "\n",
      (char *) 0);
    return (1);
  }

  if (oiftoFILE (obj, dbf, nam) != 0 || fflush (dbf) != 0) {
    log_printf ("db_put: can't save ", nam, " ", (char *) -1, "\n",
      (char *) 0);
    return (1);
  }
  return (0);
}




int dddb_check (char *nam)
{

  if (!db_initted)
    return (0);

  key.dptr = nam;
  key.dsize = strlen (nam) + 1;
#ifdef  OLDDBM
  dat = fetch (key);
#else
  dat = dbm_fetch (dbp, key);
#endif

  if (dat.dptr == (char *) 0)
    return (0);
  return (1);
}




int dddb_del (char *nam, int flg)
{

  if (!db_initted)
    return (-1);

  key.dptr = nam;
  key.dsize = strlen (nam) + 1;
#ifdef  OLDDBM
  dat = fetch (key);
#else
  dat = dbm_fetch (dbp, key);
#endif


  /* not there? */
  if (dat.dptr == (char *) 0)
    return (0);
  bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));

  /* drop key from db */
#ifdef  OLDDBM
  if (delete (key)) {
#else
  if (dbm_delete (dbp, key)) {
#endif
    log_printf ("db_del: can't delete key ", nam, "\n", (char *) 0);
    return (1);
  }

  /* mark free space in bitmap */
  dddb_mark (LOGICAL_BLOCK (hbuf.off), hbuf.siz, 0);
#ifdef  DBMCHUNK_DEBUG
  printf ("del: %s free offset %d, size %d\n", nam, hbuf.off, hbuf.siz);
#endif

  /* mark object dead in file */
  if (fseek (dbf, (long) hbuf.off, 0))
    log_printf ("db_del: can't seek ", nam, " ", (char *) -1, "\n",
      (char *) 0);
  else {
    fprintf (dbf, "delobj");
    fflush (dbf);
  }

  return (0);
}



int dddb_travstart ()
{
  startrav = 0;
  return (0);
}



int dddb_traverse (char *obuf)
{
  if (!db_initted)
    return (0);

  if (!startrav) {
#ifdef  OLDDBM
    key = firstkey ();
#else
    key = dbm_firstkey (dbp);
#endif
    startrav = 1;
  } else
#ifdef  OLDDBM
    key = nextkey (key);
#else
    key = dbm_nextkey (dbp);
#endif
  if (key.dptr == (char *) 0)
    return (0);
  strncpy (obuf, key.dptr, MAXOID);
  return (1);
}



int dddb_travend ()
{
  startrav = 0;
  return (0);
}





int dddb_backup (char *out)
{
  FILE *ou;
  char vuf[BUFSIZ];
  int tor;

  if (!db_initted)
    return (1);

  if ((ou = fopen (out, "wb")) == (FILE *) 0) {
    log_printf ("db_backup: ", out, " ", (char *) -1, "\n", (char *) 0);
    return (1);
  }

#ifdef  OLDDBM
  key = firstkey ();
#else
  key = dbm_firstkey (dbp);
#endif
  while (key.dptr != (char *) 0) {
#ifdef  OLDDBM
    dat = fetch (key);
#else
    dat = dbm_fetch (dbp, key);
#endif
    if (dat.dptr == (char *) 0) {
      log_printf ("db_init index inconsistent\n", (char *) 0);
      continue;
    }

    bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));

    if (fseek (dbf, (long) hbuf.off, 0))
      log_printf ("db_backup: can't seek ", key.dptr, " ", (char *) -1, "\n",
        (char *) 0);
    else {

      /* copy out the object, sans interpretation */
      while (hbuf.siz > 0) {
        if ((tor = hbuf.siz) > sizeof (vuf))
          tor = sizeof (vuf);
        if (fread (vuf, sizeof (char), tor, dbf) <= 0) {
          log_printf ("db_backup: can't read ", key.dptr, " ", (char *) -1,
            "\n", (char *) 0);
          break;
        }

        /* what are we gonna do if it fails, anyhow? */
        fwrite (vuf, sizeof (char), tor, ou);
        hbuf.siz -= tor;
      }
    }

#ifdef  OLDDBM
    key = nextkey (key);
#else
    key = dbm_nextkey (dbp);
#endif
  }

  fclose (ou);
  return (0);
}

#endif // DB_DBMFILE