/*************************************************************************************************
 * Implementation of Curia
 *                                                      Copyright (C) 2000-2003 Mikio Hirabayashi
 * This file is part of QDBM, Quick Database Manager.
 * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License or any later version.  QDBM 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 Lesser General Public License for more
 * details.
 * You should have received a copy of the GNU Lesser General Public License along with QDBM; if
 * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307 USA.
 *************************************************************************************************/


#include "curia.h"
#include "myconf.h"

#define CR_NAMEMAX     512               /* max size of a database name */
#define CR_DPMAX       512               /* max number of division of a database */
#define CR_DIRMODE     00755             /* permission of a creating directory */
#define CR_FILEMODE    00644             /* permission of a creating file */
#define CR_PATHBUFSIZ  1024              /* size of a path buffer */
#define CR_DEFDNUM     5                 /* default number of division of a database */
#define CR_ATTRBNUM    16                /* bucket number of attrubute database */
#define CR_DPNAME      "depot"           /* name of each sub database */
#define CR_KEYEND      "end"             /* key of endian */
#define CR_KEYDNUM     "dnum"            /* key of division number */
#define CR_KEYLRNUM    "lrnum"           /* key of the number of large objects */
#define CR_LOBDIR      "lob"             /* name of the directory of large objects */
#define CR_LOBDDEPTH   2                 /* depth of the directories of large objects */


/* private function prototypes */
static int crbigendian(void);
static int crbyteswap(int num);
static char *crstrdup(const char *str);
static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz);
static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz);
static int crmklobdir(const char *path);
static int crrmlobdir(const char *path);
static int crwrite(int fd, const void *buf, int size);
static int crread(int fd, void *buf, int size);



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


/* Get a database handle. */
CURIA *cropen(const char *name, int omode, int bnum, int dnum){
  DEPOT *attr, **depots;
  char path[CR_PATHBUFSIZ], *tname, end;
  int i, j, dpomode, inode, lrnum;
  struct stat sbuf;
  CURIA *curia;
  assert(name);
  if(dnum < 1) dnum = CR_DEFDNUM;
  if(dnum > CR_DPMAX) dnum = CR_DPMAX;
  if(strlen(name) > CR_NAMEMAX){
    dpecode = DP_EMISC;
    return NULL;
  }
  dpomode = DP_OREADER;
  if(omode & CR_OWRITER){
    dpomode = DP_OWRITER;
    if(omode & CR_OCREAT) dpomode |= DP_OCREAT;
    if(omode & CR_OTRUNC) dpomode |= DP_OTRUNC;
  }
  if(omode & CR_ONOLCK) dpomode |= DP_ONOLCK;
  attr = NULL;
  lrnum = 0;
  if((omode & CR_OWRITER) && (omode & CR_OCREAT)){
    if(mkdir(name, CR_DIRMODE) == -1 && errno != EEXIST){
      dpecode = DP_EMKDIR;
      return NULL;
    }
    for(i = 0; i < dnum; i++){
      sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
      if(mkdir(path, CR_DIRMODE) == -1 && errno != EEXIST){
        dpecode = DP_EMKDIR;
        return NULL;
      }
    }
    sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
    if(!(attr = dpopen(path, dpomode, CR_ATTRBNUM))) return NULL;
    end = crbigendian();
    if(!dpput(attr, CR_KEYEND, -1, &end, 1, DP_DOVER) ||
       !dpput(attr, CR_KEYDNUM, -1, (char *)&dnum, sizeof(int), DP_DOVER)){
      dpclose(attr);
      return NULL;
    }
  }
  if(!attr){
    sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
    if(!(attr = dpopen(path, dpomode, 1))) return NULL;
    if(!(omode & CR_OTRUNC)){
      if((dnum = crdpgetnum(attr, CR_KEYDNUM, -1)) < 1 ||
         (lrnum = crdpgetnum(attr, CR_KEYLRNUM, -1)) < 0){
        dpclose(attr);
        dpecode = DP_EBROKEN;
        return NULL;
      }
    }
  }
  if(omode & CR_OTRUNC){
    for(i = 0; i < CR_DPMAX; i++){
      sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME);
      if(unlink(path) == -1 && errno != ENOENT){
        dpclose(attr);
        dpecode = DP_EUNLINK;
        return NULL;
      }
      sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_LOBDIR);
      if(!crrmlobdir(path)){
        dpclose(attr);
        return NULL;
      }
      if(i >= dnum){
        sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
        if(rmdir(path) == -1 && errno != ENOENT){
          dpclose(attr);
          dpecode = DP_ERMDIR;
          return NULL;
        }
      }
    }
    errno = 0;
  }
  if(stat(name, &sbuf) == -1){
    dpclose(attr);
    dpecode = DP_ESTAT;
    return NULL;
  }
  inode = sbuf.st_ino;
  if(!(depots = malloc(dnum * sizeof(DEPOT *)))){
    dpclose(attr);
    dpecode = DP_EALLOC;
    return NULL;
  }
  for(i = 0; i < dnum; i++){
    sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME);
    if(!(depots[i] = dpopen(path, dpomode, bnum))){
      for(j = 0; j < i; j++){
        dpclose(depots[j]);
      }
      free(depots);
      dpclose(attr);
      return NULL;
    }
  }
  curia = malloc(sizeof(CURIA));
  tname = crstrdup(name);
  if(!curia || !tname){
    free(curia);
    free(tname);
    for(i = 0; i < dnum; i++){
      dpclose(depots[i]);
    }
    free(depots);
    dpclose(attr);
    dpecode = DP_EALLOC;
    return NULL;
  }
  curia->name = tname;
  curia->wmode = (omode & CR_OWRITER);
  curia->inode = inode;
  curia->attr = attr;
  curia->depots = depots;
  curia->dnum = dnum;
  curia->inum = 0;
  curia->lrnum = lrnum;
  return curia;
}


