/* #include_copyright"db.h"
 * GPL, Free software, NO WARRANTY && stuff, (c) Petr Baudis <pasky@ji.cz>
 * included hashing code which i didn't done, see comments there
 */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>

#define hashsize(n) ((u_int32_t)1<<(n))
#define hashmask(n) (hashsize(n)-1)

 /*
  *  --------------------------------------------------------------------
  *   mix -- mix 3 32-bit values reversibly.
  *   For every delta with one or two bits set, and the deltas of all three
  *     high bits or all three low bits, whether the original value of a,b,c
  *     is almost all zero or is uniformly distributed,
  *   * If mix() is run forward or backward, at least 32 bits in a,b,c
  *     have at least 1/4 probability of changing.
  *   * If mix() is run forward, every bit of c will change between 1/3 and
  *     2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
  *     mix() was built out of 36 single-cycle latency instructions in a
  *     structure that could supported 2x parallelism, like so:
  *       a -= b;
  *       a -= c; x = (c>>13);
  *       b -= c; a ^= x;
  *       b -= a; x = (a<<8);
  *       c -= a; b ^= x;
  *       c -= b; x = (b>>13);
  *       ...
  *     Unfortunately, superscalar Pentiums and Sparcs can't take advantage
  *     of that parallelism.  They've also turned some of those single-cycle
  *     latency instructions into multi-cycle latency instructions.  Still,
  *     this is the fastest good hash I could find.  There were about 2^^68
  *     to choose from.  I only looked at a billion or so.
  *  --------------------------------------------------------------------
  */

 #define mix(a,b,c) \
 { \
   a -= b; a -= c; a ^= (c>>13); \
   b -= c; b -= a; b ^= (a<<8); \
   c -= a; c -= b; c ^= (b>>13); \
   a -= b; a -= c; a ^= (c>>12);  \
   b -= c; b -= a; b ^= (a<<16); \
   c -= a; c -= b; c ^= (b>>5); \
   a -= b; a -= c; a ^= (c>>3);  \
   b -= c; b -= a; b ^= (a<<10); \
   c -= a; c -= b; c ^= (b>>15); \
 }

 /*
 --------------------------------------------------------------------
 hash() -- hash a variable-length key into a 32-bit value
   k       : the key (the unaligned variable-length array of bytes)
   len     : the length of the key, counting by bytes
   initval : can be any 4-byte value
 Returns a 32-bit value.  Every bit of the key affects every bit of
 the return value.  Every 1-bit and 2-bit delta achieves avalanche.
 About 6*len+35 instructions.

 The best hash table sizes are powers of 2.  There is no need to do
 mod a prime (mod is sooo slow!).  If you need less than 32 bits,
 use a bitmask.  For example, if you need only 10 bits, do
   h = (h & hashmask(10));
 In which case, the hash table should have hashsize(10) elements.

 If you are hashing n strings (ub1 **)k, do it like this:
   for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);

 By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
 code any way you wish, private, educational, or commercial.  It's free.

 See http://burtleburtle.net/bob/hash/evahash.html
 Use for hash table lookup, or anything where one collision in 2^^32 is
 acceptable.  Do NOT use for cryptographic purposes.

 :'<,'>s/k\[\([^\]]*\)\]/tolower(k[\1])/g applied by pasky, because we
 don't want case-sensitive hash
 --------------------------------------------------------------------
 */

 u_int32_t _hash( k, length, initval)
 register u_int8_t *k;        /* the key */
 register u_int32_t  length;   /* the length of the key */
 register u_int32_t  initval;  /* the previous hash, or an arbitrary value */
 {
    register u_int32_t a,b,c,len;

    /* Set up the internal state */
    len = length;
    a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
    c = initval;         /* the previous hash value */

    /*---------------------------------------- handle most of the key */
    while (len >= 12)
    {
       a += (tolower(k[0]) +((u_int32_t)tolower(k[1])<<8) +((u_int32_t)tolower(k[2])<<16) +((u_int32_t)tolower(k[3])<<24));
       b += (tolower(k[4]) +((u_int32_t)tolower(k[5])<<8) +((u_int32_t)tolower(k[6])<<16) +((u_int32_t)tolower(k[7])<<24));
       c += (tolower(k[8]) +((u_int32_t)tolower(k[9])<<8) +((u_int32_t)tolower(k[10])<<16)+((u_int32_t)tolower(k[11])<<24));
       mix(a,b,c);
       k += 12; len -= 12;
    }

    /*------------------------------------- handle the last 11 bytes */
    c += length;
    switch(len)              /* all the case statements fall through */
    {
    case 11: c+=((u_int32_t)tolower(k[10])<<24);
    case 10: c+=((u_int32_t)tolower(k[9])<<16);
    case 9 : c+=((u_int32_t)tolower(k[8])<<8);
       /* the first byte of c is reserved for the length */
    case 8 : b+=((u_int32_t)tolower(k[7])<<24);
    case 7 : b+=((u_int32_t)tolower(k[6])<<16);
    case 6 : b+=((u_int32_t)tolower(k[5])<<8);
    case 5 : b+=tolower(k[4]);
    case 4 : a+=((u_int32_t)tolower(k[3])<<24);
    case 3 : a+=((u_int32_t)tolower(k[2])<<16);
    case 2 : a+=((u_int32_t)tolower(k[1])<<8);
    case 1 : a+=tolower(k[0]);
      /* case 0: nothing left to add */
    }
    mix(a,b,c);
    /*-------------------------------------------- report the result */
    return c;
 }


