/*******************************************
 * Copyright(c) 2000-2003 Mikio Hirabayashi
 *******************************************/


#include "hovel.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#undef TRUE
#define TRUE           1
#undef FALSE
#define FALSE          0

#define HV_INITBNUM    8191
#define HV_ALIGNSIZ    32


#if 0
int curia_bool = FALSE;
int curia_bnum = HV_INITBNUM;
int curia_dnum = -1;
int curia_align = HV_ALIGNSIZ;
int curia_mode = -1;
#else
int curia_bool = TRUE;
int curia_bnum = 2 * 1024 * 1024;
int curia_dnum = 16;
int curia_align = HV_ALIGNSIZ;
int curia_mode = 00755;
#endif


static int gdbm_geterrno(int ecode);



/*************************************************************************************************
 * public objects
 *************************************************************************************************/


char *gdbm_version = "Powered by QDBM";


gdbm_error gdbm_errno = GDBM_NO_ERROR;


char *gdbm_strerror(gdbm_error gdbmerrno){
  switch(gdbmerrno){
  case GDBM_NO_ERROR: return "No error";
  case GDBM_MALLOC_ERROR: return "Malloc error";
  case GDBM_BLOCK_SIZE_ERROR: return "Block size error";
  case GDBM_FILE_OPEN_ERROR: return "File open error";
  case GDBM_FILE_WRITE_ERROR: return "File write error";
  case GDBM_FILE_SEEK_ERROR: return "File seek error";
  case GDBM_FILE_READ_ERROR: return "File read error";
  case GDBM_BAD_MAGIC_NUMBER: return "Bad magic number";
  case GDBM_EMPTY_DATABASE: return "Empty database";
  case GDBM_CANT_BE_READER: return "Can't be reader";
  case GDBM_CANT_BE_WRITER: return "Can't be writer";
  case GDBM_READER_CANT_DELETE: return "Reader can't delete";
  case GDBM_READER_CANT_STORE: return "Reader can't store";
  case GDBM_READER_CANT_REORGANIZE: return "Reader can't reorganize";
  case GDBM_UNKNOWN_UPDATE: return "Unknown update";
  case GDBM_ITEM_NOT_FOUND: return "Item not found";
  case GDBM_REORGANIZE_FAILED: return "Reorganize failed";
  case GDBM_CANNOT_REPLACE: return "Cannot replace";
  case GDBM_ILLEGAL_DATA: return "Illegal data";
  case GDBM_OPT_ALREADY_SET: return "Option already set";
  case GDBM_OPT_ILLEGAL: return "Illegal option";
  }
  return "(invalid ecode)";
}


GDBM_FILE gdbm_open(char *name, int block_size, int read_write, int mode,
                    void (*fatal_func)(void)){
  GDBM_FILE dbf;
  int dpomode, cromode, flags, fd;
  DEPOT *depot;
  CURIA *curia;
  assert(name);
  dpomode = DP_OREADER;
  cromode = CR_OREADER;
  flags = O_RDONLY;
  if(read_write & GDBM_READER){
    dpomode = DP_OREADER;
    cromode = CR_OREADER;
    flags = O_RDONLY;
  } else if(read_write & GDBM_WRITER){
    dpomode = DP_OWRITER;
    cromode = CR_OWRITER;
    flags = O_RDWR;
  } else if(read_write & GDBM_WRCREAT){
    dpomode = DP_OWRITER | DP_OCREAT;
    cromode = CR_OWRITER | CR_OCREAT;
    flags = O_RDWR | O_CREAT;
  } else if(read_write & GDBM_NEWDB){
    dpomode = DP_OWRITER | DP_OCREAT | DP_OTRUNC;
    cromode = CR_OWRITER | CR_OCREAT | CR_OTRUNC;
    flags = O_RDWR | O_CREAT | O_TRUNC;
  } else {
    gdbm_errno = GDBM_ILLEGAL_DATA;
    return NULL;
  }
  depot = NULL;
  curia = NULL;
  if(curia_bool){
    if((cromode & CR_OWRITER) && curia_mode >= 0) mkdir(name, curia_mode);
    if(!(curia = cropen(name, cromode, curia_bnum, curia_dnum))){
      gdbm_errno = gdbm_geterrno(dpecode);
      return NULL;
    }
    crsetalign(curia, curia_align);
  } else {
    if(dpomode & DP_OWRITER){
      if((fd = open(name, flags | DP_OPENFLAGS, mode)) == -1 || close(fd) == -1){
        gdbm_errno = GDBM_FILE_OPEN_ERROR;
        return NULL;
      }
    }
    if(!(depot = dpopen(name, dpomode, HV_INITBNUM))){
      gdbm_errno = gdbm_geterrno(dpecode);
      return NULL;
    }
    if(dpomode & DP_OWRITER){
      if(!dpsetalign(depot, HV_ALIGNSIZ)){
        gdbm_errno = gdbm_geterrno(dpecode);
        dpclose(depot);
      }
    }
  }
  if(!(dbf = malloc(sizeof(GDBM)))){
    gdbm_errno = gdbm_geterrno(dpecode);
    if(depot) dpclose(depot);
    if(curia) crclose(curia);
    return NULL;
  }
  dbf->depot = depot;
  dbf->curia = curia;
  dbf->syncmode = (dpomode & DP_OWRITER) && (read_write & GDBM_SYNC) ? TRUE : FALSE;
  return dbf;
}