/* Close a database handle. */
int crclose(CURIA *curia){
  int i, err;
  assert(curia);
  err = FALSE;
  for(i = 0; i < curia->dnum; i++){
    if(!dpclose(curia->depots[i])) err = TRUE;
  }
  free(curia->depots);
  if(curia->wmode){
    if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER))
      err = TRUE;
  }
  if(!dpclose(curia->attr)) err = TRUE;
  free(curia->name);
  free(curia);
  return err ? FALSE : TRUE;
}


/* Store a record. */
int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
  int dpdmode;
  int tnum;
  assert(curia && kbuf && vbuf);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  switch(dmode){
  case CR_DKEEP: dpdmode = DP_DKEEP; break;
  case CR_DCAT: dpdmode = DP_DCAT; break;
  default: dpdmode = DP_DOVER; break;
  }
  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
  return dpput(curia->depots[tnum], kbuf, ksiz, vbuf, vsiz, dpdmode);
}


/* Delete a record. */
int crout(CURIA *curia, const char *kbuf, int ksiz){
  int tnum;
  assert(curia && kbuf);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
  return dpout(curia->depots[tnum], kbuf, ksiz);
}


/* Retrieve a record. */
char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){
  int tnum;
  assert(curia && kbuf && start >= 0);
  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
  return dpget(curia->depots[tnum], kbuf, ksiz, start, max, sp);
}


/* Get the size of the value of a record. */
int crvsiz(CURIA *curia, const char *kbuf, int ksiz){
  int tnum;
  assert(curia && kbuf);
  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
  return dpvsiz(curia->depots[tnum], kbuf, ksiz);
}


/* Initialize the iterator of a database handle. */
int criterinit(CURIA *curia){
  int i, err;
  assert(curia);
  err = FALSE;
  for(i = 0; i < curia->dnum; i++){
    if(!dpiterinit(curia->depots[i])){
      err = TRUE;
      break;
    }
  }
  curia->inum = 0;
  return err ? FALSE : TRUE;
}


/* Get the next key of the iterator. */
char *criternext(CURIA *curia, int *sp){
  char *kbuf;
  assert(curia);
  kbuf = NULL;
  while(curia->inum < curia->dnum && !(kbuf = dpiternext(curia->depots[curia->inum], sp))){
    if(dpecode != DP_ENOITEM) return NULL;
    (curia->inum)++;
  }
  return kbuf;
}