/* and the rest is may own work ;P --pasky */

#define do_hash(key, len, ini) (_hash(key, len, ini) & hashmask(16))

typedef struct t_hash {
  char *key;
  char *val;
  struct t_hash *next;
  int volume; /* non-zero == keep searching even if matched, more to come */
  	      /* it seems we aren't using it anyway */
#define HASH_ATTR_NONE		0
#define HASH_ATTR_RDONLY	1 /* read-only */
#define HASH_ATTR_SECRET	16 /* only to msg, no forwarding */
  unsigned long attr; /* attrs */
  char *owner; /* owner of the pair (nick) */
} hash_t;

typedef struct {
  hash_t hash[65536];
  int keys;
} db_t;

void
db_assign(db_t *db, char *key, char *val, unsigned long attr, int overwrite, char *nick) {
  u_int16_t hash;
  hash_t *ht;

  hash=do_hash(key, strlen(key), 0x3031337);
  
  ht=&db->hash[hash];

  Context;
  
  while(ht->key) {
    if (!strcasecmp(ht->key,key)) {
      if (!overwrite) {
        Context;
        nfree(ht->key);
        nfree(ht->val);
	if (ht->owner) nfree(ht->owner);
        break;
      } else {
	ht->volume=1;
	overwrite--;
      }
    }
    if (!ht->next) {
      Context;
      ht->next=nmalloc(sizeof(hash_t));
      ht=ht->next;
      ht->key=ht->val=ht->next=ht->owner=NULL;
    } else {
      ht=ht->next;
    }
  }
  Context;

  ht->key=mystrdup(key);
  ht->val=mystrdup(val);
  ht->owner=nick?mystrdup(nick):NULL;
  ht->attr=attr;
  ht->volume=1;

  Context;
  return;
}

hash_t **
db_lookup(db_t *db, char *key, int *records) {
  u_int16_t hash;
  hash_t *ht, **htl;

  hash=do_hash(key, strlen(key), 0x3031337);
  
  ht=&db->hash[hash];

  Context;

  *records=0; htl=NULL;

  while(ht->key) {
    if (!strcasecmp(ht->key, key)) {
      Context;
      htl=nrealloc(htl, ((*records)+1)*sizeof(hash_t *));
      htl[*records]=ht;
      (*records)++;
      if (!ht->volume) break;
    }
    if (!ht->next) {
      break;
    } else {
      ht=ht->next;
    }
  }
  
  Context;
  return htl;
}

void
db_unassign(db_t *db, char *key, int index) {
  u_int16_t hash;
  hash_t *ht, *htp;

  hash=do_hash(key, strlen(key), 0x3031337);
  
  ht=htp=&db->hash[hash];

  Context;

  while(ht->key) {
    if (!strcasecmp(ht->key,key) && !(index--)) {
      break;
    }
    if (!ht->next) {
      return;
    } else {
      htp=ht;
      ht=ht->next;
    }
  }

  nfree(ht->key); ht->key=NULL;
  nfree(ht->val); ht->val=NULL;
  if (ht->owner) nfree(ht->owner); ht->owner=NULL;
  ht->volume=ht->attr=0;
  if (ht!=htp) {
    htp->next=ht->next;
    nfree(ht);
  } else {
    /* FIXME: I'm lazy now and i want to done this all at last ;P maybe later */
    ht->key=nmalloc(1); *ht->key=0;
    ht->val=nmalloc(1); *ht->val=0;
    ht->owner=nmalloc(1); *ht->owner=0;
  }

  return;
}

