talker/
talker/bin/
talker/files/whois/
talker/update/
talker/update/bin/
/*
    dynamic.c   dynamic files (in theory)
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <unistd.h>
#include <fcntl.h>

/* just in case youre on a dippy system */

#ifndef S_IRUSR
 #define S_IRUSR 00400
#endif
#ifndef S_IWUSR
 #define S_IWUSR 00200
#endif


#include "config.h"
#include "player.h"
#include "dynamic.h"

/* External functions */

extern char *end_string(char *);
extern char *store_int(char *,int);
extern char *get_int(int *,char *);
extern dfile *room_df;
extern saved_player **saved_hash[];

/* throw the keylist to disk */

void dynamic_key_sync(dfile *df)
{
  int fd,length;
  char *oldstack,*to;
  oldstack=stack;

  store_int((char *)df->keylist,df->first_free_block);
  store_int((char *)(df->keylist+1),df->first_free_key);

/* doing a straight open could mean losing the key data
   dont risk it by moving the file first
   this is grossly slow, so provide a means of escaping it */

  sprintf(oldstack,"files/%s/keys",df->fname);
  if (!(sys_flags&SECURE_DYNAMIC)) {
    stack=end_string(oldstack);
    to=stack;
    sprintf(to,"files/%s/keys.b",df->fname);
    rename(oldstack,to);
  }
  fd=open(oldstack,O_CREAT|O_WRONLY|O_TRUNC,S_IRUSR|S_IWUSR);
  if (fd<0) handle_error("Failed to open key file");
  length=(df->nkeys+1)*8;
  if (write(fd,df->keylist,length)!=length)
    handle_error("Failed to write key data");
  close(fd);    
  
  stack=oldstack;  
}


/* set up a dfile structure */