/* Set alignment of a database handle. */
int crsetalign(CURIA *curia, int align){
  int i, err;
  assert(curia);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  err = FALSE;
  for(i = 0; i < curia->dnum; i++){
    if(!dpsetalign(curia->depots[i], align)){
      err = TRUE;
      break;
    }
  }
  return err ? FALSE : TRUE;
}


/* Synchronize contents of updating a database with the files and the devices. */
int crsync(CURIA *curia){
  int i, err;
  assert(curia);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  err = FALSE;
  if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
     !dpsync(curia->attr)) err = TRUE;
  for(i = 0; i < curia->dnum; i++){
    if(!dpsync(curia->depots[i])){
      err = TRUE;
      break;
    }
  }
  return err ? FALSE : TRUE;
}


/* Optimize a database. */
int croptimize(CURIA *curia, int bnum){
  int i, err;
  assert(curia);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  err = FALSE;
  for(i = 0; i < curia->dnum; i++){
    if(!dpoptimize(curia->depots[i], bnum)){
      err = TRUE;
      break;
    }
  }
  curia->inum = 0;
  return err ? FALSE : TRUE;
}


/* Get the name of a database. */
char *crname(CURIA *curia){
  char *name;
  assert(curia);
  if(!(name = crstrdup(curia->name))){
    dpecode = DP_EALLOC;
    return NULL;
  }
  return name;
}


/* Get the total size of database files. */
int crfsiz(CURIA *curia){
  int i, sum, rv;
  assert(curia);
  sum = 0;
  for(i = 0; i < curia->dnum; i++){
    rv = dpfsiz(curia->depots[i]);
    if(rv == -1) return -1;
    sum += rv;
  }
  return sum;
}


/* Get the total number of the elements of each bucket array. */
int crbnum(CURIA *curia){
  int i, sum, rv;
  assert(curia);
  sum = 0;
  for(i = 0; i < curia->dnum; i++){
    rv = dpbnum(curia->depots[i]);
    if(rv == -1) return -1;
    sum += rv;
  }
  return sum;
}


/* Get the total number of the used elements of each bucket array. */
int crbusenum(CURIA *curia){
  int i, sum, rv;
  assert(curia);
  sum = 0;
  for(i = 0; i < curia->dnum; i++){
    rv = dpbusenum(curia->depots[i]);
    if(rv == -1) return -1;
    sum += rv;
  }
  return sum;
}


/* Get the number of the records stored in a database. */
int crrnum(CURIA *curia){
  int i, sum, rv;
  assert(curia);
  sum = 0;
  for(i = 0; i < curia->dnum; i++){
    rv = dprnum(curia->depots[i]);
    if(rv == -1) return -1;
    sum += rv;
  }
  return sum;
}


/* Check whether a database handle is a writer or not. */
int crwritable(CURIA *curia){
  assert(curia);
  return curia->wmode;
}


/* Check whether a database has a fatal error or not. */
int crfatalerror(CURIA *curia){
  int i;
  assert(curia);
  if(dpfatalerror(curia->attr)) return TRUE;
  for(i = 0; i < curia->dnum; i++){
    if(dpfatalerror(curia->depots[i])) return TRUE;
  }
  return FALSE;
}


/* Get the inode number of a database directory. */
int crinode(CURIA *curia){
  assert(curia);
  return curia->inode;
}


/* Remove a database directory. */
int crremove(const char *name){
  struct stat sbuf;
  CURIA *curia;
  char path[CR_PATHBUFSIZ];
  assert(name);
  if(stat(name, &sbuf) == -1){
    dpecode = DP_ESTAT;
    return FALSE;
  }
  if((curia = cropen(name, CR_OWRITER | CR_OTRUNC, 1, 1)) != NULL) crclose(curia);
  sprintf(path, "%s%c0001%c%s", name, MYPATHCHR, MYPATHCHR, CR_DPNAME);
  dpremove(path);
  sprintf(path, "%s%c0001", name, MYPATHCHR);
  if(rmdir(path) == -1){
    dpecode = DP_ERMDIR;
    return FALSE;
  }
  sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
  if(!dpremove(path)) return FALSE;
  if(rmdir(name) == -1){
    dpecode = DP_ERMDIR;
    return FALSE;
  }
  return TRUE;
}