/*
 * file format:
 * no special header
 * each line consists from basic structure:
 * key^value
 * which is compatible with t00th's assoc.tcl database
 * but it introduces meta-structures to allow extensibility
 * of the database
 * meta-structure is placed before key, and is escaped
 * by \1 (^A) - everything from there to another \1
 * or \2 is meta-structure. there can be multiple meta-structures,
 * the key itself is separated by \2
 * now theere are two meta-structures:
 * v%d which indicates whether this is last field of that name or not
 * a%d which is attribute bitmask of the pair
 * o%d%d which contains length && name of field's owner
 */

db_t *
db_load_(char *file) {
  FILE *f;
  db_t *d;
  int i, vol; unsigned long attr;
  char *l=nmalloc(1024), *n, *owner=NULL, *o;
  int l_len=1024, own_len=0;
  char *v;

  f=fopen(file,"r");
  if (!f) {
    dprintf(DP_LOG, "[db] Error! Cannot open %s for reading!\n", file);
    return NULL;
  }

  Context;

  d=nmalloc(sizeof(db_t));

  for (i=0; i<65536; i++) {
    d->hash[i].key=d->hash[i].val=d->hash[i].next=NULL;
  }

  i=0;

  Context;
  
  while(fgets(l, l_len, f)) {
    i++;
    while (strlen(l)==l_len-1 && l[l_len-2]!='\n') { /* drain the rest of line */
      n=nmalloc(l_len+1024);
      memcpy(n, l, l_len);
      nfree(l); l=n;
      l_len+=1024;
      if (!fgets(l, l_len, f)) {
	if (strlen(l)<l_len-1) strcat(l, "\n");
	break;
      }
    }
    v=l; vol=attr=0; owner=NULL;
    while (*v=='\1') {
      char *tok1, *tok2;
      v++;
      switch (*v) {
	case 'v': v++; vol=atoi(v);   while(*v>2) v++; break;
	case 'a': v++; attr=atoi(v);  while(*v>2) v++; break;
		  /* XXX evil hacking of some nasty things around owner's rec to read it correctly */
	case 'o': v++; own_len=atoi(v);
		  if (!(tok1=strchr(v, '\1')) && !(tok2=strchr(v, '\2'))) {
      		    dprintf(DP_LOG, "[db] Warning! No separator found in %s, line %d!\n", file, i);
		    goto mlend;
		  }
		  if (own_len>(tok1?tok1:tok2)-v) {
		    dprintf(DP_LOG, "[db] Warning! Incorrect length '%d' of owner string in %s, line %d!\n", own_len, file, i);
		    break;
		  }
		  /* XXX we assume we has to jump own_len+1 BACK from \1||\2 found, to jump over own_len's %d */
		  /* XXX may be confusing for l33t users editing db.dat manually */
		  v=(tok1?tok1:tok2)-own_len;
		  owner=nmalloc(own_len+1); strncpy(owner, v, own_len); owner[own_len]=0;
		  while(*v>2) v++; /* propably useles after this evil kludge of hack */ break;
	default : dprintf(DP_LOG, "[db] Warning! Unknown meta '%c' in %s, line %d!\n", *v, file, i); break;
      }
    }
    if (*v=='\2') v++;
    o=v;
    if (!(v=strchr(l, '^'))) {
      dprintf(DP_LOG, "[db] Warning! No separator found in %s, line %d!\n", file, i);
      continue;
    }
    *v=0; v++;
    v[strlen(v)-1]=0;
    if (strlen(v)>1024) v[1024]=0;
    db_assign(d, o, v, attr, 255, owner);
mlend:
  }

  fclose(f);

  return d;
}

void
db_save(db_t *db, char *file) {
  FILE *f;
  int i;
  hash_t *field;

  f=fopen(file,"w");
  if (!f) {
    dprintf(DP_LOG, "[db] Error! Cannot open %s for writing!\n", file);
    return;
  }

  Context;

  for (i=0; i<65536; i++) {
    field=&db->hash[i];
    while (field) {
      if (field->key && field->key[0]) {
	if (field->volume) fprintf(f,"\1v%d", field->volume);
	if (field->attr) fprintf(f,"\1a%ld", field->attr);
	if (field->owner) fprintf(f,"\1o%d%s", strlen(field->owner), field->owner);
	if (field->attr || field->volume || field->owner) fprintf(f,"\2");
	fprintf(f, "%s^%s\n", field->key, field->val);
      }
      field=field->next;
    }
  }

  fclose(f);

  return;
}

void
db_forget(db_t *db) {
  hash_t *field, *pf;
  int i;

  Context;

  for (i=0; i<65536; i++) {
    field=&db->hash[i];
    while (field) {
      nfree(field->key); nfree(field->val); nfree(field->owner);
      pf=field;
      field=field->next;
      if (pf!=&db->hash[i]) nfree(pf);
    }
  }

  nfree(db);

  return;
}