dfile *dynamic_init(char *file,int granularity)
{
  dfile *df;
  char *oldstack;
  int fd,length,i,*fill;
  oldstack=stack;

  if (sys_flags&VERBOSE) {
    sprintf(oldstack,"Loading dynamic file '%s'",file);
    stack=end_string(oldstack);
    log("sync",oldstack);
    stack=oldstack;
  }
  
  df=(dfile *)MALLOC(sizeof(dfile));
  memset(df,0,sizeof(dfile));
  df->granularity=granularity;
  sprintf(oldstack,"%skeys",rc_options->prooms_path);
  fd=open(oldstack,O_RDONLY|O_NDELAY);
  /* just in case this is the first time round */
  
  if (fd<0) {
    printf("in if fd<0\n");
    sprintf(oldstack,"Failed to load room keys");
    stack=end_string(oldstack);
    log("error",oldstack);
    stack=oldstack;
    df->first_free_block=0;
    df->first_free_key=0;
    df->nkeys=0;
    df->keylist=0;
  }
  else {
    length=lseek(fd,0,SEEK_END);
    lseek(fd,0,SEEK_SET);
    if ((length%8)!=0) handle_error("Corrupt key data");
    df->nkeys=(length/8)-1;
    df->keylist=(int *)MALLOC(length);
    if (read(fd,df->keylist,length)!=length)
      handle_error("Failed to read keys");
    close(fd);
    (void)get_int(&(df->first_free_block),(char *)df->keylist);
    (void)get_int(&(df->first_free_key),(char *)(df->keylist+1));
  }
  /* keep the data file open all the time */
  
  sprintf(oldstack,"%sdata",rc_options->prooms_path);
  df->data_fd=open(oldstack,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
  if (df->data_fd<0) handle_error("Failed to open dynamic data file");
  lseek(df->data_fd,0,SEEK_SET);
  stack=oldstack;
  return df; 
}

/* free up a dfile structure */

void dynamic_close(dfile *df)
{
  dynamic_key_sync(df);
  close(df->data_fd);
  free(df->keylist);
  free(df);
}

/* get the block and length info from the keylist */

int convert_key(dfile *df,int key,int *block,int *length)
{
  if ((key<=0) || (key>(df->nkeys-1))) return 0;
  key*=2;
  (void) get_int(block,(char *)(df->keylist+key));
  if (*block<=0) return 0;
  (void) get_int(length,(char *)(df->keylist+key+1));
  if (*length<=0) return 0;
  return 1;
}

/* grab a block from the data file */

int load_block(dfile *df,int *block,char *data)
{
  if (*block<=0) return 0;
  dynamic_seek_block(df,*block);
  if (read(df->data_fd,data,4)!=4) handle_error("Failed to read next block 1");
  get_int(block,data);
  if (read(df->data_fd,data,df->granularity-4)!=(df->granularity-4))
    handle_error("Failed to read block");
  return 1;
}

/* load an entire entry from the data file */

int dynamic_load(dfile *df,int key,char *data)
{
  int block,length,l;
/*  printf("Dynamic load key=%d\n",key);  */
  if (!convert_key(df,key,&block,&length)) return -1;
  l=length;
/*    printf("DL block %d\n",block);  */
  while(l>0) {
    if (!load_block(df,&block,data))
      handle_error("Failed to load block of data");
/*    printf("DL block %d\n",block);  */
    l-=(df->granularity-4);
    data+=(df->granularity-4);
  }
  return length;
}

/* returns a free key, if necessary it will go and create
   some new key space (in chunks of 100 keys at a time) */

int dynamic_find_free_key(dfile *df)
{
  int *newlist,oldnkeys,i,key;
  if (!df->first_free_key) {
    oldnkeys=df->nkeys;
    df->nkeys+=100;
    newlist=(int *)MALLOC((df->nkeys+1)*8);
    if (df->keylist) {
      memcpy(newlist+2,df->keylist+2,(oldnkeys+1)*8);
      FREE(df->keylist);
    }
    df->keylist=newlist;
    df->first_free_key=oldnkeys+1;
    newlist+=df->first_free_key*2;
    for(i=-df->first_free_key-1;i>=-df->nkeys;i--) {
      newlist=(int *)store_int((char *)newlist,i);
      newlist=(int *)store_int((char *)newlist,0);
    }
    newlist=(int *)store_int((char *)newlist,0);
    newlist=(int *)store_int((char *)newlist,0);
  }
  key=df->first_free_key;
  (void) get_int(&(df->first_free_key),(char *)(df->keylist+key*2));
  df->first_free_key=-df->first_free_key;
  return key;
}

/* read in the index from the start of a block */

int dynamic_get_next_block(dfile *df,int block)
{
  int next_block,ret;
  if (block<=0) return;
  dynamic_seek_block(df,block);
  ret=read(df->data_fd,stack,4);
  if (!ret) return 0;
  if (ret!=4) handle_error("Failed to read next block 2");
  get_int(&next_block,stack);
  return next_block;
}

/* go through all the blocks in a file and free them up */

void dynamic_free(dfile *df,int key)
{
  char *oldstack;
  int block,length,next_block;

  if (!convert_key(df,key,&block,&length))
     return;
  (void) store_int((char *)(df->keylist+key*2),-df->first_free_key);
  (void) store_int((char *)(df->keylist+key*2+1),0);
  df->first_free_key=key;

/* simply keep adding blocks onto the top of the free list */
  
   while(length>0)
   {
      if (block<=0)
         return;
      next_block = dynamic_get_next_block(df,block);
      dynamic_seek_block(df,block);
      store_int(stack,-df->first_free_block);
      if (write(df->data_fd,stack,4)!=4)
         handle_error("Failed to write next block");
      df->first_free_block=block;
      block = next_block;
      length -= (df->granularity-4);
   }
   if (!(sys_flags&SECURE_DYNAMIC))
      dynamic_key_sync(df);
}

/* go hunting for a free block, if necessary, create enough
   space on the end of the file */

int dynamic_find_free_block(dfile *df)
{
  int length,new_block;
/*  printf("Called find_free_block ffb=%d\n",df->first_free_block); */
  if (df->first_free_block) {
    new_block=df->first_free_block;
    df->first_free_block=-dynamic_get_next_block(df,new_block);
  }
  else {
    length=lseek(df->data_fd,0,SEEK_END);
    memset(stack,0,df->granularity);
    if (!length) {
      if (write(df->data_fd,stack,df->granularity)!=df->granularity)
        handle_error("Failed to make data block");
      length=df->granularity;
    }
    if (write(df->data_fd,stack,df->granularity)!=df->granularity)
      handle_error("Failed to make data block");
    new_block=(length/df->granularity);
  }
  return new_block;
}

/* save an entry to the data file */

int dynamic_save(dfile *df,char *data,int l,int key)
{
  int block,length,next_block,blength,free_block;

/* key==0 means this is a new entry */
  
  if (!key) {
    key=dynamic_find_free_key(df);
    block=dynamic_find_free_block(df);
  }
  else if (!convert_key(df,key,&block,&length)) return;
  (void) store_int((char *)(df->keylist+key*2),block);
  (void) store_int((char *)(df->keylist+key*2+1),l);  
  blength=df->granularity-4;
/*  printf("Dynamic Save key=%d\n",key); */
  while(l>0) {
    next_block=dynamic_get_next_block(df,block);
/*    printf("DS block %d / next block %d\n",block,next_block);  */

/* do we need more space ? */

    if (l>blength) {
      if (next_block<=0) next_block=dynamic_find_free_block(df);
    }
    else {

/* can we free up any blocks at the end ? */
    
      free_block=next_block;
      while(free_block>0) {
        next_block=dynamic_get_next_block(df,free_block);
        dynamic_seek_block(df,free_block);
        store_int(stack,-df->first_free_block);
        if (write(df->data_fd,stack,4)!=4)
          handle_error("Failed to write next block");
        df->first_free_block=free_block;
        free_block=next_block;
      }
      next_block=0;
    }
    store_int(stack,next_block);
    dynamic_seek_block(df,block);
    if (write(df->data_fd,stack,4)!=4)
      handle_error("Failed to write next block");
    if (write(df->data_fd,data,blength)!=blength)
      handle_error("Failed to write block data");
    block=next_block;
    l-=blength;
    data+=blength;
  }

  /* make sure the keylist is up to date */
  
  if (!(sys_flags&SECURE_DYNAMIC)) dynamic_key_sync(df);
  return key;
}