/* Convert a database directory for another platform with different byte order. */
int creconv(const char *name, int big){
  int i, mybig, dnum, lrnum, vsiz, loop;
  char path[CR_PATHBUFSIZ], fbig, *vbuf;
  DEPOT *depot;
  assert(name);
  mybig = crbigendian();
  sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
  if(!dpeconv(path, mybig)) return FALSE;
  if(!(depot = dpopen(path, DP_OWRITER, -1))) return FALSE;
  if(!(vbuf = dpget(depot, CR_KEYEND, strlen(CR_KEYEND), 0, -1, &vsiz)) ||
     vsiz != 1){
    free(vbuf);
    dpclose(depot);
    return FALSE;
  }
  fbig = *vbuf;
  free(vbuf);
  if(!(vbuf = dpget(depot, CR_KEYDNUM, strlen(CR_KEYDNUM), 0, -1, &vsiz)) ||
     vsiz != sizeof(int)){
    free(vbuf);
    dpclose(depot);
    return FALSE;
  }
  dnum = *(int *)vbuf;
  free(vbuf);
  if(!(vbuf = dpget(depot, CR_KEYLRNUM, strlen(CR_KEYLRNUM), 0, -1, &vsiz)) ||
     vsiz != sizeof(int)){
    free(vbuf);
    dpclose(depot);
    return FALSE;
  }
  lrnum = *(int *)vbuf;
  free(vbuf);
  if(mybig ? !fbig : fbig){
    dnum = crbyteswap(dnum);
    lrnum = crbyteswap(lrnum);
  }
  loop = dnum;
  if(mybig ? !big : big){
    dnum = crbyteswap(dnum);
    lrnum = crbyteswap(lrnum);
  }
  fbig = big;
  if(!dpput(depot, CR_KEYEND, strlen(CR_KEYEND), &fbig, 1, DP_DOVER) ||
     !dpput(depot, CR_KEYDNUM, strlen(CR_KEYDNUM), (char *)&dnum, sizeof(int), DP_DOVER) ||
     !dpput(depot, CR_KEYLRNUM, strlen(CR_KEYLRNUM), (char *)&lrnum, sizeof(int), DP_DOVER)){
    dpclose(depot);
    return FALSE;
  }
  if(!dpclose(depot)) return FALSE;
  if(!dpeconv(path, big)) return FALSE;
  for(i = 1; i <= loop; i++){
    sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i, MYPATHCHR, CR_DPNAME);
    if(!dpeconv(path, big)) return FALSE;
  }
  return TRUE;
}


/* Store a large object. */
int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
  char *path;
  int mode, fd, err, be;
  struct stat sbuf;
  assert(curia && kbuf && vbuf);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  if(ksiz < 0) ksiz = strlen(kbuf);
  if(vsiz < 0) vsiz = strlen(vbuf);
  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE;
  if(!crmklobdir(path)){
    free(path);
    return FALSE;
  }
  be = stat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode);
  mode = O_RDWR | O_CREAT;
  if(dmode & CR_DKEEP) mode |= O_EXCL;
  if(dmode & CR_DCAT){
    mode |= O_APPEND;
  } else {
    mode |= O_TRUNC;
  }
  if((fd = open(path, mode, CR_FILEMODE)) == -1){
    free(path);
    dpecode = DP_EOPEN;
    if(dmode == CR_DKEEP) dpecode = DP_EKEEP;
    return FALSE;
  }
  free(path);
  err = FALSE;
  if(crwrite(fd, vbuf, vsiz) == -1){
    err = TRUE;
    dpecode = DP_EWRITE;
  }
  if(close(fd) == -1){
    err = TRUE;
    dpecode = DP_ECLOSE;
  }
  if(!err && !be) (curia->lrnum)++;
  return err ? FALSE : TRUE;
}


/* Delete a large object. */
int croutlob(CURIA *curia, const char *kbuf, int ksiz){
  char *path;
  int err, be;
  struct stat sbuf;
  assert(curia && kbuf);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  if(ksiz < 0) ksiz = strlen(kbuf);
  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE;
  be = stat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode);
  err = FALSE;
  if(unlink(path) == -1){
    err = TRUE;
    dpecode = DP_ENOITEM;
  }
  free(path);
  if(!err && be) (curia->lrnum)--;
  return err ? FALSE : TRUE;
}