void gdbm_close(GDBM_FILE dbf){
  assert(dbf);
  if(dbf->depot){
    if(dbf->syncmode) dpsync(dbf->depot);
    dpclose(dbf->depot);
  }
  if(dbf->curia){
    if(dbf->syncmode) crsync(dbf->curia);
    crclose(dbf->curia);
  }
  free(dbf);
}


int gdbm_store(GDBM_FILE dbf, datum key, datum content, int flag){
  int dmode;
  assert(dbf);
  if(dbf->depot){
    if(!dpwritable(dbf->depot)){
      gdbm_errno = GDBM_READER_CANT_STORE;
      return -1;
    }
    if(!key.dptr || key.dsize < 0 || !content.dptr || content.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      return -1;
    }
    dmode = (flag == GDBM_INSERT) ? DP_DKEEP : DP_DOVER;
    if(!dpput(dbf->depot, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
      gdbm_errno = gdbm_geterrno(dpecode);
      if(dpecode == DP_EKEEP) return 1;
      return -1;
    }
    if(dbf->syncmode && !dpsync(dbf->depot)){
      gdbm_errno = gdbm_geterrno(dpecode);
      return -1;
    }
    return 0;
  } else if(dbf->curia){
    if(!crwritable(dbf->curia)){
      gdbm_errno = GDBM_READER_CANT_STORE;
      return -1;
    }
    if(!key.dptr || key.dsize < 0 || !content.dptr || content.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      return -1;
    }
    dmode = (flag == GDBM_INSERT) ? CR_DKEEP : CR_DOVER;
    if(!crput(dbf->curia, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
      gdbm_errno = gdbm_geterrno(dpecode);
      if(dpecode == DP_EKEEP) return 1;
      return -1;
    }
    if(dbf->syncmode && !crsync(dbf->curia)){
      gdbm_errno = gdbm_geterrno(dpecode);
      return -1;
    }
    return 0;
  }
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return -1;
}


int gdbm_delete(GDBM_FILE dbf, datum key){
  assert(dbf);
  if(dbf->depot){
    if(!dpwritable(dbf->depot)){
      gdbm_errno = GDBM_READER_CANT_DELETE;
      return -1;
    }
    if(!key.dptr || key.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      return -1;
    }
    if(!dpout(dbf->depot, key.dptr, key.dsize)) return -1;
    if(dbf->syncmode && !dpsync(dbf->depot)){
      gdbm_errno = gdbm_geterrno(dpecode);
      return -1;
    }
    return 0;
  } else if(dbf->curia){
    if(!crwritable(dbf->curia)){
      gdbm_errno = GDBM_READER_CANT_DELETE;
      return -1;
    }
    if(!key.dptr || key.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      return -1;
    }
    if(!crout(dbf->curia, key.dptr, key.dsize)) return -1;
    if(dbf->syncmode && !crsync(dbf->curia)){
      gdbm_errno = gdbm_geterrno(dpecode);
      return -1;
    }
    return 0;
  }
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return -1;
}


datum gdbm_fetch(GDBM_FILE dbf, datum key){
  datum content;
  char *vbuf;
  int vsiz;
  assert(dbf);
  if(dbf->depot){
    if(!key.dptr || key.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      content.dptr = NULL;
      content.dsize = 0;
      return content;
    }
    if(!(vbuf = dpget(dbf->depot, key.dptr, key.dsize, 0, -1, &vsiz))){
      gdbm_errno = gdbm_geterrno(dpecode);
      content.dptr = NULL;
      content.dsize = 0;
      return content;
    }
    content.dptr = vbuf;
    content.dsize = vsiz;
    return content;
  } else if(dbf->curia){
    if(!key.dptr || key.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      content.dptr = NULL;
      content.dsize = 0;
      return content;
    }
    if(!(vbuf = crget(dbf->curia, key.dptr, key.dsize, 0, -1, &vsiz))){
      gdbm_errno = gdbm_geterrno(dpecode);
      content.dptr = NULL;
      content.dsize = 0;
      return content;
    }
    content.dptr = vbuf;
    content.dsize = vsiz;
    return content;
  }
  content.dptr = NULL;
  content.dsize = 0;
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return content;
}


int gdbm_exists(GDBM_FILE dbf, datum key){
  assert(dbf);
  if(dbf->depot){
    if(!key.dptr || key.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      return FALSE;
    }
    if(dpvsiz(dbf->depot, key.dptr, key.dsize) == -1){
      gdbm_errno = gdbm_geterrno(dpecode);
      return FALSE;
    }
    return TRUE;
  } else if(dbf->curia){
    if(!key.dptr || key.dsize < 0){
      gdbm_errno = GDBM_ILLEGAL_DATA;
      return FALSE;
    }
    if(crvsiz(dbf->curia, key.dptr, key.dsize) == -1){
      gdbm_errno = gdbm_geterrno(dpecode);
      return FALSE;
    }
    return TRUE;
  }
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return FALSE;
}


datum gdbm_firstkey(GDBM_FILE dbf){
  datum key;
  assert(dbf);
  if(dbf->depot){
    if(dprnum(dbf->depot) < 1){
      gdbm_errno = GDBM_EMPTY_DATABASE;
      key.dptr = NULL;
      key.dsize = 0;
      return key;
    }
    dpiterinit(dbf->depot);
    return gdbm_nextkey(dbf, key);
  } else if(dbf->curia){
    if(crrnum(dbf->curia) < 1){
      gdbm_errno = GDBM_EMPTY_DATABASE;
      key.dptr = NULL;
      key.dsize = 0;
      return key;
    }
    criterinit(dbf->curia);
    return gdbm_nextkey(dbf, key);
  }
  key.dptr = NULL;
  key.dsize = 0;
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return key;
}


datum gdbm_nextkey(GDBM_FILE dbf, datum key){
  char *kbuf;
  int ksiz;
  if(dbf->depot){
    if(!(kbuf = dpiternext(dbf->depot, &ksiz))){
      gdbm_errno = gdbm_geterrno(dpecode);
      key.dptr = NULL;
      key.dsize = 0;
      return key;
    }
    key.dptr = kbuf;
    key.dsize = ksiz;
    return key;
  } else if(dbf->curia){
    if(!(kbuf = criternext(dbf->curia, &ksiz))){
      gdbm_errno = gdbm_geterrno(dpecode);
      key.dptr = NULL;
      key.dsize = 0;
      return key;
    }
    key.dptr = kbuf;
    key.dsize = ksiz;
    return key;
  }
  key.dptr = NULL;
  key.dsize = 0;
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return key;
}


void gdbm_sync(GDBM_FILE dbf){
  assert(dbf);
  if(dbf->depot){
    if(!dpsync(dbf->depot)) gdbm_errno = gdbm_geterrno(dpecode);
  } else if(dbf->curia){
    if(!crsync(dbf->curia)) gdbm_errno = gdbm_geterrno(dpecode);
  }
}


int gdbm_reorganize(GDBM_FILE dbf){
  assert(dbf);
  if(dbf->depot){
    if(!dpwritable(dbf->depot)){
      gdbm_errno = GDBM_READER_CANT_REORGANIZE;
      return -1;
    }
    if(!dpoptimize(dbf->depot, -1)){
      gdbm_errno = gdbm_geterrno(dpecode);
      return -1;
    }
    return 0;
  } else if(dbf->curia){
    if(!crwritable(dbf->curia)){
      gdbm_errno = GDBM_READER_CANT_REORGANIZE;
      return -1;
    }
    if(!croptimize(dbf->curia, -1)){
      gdbm_errno = gdbm_geterrno(dpecode);
      return -1;
    }
    return 0;
  }
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return -1;
}


int gdbm_fdesc(GDBM_FILE dbf){
  assert(dbf);
  if(dbf->depot) return dpfdesc(dbf->depot);
  gdbm_errno = GDBM_ILLEGAL_DATA;
  return -1;
}


int gdbm_setopt(GDBM_FILE dbf, int option, int *value, int size){
  assert(dbf);
  return 0;
}


void gdbm_setcuria(int bnum, int dnum, int align, int mode){
  curia_bool = TRUE;
  if(bnum > 0) curia_bnum = bnum;
  if(dnum > 0) curia_dnum = dnum;
  if(align >= 0) curia_align = align;
  if(mode >= 0) curia_mode = mode;
}



/*************************************************************************************************
 * private objects
 *************************************************************************************************/


static int gdbm_geterrno(int ecode){
  switch(ecode){
  case DP_ENOERR: return GDBM_NO_ERROR;
  case DP_EBROKEN: return GDBM_BAD_MAGIC_NUMBER;
  case DP_EKEEP: return GDBM_CANNOT_REPLACE;
  case DP_ENOITEM: return GDBM_ITEM_NOT_FOUND;
  case DP_EALLOC: return GDBM_MALLOC_ERROR;
  case DP_EMAP: return GDBM_MALLOC_ERROR;
  case DP_EOPEN: return GDBM_FILE_OPEN_ERROR;
  case DP_ESTAT: return GDBM_BAD_MAGIC_NUMBER;
  case DP_ESEEK: return GDBM_FILE_SEEK_ERROR;
  case DP_EREAD: return GDBM_FILE_READ_ERROR;
  case DP_EWRITE: return GDBM_FILE_WRITE_ERROR;
  case DP_EMKDIR: return GDBM_FILE_OPEN_ERROR;
  default: break;
  }
  return GDBM_ILLEGAL_DATA;
}



/* END OF FILE */