/* Retrieve a large object. */
char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){
  char *path, *buf;
  struct stat sbuf;
  int fd, size;
  assert(curia && kbuf && start >= 0);
  if(ksiz < 0) ksiz = strlen(kbuf);
  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return NULL;
  if((fd = open(path, O_RDONLY, CR_FILEMODE)) == -1){
    free(path);
    dpecode = DP_ENOITEM;
    return NULL;
  }
  free(path);
  if(fstat(fd, &sbuf) == -1){
    close(fd);
    dpecode = DP_ESTAT;
    return NULL;
  }
  if(start > sbuf.st_size){
    close(fd);
    dpecode = DP_ENOITEM;
    return NULL;
  }
  if(lseek(fd, start, SEEK_SET) == -1){
    close(fd);
    dpecode = DP_ESEEK;
    return NULL;
  }
  if(max < 0) max = sbuf.st_size;
  if(!(buf = malloc(max + 1))){
    close(fd);
    dpecode = DP_EALLOC;
    return NULL;
  }
  size = crread(fd, buf, max);
  close(fd);
  if(size == -1){
    free(buf);
    dpecode = DP_EREAD;
    return NULL;
  }
  buf[size] = '\0';
  if(sp) *sp = size;
  return buf;
}


/* Get the size of the value of a large object. */
int crvsizlob(CURIA *curia, const char *kbuf, int ksiz){
  char *path;
  struct stat sbuf;
  assert(curia && kbuf);
  if(ksiz < 0) ksiz = strlen(kbuf);
  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return -1;
  if(stat(path, &sbuf) == -1){
    free(path);
    dpecode = DP_ENOITEM;
    return -1;
  }
  free(path);
  return sbuf.st_size;
}


/* Get the number of the large objects stored in a database. */
int crrnumlob(CURIA *curia){
  assert(curia);
  return curia->lrnum;
}



/*************************************************************************************************
 * Functions for Experts
 *************************************************************************************************/


/* Synchronize updating contents on memory. */
int crmemsync(CURIA *curia){
  int i, err;
  assert(curia);
  if(!curia->wmode){
    dpecode = DP_EMODE;
    return FALSE;
  }
  err = FALSE;
  if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
     !dpmemsync(curia->attr)) err = TRUE;
  for(i = 0; i < curia->dnum; i++){
    if(!dpmemsync(curia->depots[i])){
      err = TRUE;
      break;
    }
  }
  return err ? FALSE : TRUE;

}



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


/* Check whether the byte order of the platform is big endian or not.
   The return value is true if bigendian, else, it is false. */
static int crbigendian(void){
  char buf[sizeof(int)];
  *(int *)buf = 1;
  return !buf[0];
}


/* Get a byte-reversed integer.
   `num' specifies an original integer.
   The return value is the swapped integer of the original integer. */
static int crbyteswap(int num){
  char buf[sizeof(int)];
  int i;
  for(i = 0; i < sizeof(int); i++){
    buf[i] = ((char *)&num)[sizeof(int)-i-1];
  }
  return *(int *)buf;
}


/* Get a copied string.
   `str' specifies an original string.
   The return value is a copied string whose region is allocated by `malloc'. */
static char *crstrdup(const char *str){
  int len;
  char *buf;
  assert(str);
  len = strlen(str);
  if(!(buf = malloc(len + 1))) return NULL;
  memcpy(buf, str, len + 1);
  return buf;
}


/* Get an integer from a database.
   `depot' specifies an inner database handle.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the key.
   The return value is the integer of the corresponding record. */
static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz){
  char *vbuf;
  int vsiz, rv;
  if(!(vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) || vsiz != sizeof(int)){
    free(vbuf);
    return INT_MIN;
  }
  rv = *(int *)vbuf;
  free(vbuf);
  return rv;
}


/* Get the path of a large object.
   `curia' specifies a database handle.
   `kbuf' specifies the pointer to the region of a key.
   `ksiz' specifies the size of the key.
   The return value is a path string whose region is allocated by `malloc'. */
static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz){
  char prefix[CR_PATHBUFSIZ], *wp, *path;
  int i, hash;
  assert(curia && kbuf && ksiz >= 0);
  wp = prefix;
  wp += sprintf(wp, "%s%c%04d%c%s%c",
                curia->name, MYPATHCHR, dpouterhash(kbuf, ksiz) % curia->dnum + 1,
                MYPATHCHR, CR_LOBDIR, MYPATHCHR);
  hash = dpinnerhash(kbuf, ksiz);
  for(i = 0; i < CR_LOBDDEPTH; i++){
    wp += sprintf(wp, "%02X%c", hash % 0x100, MYPATHCHR);
    hash /= 0x100;
  }
  if(!(path = malloc(strlen(prefix) + ksiz * 2 + 1))){
    dpecode = DP_EALLOC;
    return NULL;
  }
  wp = path;
  wp += sprintf(path, "%s", prefix);
  for(i = 0; i < ksiz; i++){
    wp += sprintf(wp, "%02X", ((unsigned char *)kbuf)[i]);
  }
  return path;
}


/* Create directories included in a path.
   `path' specifies a path.
   The return value is true if successful, else, it is false. */
static int crmklobdir(const char *path){
  char elem[CR_PATHBUFSIZ], *wp;
  const char *dp;
  int err, len;
  wp = elem;
  err = FALSE;
  while(*path != '\0' && (dp = strchr(path, MYPATHCHR)) != NULL){
    len = dp - path;
    if(wp != elem) wp += sprintf(wp, "%c", MYPATHCHR);
    memcpy(wp, path, len);
    wp[len] = '\0';
    wp += len;
    if(mkdir(elem, CR_DIRMODE) == -1 && errno != EEXIST) err = TRUE;
    path = dp + 1;
  }
  if(err) dpecode = DP_EMKDIR;
  return err ? FALSE : TRUE;
}


/* Remofe file and directories under a directory.
   `path' specifies a path.
   The return value is true if successful, else, it is false. */
static int crrmlobdir(const char *path){
  char elem[CR_PATHBUFSIZ];
  DIR *DD;
  struct dirent *dp;
  assert(path);
  if(unlink(path) != -1){
    return TRUE;
  } else {
    if(errno == ENOENT) return TRUE;
    if(!(DD = opendir(path))){
      dpecode = DP_EMISC;
      return FALSE;
    }
    while((dp = readdir(DD)) != NULL){
      if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
      sprintf(elem, "%s%c%s", path, MYPATHCHR, dp->d_name);
      if(!crrmlobdir(elem)){
        closedir(DD);
        return FALSE;
      }
    }
  }
  if(closedir(DD) == -1){
    dpecode = DP_EMISC;
    return FALSE;
  }
  if(rmdir(path) == -1){
    dpecode = DP_ERMDIR;
    return FALSE;
  }
  return TRUE;
}


/* Write into a file.
   `fd' specifies a file descriptor.
   `buf' specifies a buffer to write.
   `size' specifies the size of the buffer.
   The return value is the size of the written buffer, or, -1 on failure. */
static int crwrite(int fd, const void *buf, int size){
  char *lbuf;
  int rv, wb;
  assert(fd >= 0 && buf && size >= 0);
  lbuf = (char *)buf;
  rv = 0;
  do {
    wb = write(fd, lbuf, size);
    switch(wb){
    case -1: if(errno != EINTR) return -1;
    case 0: break;
    default:
      lbuf += wb;
      size -= wb;
      rv += wb;
      break;
    }
  } while(size > 0);
  return rv;
}


/* Read from a file and store the data into a buffer.
   `fd' specifies a file descriptor.
   `buffer' specifies a buffer to store into.
   `size' specifies the size to read with.
   The return value is the size read with, or, -1 on failure. */
static int crread(int fd, void *buf, int size){
  char *lbuf;
  int i, bs;
  assert(fd >= 0 && buf && size >= 0);
  lbuf = buf;
  for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){
    if(bs == -1 && errno != EINTR) return -1;
  }
  return i;
}



/* END OF FILE */
