/* This file was generated by scm2c from source file "ents.scm" */
/*  "ents.scm" WB-tree File Based Associative String Data Base System. */
/*  Copyright (C) 1991, 1992, 1993, 2000, 2003, 2006 Free Software Foundation, Inc. */
/*  */
/*  This program 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 3 of the */
/*  License, or (at your option) any later version. */
/*  */
/*  This program 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 this program.  If not, see */
/*  <http://www.gnu.org/licenses/>. */


#include "wbsys.h"

/*  tables */


LCK **lck_tab = 0;

ENTRY **buk_tab = 0;

ENTRY **ent_tab = 0;

long num_ents_ct = 0x0L;

int num_buks = 0;

int blk_size = 0;


int cache_ent_enable_P = !0;

/*  DATABASE LEVEL OPERATIONS */

/*  This can be bummed to write less than the full BSIZ if we know */
/*  what the disk sector size is. */
/*  fixed order check in ent-write */


int ents_ent_write(ent)
     ENTRY *ent;
{
  SEGD *seg = ent_seg(ent);
  unsigned char *blk = ent_blk(ent);
  if (!(blk_typ_P(blk, seq_typ)))
    check_key_order(blk);
  blk_set_time(blk, time(0L));
  if (!(seg_mutable_P(seg)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< %s on read only segment %ld?\n",
	 "ent_write",
	 seg_id(seg)));
      return 0;
    }
  else if (blkio_write(seg_port(seg), blk, seg_bsiz(seg), ent_id(ent)))
    {
      ent_set_dty(ent, 0);
      return !0;
    }
  else {
    ent_set_dty(ent, !0);
    return 0;
  }
}

/*  FLUSHING needs to be proportional to time (to put some limit on */
/*  how long things are left unwritten) plus write-activity. */

/*  NOTE: While flushing a buffer, get ACCPEND accmode to it (to */
/*  prevent surprise mods) */


int flush_ent_cntr = 0;

LCK flush_ent_lock = {0, -2, PTHREAD_MUTEX_INITIALIZER};
LCK *flush_ent_lck = &flush_ent_lock;

int ents_flush(trynum, flushnum)
     long trynum;
     long flushnum;
{
  long flushednum = 0x0L;
  if ((flush_ent_lck)
      && (try_lck_P(flush_ent_lck)))
    {
      long i_ct = trynum;
      while (!((!(i_ct))
	     || ((flushednum)>=(flushnum)))) {
	flush_ent_cntr = (1+(flush_ent_cntr))%(num_ents_ct);
	{
	  ENTRY *tent = ent_tab[flush_ent_cntr];
	  if ((ent_dty_P(tent))
	      && (!(ent_acc(tent)))
	      && (ent_flush_P(tent)))
	    {
	      show_buffer_1(tent);
	      flushednum = (flushednum)+0x1L;
	    }
	}
	{
	  i_ct = (i_ct)-0x1L;
	}
      }
      unlck(flush_ent_lck);
      return flushednum;
    }
  else return 0;
}


int ent_flush_P(tent)
     ENTRY *tent;
{
  SEGD *e_seg = ent_seg(tent);
  long e_id = ent_id(tent);
  get_buk_lck_P(seg_id(e_seg), e_id);
  {
    ENTRY *ent = get_buk(seg_id(e_seg), e_id);
L_loop:
    if (!(ent))
      {
	rel_buk(seg_id(e_seg), e_id);
	return 0;
      }
    else if ((tent)!=(ent))
      {
	ent = ent_next(ent);
	goto L_loop;
      }
    else if (!((ent_dty_P(ent))
	  && ((accnone)==(ent_acc(ent)))))
      {
	rel_buk(seg_id(e_seg), e_id);
	return 0;
      }
    else {
      ent_set_acc(ent, accpend);
      rel_buk(seg_id(e_seg), e_id);
      ents_ent_write(ent);
      get_buk_wait(seg_id(e_seg), e_id);
      ent_set_acc(ent, accnone);
      rel_buk(seg_id(e_seg), e_id);
      flush_ct = (flush_ct)+0x1L;
      return !0;
    }
  }
}

/*  release-ent! gives up all claim to ent, which is expected to be of */
/*  type accmode */
/*  fixed warning about dirty dirs -- twice */
/*  fixed dirty-block writer in UPDATE-ACCESS! */


void release_ent(ent, accmode)
     ENTRY *ent;
     int accmode;
{
  long blknum = ent_id(ent);
  SEGD *seg = ent_seg(ent);
  ENTRY *buk = 0;
  buk = get_buk_wait(seg_id(seg), blknum);
  if (((accnone)!=(accmode))
      && ((ent_acc(ent))!=(accmode)))
    dprintf((diagout,
	 "\
>>>>ERROR<<<< %s: unexpected accmode of %ld:%ld is %d not %d\n",
	 "release_ent",
	 seg_id(seg),
	 blknum,
	 ent_acc(ent),
	 accmode));
  if ((accnone)==(accmode))
    ;
  else if (!(ent_dty_P(ent)))
    ;
  else if ((blk_typ_P(ent_blk(ent), dir_typ))
      && (leaf_P(ent_blk(ent))))
    {
      dprintf((diagout,
	 "WARNING: Directory block %ld:%ld dirty at RELEASE-ENT! \n",
	 seg_id(seg),
	 blknum));
      dir_dty_ct = 0x1L+(dir_dty_ct);
    }
  else if (blk_typ_P(ent_blk(ent), seq_typ))
    {
      rel_buk(seg_id(seg), blknum);
      ents_ent_write(ent);
      buk = get_buk_wait(seg_id(seg), blknum);
    }
  if ((accnone)!=(accmode))
    ent_set_acc(ent, accnone);
  if ((ent_ref(ent))<=0)
    {
      ent_set_ref(ent, 0);
      dprintf((diagout,
	 ">>>>ERROR<<<< REF count below 0 in %ld:%ld\n",
	 seg_id(seg),
	 blknum));
    }
  else ent_set_ref(ent, (ent_ref(ent))-1);
  if (!(seg))
    {
      splice_out_ent(seg, blknum, buk, ent);
    }
  else ent_set_age(ent, (
     (ent_dty_P(ent)
       ?5
       :0))+(5*(6+((leaf)-(blk_level(ent_blk(ent)))))));
  rel_buk(seg_id(seg), blknum);
  return;
}


int ents_ent_update_access_P(ent, old_accmode, new_accmode)
     ENTRY *ent;
     int old_accmode;
     int new_accmode;
{
  get_buk_wait(seg_id(ent_seg(ent)), ent_id(ent));
  if ((ent_acc(ent))!=(old_accmode))
    {
      rel_buk(seg_id(ent_seg(ent)), ent_id(ent));
      dprintf((diagout,
	 "\
>>>>ERROR<<<< unexpected accmode type on %ld:%ld %d (expected %d)\n",
	 seg_id(ent_seg(ent)),
	 ent_id(ent),
	 ent_acc(ent),
	 old_accmode));
    }
  if ((accnone)==(old_accmode))
    ;
  else if (!(ent_dty_P(ent)))
    ;
  else if (blk_typ_P(ent_blk(ent), seq_typ))
    {
      rel_buk(seg_id(ent_seg(ent)), ent_id(ent));
      ents_ent_write(ent);
      get_buk_wait(seg_id(ent_seg(ent)), ent_id(ent));
    }
  else if ((blk_typ_P(ent_blk(ent), dir_typ))
      && (leaf_P(ent_blk(ent))))
    {
      dprintf((diagout,
	 "WARNING: Directory block %ld:%ld dirty at ENT-UPD-ACCESS! \n",
	 seg_id(ent_seg(ent)),
	 ent_id(ent)));
      dir_dty_ct = 0x1L+(dir_dty_ct);
    }
  ent_set_acc(ent, new_accmode);
  rel_buk(seg_id(ent_seg(ent)), ent_id(ent));
  return (ent)
  && !0;
}

/*  ENT-FREE-LIST stuff ----------------------------------------------------------- */


int free_buk_cntr = 0;

ENTRY *free_ents = 0;

LCK free_ent_lock = {0, -1, PTHREAD_MUTEX_INITIALIZER};
LCK *free_ent_lck = &free_ent_lock;

ENTRY *get_free_free_ent()
{
  lck(free_ent_lck);
  if (free_ents)
    {
      ENTRY *free_ent = free_ents;
      free_ents = ent_next(free_ents);
      unlck(free_ent_lck);
      return free_ent;
    }
  else return 0;
}

/*  this version assumes the caller has already locked the bucket */
/*  BUK containing ENT */


void splice_out_ent(seg, blk_num, buk, ent)
     SEGD *seg;
     long blk_num;
     ENTRY *buk;
     ENTRY *ent;
{
  {
    ENTRY *bent = buk;
    ENTRY *lastent = 0;
    while (!((!(bent))
	   || ((bent)==(ent)))) {
      {
	ENTRY *T_bent = ent_next(bent);
	lastent = bent;
	bent = T_bent;
      }
    }
    if (bent)
      {
	if (lastent)
	  {
	    ent_set_next(lastent, ent_next(bent));
	  }
	else set_buk(seg_id(seg), blk_num, ent_next(bent));
	recycle_ent(bent);
	return;
      }
    else dprintf((diagout, "WARNING: %s couldn't %ld:%ld\n", "splice_out_ent", seg_id(seg), blk_num));
    return;
  }
}


void recycle_ent(ent)
     ENTRY *ent;
{
  ent_set_dty(ent, 0);
  ent_set_pus(ent, 0);
  ent_set_seg(ent, 0);
  ent_set_id(ent, 0);
  lck(free_ent_lck);
  ent_set_ref(ent, 0);
  ent_set_acc(ent, accnone);
  ent_set_next(ent, free_ents);
  free_ents = ent;
  unlck(free_ent_lck);
  return;
}

/*  SELECT-IDLE-ENT selects a candidate entry for reuse.  caller needs to call */
/*  RECLAIM-ENT next to splice entry out of its bucket. */
/*  NOTE: when called, bucket (lseg lblk-num) is lcked. */
/*  The target bucket is assumed unlocked if lseg < 0. */
/*  (GET-ENT calls this with the bucket locked to prevent someone else from */
/*  getting another entry for the same block.) */


ENTRY *select_idle_ent(lseg, lblk_num)
     SEGD *lseg;
     long lblk_num;
{
  {
    ENTRY *oldest_ent = 0;
    int num_scan = max(min(num_buks, 0xa), (num_buks)/0x14);
    int free_base = free_buk_cntr;
    free_buk_cntr = ((num_scan)+(free_buk_cntr))%(num_buks);
    unlck(free_ent_lck);
    {
      int i = 0;
      while (!((((i)>(num_scan))
	      && (oldest_ent))
	     || ((i)>(num_buks)))) {
	{
	  long free_num = ((free_base)+(i))%(num_buks);
	  int dont_lock_P = !(lseg)
	    ?0
	    :(free_num)==(hash2int(seg_id(lseg), lblk_num));
	  if ((dont_lock_P)
	      || (get_buk_lck_P(0, free_num)))
	    {
	      ENTRY *ent = get_buk(0, free_num);
	      while (ent) {
		if (!(ent_ref(ent)))
		  {
		    ent_set_age(ent, (
		       (ent_dty_P(ent)
			 ?1
			 :2))+(ent_age(ent)));
		    if (((accnone)==(ent_acc(ent)))
		        && ((!(oldest_ent))
			    || ((ent_age(ent))>(ent_age(oldest_ent)))))
		      oldest_ent = ent;
		  }
		{
		  ent = ent_next(ent);
		}
	      }
	      if (dont_lock_P)
		;
	      else rel_buk(0, free_num);
	    }
	}
	{
	  i = (i)+1;
	}
      }
      if ((i)>(num_buks))
	dprintf((diagout, ">>>>ERROR<<<< No free ents\n"));
      return oldest_ent;
    }
  }
}

/*  RECLAIM-ENT unlinks ENT from its bucket if its not in use. */
/*  It writes out the entry-s block if it's dirty */
/*  RECLAIM-ENT has 3 cases */
/*   (a) ENT is in use -- LSEG is unlocked, NIL is returned */
/*   (b) ENT is clean -- ENT is unlinked and returned */
/*   (c) ENT is DIRTY -- ENT is written, unlinked, and reclaimed (put on */
/*             free lsit); LSEG is UNLOCKED, NIL is returned. */
/*     possible optimization in case (c): if LSEG = -, */
/*     ENT could be written, unlinked, and returned (like (b)) */


ENTRY *reclaim_ent(ent, lseg, lblk_num)
     ENTRY *ent;
     SEGD *lseg;
     long lblk_num;
{
  {
    SEGD *seg = ent_seg(ent);
    long blk_num = ent_id(ent);
    int segs_equal_P = (lseg)
    && (same_buk_P(seg_id(lseg), lblk_num, seg_id(seg), blk_num));
    ENTRY *buk = segs_equal_P
      ?get_buk(seg_id(seg), blk_num)
      :get_buk_wait(seg_id(seg), blk_num);
    if ((ent_ref(ent))
        || ((accnone)!=(ent_acc(ent))))
      {
	rel_buk(seg_id(seg), blk_num);
	if (segs_equal_P)
	  ;
	else if (!(lseg))
	  ;
	else rel_buk(seg_id(lseg), lblk_num);
	dprintf((diagout,
	 "WARNING: %s: couldn't splice-out-ent %ld:%ld\n",
	 "reclaim_ent",
	 seg_id(lseg),
	 lblk_num));
	return 0;
      }
    else {
      ENTRY *bent = buk;
      ENTRY *lastent = 0;
      while (!((!(bent))
	     || ((ent)==(bent)))) {
	{
	  ENTRY *T_bent = ent_next(bent);
	  lastent = bent;
	  bent = T_bent;
	}
      }
      if (!(bent))
	{
	  rel_buk(seg_id(seg), blk_num);
	  if (segs_equal_P)
	    ;
	  else if (!(lseg))
	    ;
	  else rel_buk(seg_id(lseg), lblk_num);
	  dprintf((diagout,
	 "\
>>>>ERROR<<<< %s: couldn't find ent in bucket %ld:%ld l=%ld:%ld\n",
	 "reclaim_ent",
	 seg_id(seg),
	 blk_num,
	 seg_id(lseg),
	 lblk_num));
	  return 0;
	}
      else if (!(ent_dty_P(ent)))
	{
	  if (lastent)
	    {
	      ent_set_next(lastent, ent_next(ent));
	    }
	  else set_buk(seg_id(seg), blk_num, ent_next(ent));
	  ent_set_next(ent, 0);
	  if (segs_equal_P)
	    ;
	  else rel_buk(seg_id(seg), blk_num);
	  return ent;
	}
      else {
	ent_set_acc(ent, accpend);
	rel_buk(seg_id(seg), blk_num);
	if (segs_equal_P)
	  ;
	else if (!(lseg))
	  ;
	else rel_buk(seg_id(lseg), lblk_num);
	ents_ent_write(ent);
	buk = get_buk_wait(seg_id(seg), blk_num);
	ent_set_acc(ent, accnone);
	splice_out_ent(seg, blk_num, buk, ent);
	rel_buk(seg_id(seg), blk_num);
	return 0;
      }
    }
  }
}

/*  TRY-GET-FREE-ENT either returns a free ent OR unlocks (lseg lblk-num) */


ENTRY *try_get_free_ent(lseg, lblk_num)
     SEGD *lseg;
     long lblk_num;
{
  ENTRY *ent = get_free_free_ent();
  if (!(ent))
    {
      ent = select_idle_ent(lseg, lblk_num);
      if (ent)
	{
	  ent = reclaim_ent(ent, lseg, lblk_num);
	}
      else if (!(lseg))
	;
      else rel_buk(seg_id(lseg), lblk_num);
    }
  return ent;
}


/*  Special entry points for Jonathan to do non-B-tree stuff. */
/*  Also now used in chain-scan. */


ENTRY *allocate_ent()
{
  {
    ENTRY *ent = try_get_free_ent(0,  -1);
    int cnt = 0x13;
L_loop:
    if (ent)
      {
	ent_set_acc(ent, accwrite);
	ent_set_dty(ent, !0);
	ent_set_pus(ent, 0);
	ent_set_seg(ent, 0);
	ent_set_id(ent, 0);
	ent_set_ref(ent, 1);
	ent_set_next(ent, 0);
	return ent;
      }
    else if (!(cnt))
      return 0;
    else {
      ent = try_get_free_ent(0,  -1);
      cnt =  -1+(cnt);
      goto L_loop;
    }
  }
}


void ent_copy(to_ent, from_ent)
     ENTRY *to_ent;
     ENTRY *from_ent;
{
  if ((ent_acc(to_ent))!=(accwrite))
    dprintf((diagout,
	 ">>>>ERROR<<<< %s: copying into non-ACCWRITE %ld:%ld\n",
	 "ent_copy",
	 seg_id(ent_seg(to_ent)),
	 ent_id(to_ent)));
  ent_set_seg(to_ent, ent_seg(from_ent));
  ent_set_id(to_ent, ent_id(from_ent));
  subbytes_move(ent_blk(from_ent), 0, seg_bsiz(ent_seg(from_ent)), ent_blk(to_ent), 0);
  return;
}


int get_ent_copy(to_ent, seg, blk_num)
     ENTRY *to_ent;
     SEGD *seg;
     long blk_num;
{
  ENTRY *from_ent = get_ent(seg, blk_num, accread);
  if (from_ent)
    {
      ent_copy(to_ent, from_ent);
      release_ent(from_ent, accread);
      return !0;
    }
  else return 0;
}


void flush_flc(seg, fullness)
     SEGD *seg;
     int fullness;
{
L_flush_flc:
  {
    unsigned char fstr[4];
    unsigned char tstr[4];
    lck(seg_lck(seg));
    if ((seg_flc_len(seg))<=(fullness))
      {
	unlck(seg_lck(seg));
	return;
      }
    else if (!0)
      {
	long2str(fstr, 0, seg_flc(seg)[(seg_flc_len(seg))-1]);
	seg_set_flc_len(seg, (seg_flc_len(seg))-1);
	unlck(seg_lck(seg));
	long2str(tstr, 0, time(0L));
	bt_put(seg_fl_han(seg), fstr, 4, tstr, 4);
	goto L_flush_flc;
      }
  }
}

/*  Assumes that SEG-LCK is locked by this process */


int initload_flc_P(seg)
     SEGD *seg;
{
  switch (seg_flc_len(seg)) {
  case  -1:
    {
      unsigned char tmp_str[0x14];
      int flc_image_len = bt_get(seg_rt_han(seg), flc_byts, 3, tmp_str);
      if (0 > (flc_image_len))
	flc_image_len = 0;
      bt_put(seg_rt_han(seg), flc_byts, 3, no_byts, 0);
      seg_set_flc_len(seg, (flc_image_len)/4);
      {
	int i =  -4+(flc_image_len);
	while (!(0 > (i))) {
	  seg_flc(seg)[(i)/4] = str2long(tmp_str, i);
	  {
	    i =  -4+(i);
	  }
	}
      }
    }
    return !0;
  case  -2:
    dprintf((diagout,
	 ">>>>ERROR<<<< %s on read only segment %ld?\n",
	 "initload_flc",
	 seg_id(seg)));
    return 0;
  default:
    return !0;
  }
}

/*  Called with SEG-LCK locked. */
/*  If you don't know what you are doing. DON'T DO IT! */
/*  Compute inverse hash function so that ent can still be found. */

void amnesia_ent(ent)
     ENTRY *ent;
{
  int segid = seg_id(ent_seg(ent));
  {
    int hash = hash2int(segid, ent_id(ent));
    int newid = hash2int(1+(segid), ent_id(ent));
    ent_set_id(ent, newid);
    ent_set_seg(ent, 0);
    if ((hash)!=(hash2int( -1, newid)))
      dprintf((diagout, "WARNING: %s: hash mismatch %d >< %ld\n", "amnesia_ent", hash, hash2int( -1, newid)));
    ent_set_dty(ent, 0);
    ent_set_pus(ent, 0);
    if (ent_blk(ent))
      if (blk_typ_P(ent_blk(ent), dir_typ))
	blk_set_typ(ent_blk(ent), ind_typ);
    ent_set_age(ent, 0x80);
    return;
  }
}


int blk_free(ent)
     ENTRY *ent;
{
L_blk_free:
  {
    SEGD *seg = ent_seg(ent);
    if ((ent_acc(ent))!=(accwrite))
      {
	dprintf((diagout,
	 ">>>>ERROR<<<< %s: %ld:%ld without ACCWRITE\n",
	 "blk_free",
	 seg_id(seg),
	 ent_id(ent)));
	return 0;
      }
    else {
      lck(seg_lck(seg));
      if (!(initload_flc_P(seg)))
	{
	  unlck(seg_lck(seg));
	  return 0;
	}
      else if ((seg_flc_len(seg))>=((flc_len)-1))
	{
	  unlck(seg_lck(seg));
	  flush_flc(seg, (flc_len)-2);
	  goto L_blk_free;
	}
      else {
	seg_flc(seg)[seg_flc_len(seg)] = ent_id(ent);
	seg_set_flc_len(seg, (seg_flc_len(seg))+1);
	amnesia_ent(ent);
	unlck(seg_lck(seg));
	return !0;
      }
    }
  }
}


int flc_fill(seg)
     SEGD *seg;
{
  unsigned char fstr[4];
  int flen = 0;
  int status;
  lck(seg_lck(seg));
  if ((seg_flc_len(seg))>=1)
    {
      unlck(seg_lck(seg));
      return success;
    }
  else if (!(try_lck_P(seg_fck(seg))))
    {
      unlck(seg_lck(seg));
      dprintf((diagout,
	 "\
WARNING: %s failed -- branch never tried before! Segment %ld %s\n",
	 "flc_fill",
	 seg_id(seg),
	 seg_str(seg)));
      return retryerr;
    }
  else if ((flen = bt_next(seg_fl_han(seg), no_byts, 0, fstr),err_P(flen)))
    {
      long xnum = (seg_used(seg))+((flc_len)/0x2L);
      if (blkio_file_extend(seg_port(seg), seg_bsiz(seg), xnum))
	{
	  {
	    int i = 0;
	    while (!((i)>((flc_len)/2))) {
	      seg_flc(seg)[seg_flc_len(seg)] = (xnum)-(i);
	      seg_set_flc_len(seg, (seg_flc_len(seg))+1);
	      seg_set_used(seg, (seg_used(seg))+1);
	      {
		i = (i)+1;
	      }
	    }
	  }
	  {
	    unsigned char used_str[4];
	    long2str(used_str, 0, seg_used(seg));
	    bt_put(seg_rt_han(seg), used_byts, 4, used_str, 4);
	  }
	  {
	    status = success;
	    goto L_unlck_and_return;
	  }
	}
      else {
	dprintf((diagout,
	 ">>>>ERROR<<<< No more file space available! Segment %ld %s\n",
	 seg_id(seg),
	 seg_str(seg)));
	{
	  status = noroom;
	  goto L_unlck_and_return;
	}
      }
    }
  else {
    unlck(seg_lck(seg));
    {
      long long_ara[(flc_len)+1];
      unsigned char xstr[0x100];
      int respkt[pkt_size];
      int result = success;
      subbytes_move(fstr, 0, flen, xstr, 0);
      long_ara[0] = 0;
      pkt_set_skey_count(respkt, 0);
      result = bt_scan(seg_fl_han(seg), rem_scan, xstr, flen, no_byts, end_of_chain, flc_proc, long_ara, respkt, 1);
      if (((result)==(success))
	  || ((result)==(notpres))
	  || ((result)==(terminated)))
	{
	  lck(seg_lck(seg));
	  {
	    int i = long_ara[0];
	    while (!((i)<=0)) {
	      seg_flc(seg)[seg_flc_len(seg)] = long_ara[i];
	      seg_set_flc_len(seg, (seg_flc_len(seg))+1);
	      {
		i = (i)-1;
	      }
	    }
	  }
	  {
	    status = success;
	    goto L_unlck_and_return;
	  }
	}
      else {
	unlck(seg_fck(seg));
	return result;
      }
    }
  }
L_unlck_and_return:
  unlck(seg_lck(seg));
  unlck(seg_fck(seg));
  return status;
}


int flc_proc(keystr, klen, vstr, vlen, long_ara)
     unsigned char *keystr;
     int klen;
     unsigned char *vstr;
     int vlen;
     long *long_ara;
{
  {
    int ct = long_ara[0];
    if ((ct)<((flc_len)/2))
      {
	long num = str2long(keystr, 0);
	ct = (ct)+1;
	long_ara[0] = ct;
	long_ara[ct] = num;
	return success;
      }
    else return terminated;
  }
}

/*  create-new-blk-ent leaves you with ACCWRITE accmode to blk */

ENTRY *create_new_blk_ent(seg)
     SEGD *seg;
{
L_create_new_blk_ent:
  lck(seg_lck(seg));
  if (!(initload_flc_P(seg)))
    {
      unlck(seg_lck(seg));
      return 0;
    }
  else if ((seg_flc_len(seg))<=0)
    {
      unlck(seg_lck(seg));
      if (!(realerr_P(flc_fill(seg))))
	goto L_create_new_blk_ent;
      else return 0;
    }
  else {
    seg_set_flc_len(seg, (seg_flc_len(seg))-1);
    {
      long bnum = seg_flc(seg)[seg_flc_len(seg)];
      unlck(seg_lck(seg));
      {
	ENTRY *ent = get_ent(seg, bnum, accwrite);
	if (ent)
	  ent_set_dty(ent, !0);
	return ent;
      }
    }
  }
}

/*  End of stuff to deal with the free-list-cache (FLC) */

/*  try-get-ent returns an entry with accmode or #f if blk is lcked. */
/*  When you are done with the entry you need to release-ent!. */

ENTRY *try_get_ent(seg, blk_num, accmode)
     SEGD *seg;
     long blk_num;
     int accmode;
{
  {
    ENTRY *buk = get_buk_wait(seg_id(seg), blk_num);
    {
      ENTRY *ent = buk;
L_entloop:
      if (!(ent))
	{
	  rel_buk(seg_id(seg), blk_num);
	  tge_fct = 0x1L+(tge_fct);
	  return 0;
	}
      else if (!(((seg)==(ent_seg(ent)))
	    && ((blk_num)==(ent_id(ent)))))
	{
	  ent = ent_next(ent);
	  goto L_entloop;
	}
      else if ((blk_id(ent_blk(ent)))!=(blk_num))
	{
	  rel_buk(seg_id(seg), blk_num);
	  dprintf((diagout,
	 ">>>>ERROR<<<< corrutped buffer %ld:%ld <> %ld\n",
	 seg_id(ent_seg(ent)),
	 blk_id(ent_blk(ent)),
	 blk_num));
	  tge_fct = 0x1L+(tge_fct);
	  return 0;
	}
      else if ((accnone)==(accmode))
	{
	  ent_set_ref(ent, 1+(ent_ref(ent)));
	  rel_buk(seg_id(seg), blk_num);
	  tge_ct = 0x1L+(tge_ct);
	  return ent;
	}
      else if ((accnone)==(ent_acc(ent)))
	{
	  ent_set_acc(ent, accmode);
	  ent_set_ref(ent, 1+(ent_ref(ent)));
	  rel_buk(seg_id(seg), blk_num);
	  tge_ct = 0x1L+(tge_ct);
	  return ent;
	}
      else {
	rel_buk(seg_id(seg), blk_num);
	tge_fct = 0x1L+(tge_fct);
	return 0;
      }
    }
  }
}


ENTRY *chain_find_ent(han, accmode, key_str, k_len, pkt)
     HAND *han;
     int accmode;
     unsigned char *key_str;
     int k_len;
     int *pkt;
{
  ENTRY *ent = (cache_ent_enable_P)
      && (han_last(han))
    ?try_get_ent(han_seg(han), han_last(han), accmode)
    :0;
  if ((ent)
      && (leaf_P(ent_blk(ent)))
      && ((blk_top_id(ent_blk(ent)))==(han_id(han)))
      && (blk_find_pos(ent_blk(ent), key_str, k_len, pkt))
      && (((pkt_match_type(pkt))==(match))
	  || ((((pkt_match_type(pkt))==(pastp))
	       || ((pkt_match_type(pkt))==(qpastp)))
	      && ((pkt_match_pos(pkt))>(blk_data_start)))))
    {
      tce_ct = (tce_ct)+0x1L;
      return ent;
    }
  else {
    if (ent)
      release_ent(ent, accmode);
    tce_fct = (tce_fct)+0x1L;
    ent = get_ent(han_seg(han), han_id(han), accnone);
    if ((!(root_P(ent_blk(ent))))
        || (blk_typ_P(ent_blk(ent), seq_typ)))
      {
	dprintf((diagout,
	 ">>>>ERROR<<<< %s: not a B-tree root %ld:%ld\n",
	 "bt_open",
	 seg_id(ent_seg(ent)),
	 ent_id(ent)));
	release_ent(ent, accnone);
	ent = 0;
      }
    else ent = find_ent(ent, leaf,  -1, key_str, k_len);
    if (!(ent))
      ;
    else if ((accread)==(accmode))
      ;
    else if (ents_ent_update_access_P(ent, accread, accmode))
      ;
    else {
      release_ent(ent, accread);
      ent = 0;
    }
    if (ent)
      ent = chain_find(ent, accmode, key_str, k_len, pkt);
    if (ent)
      han_set_last(han, ent_id(ent));
    return ent;
  }
}

/*  I havent put the call to PREV:PREV-K-ENT inside here, */
/*  as both paths need to call it - rjz */


ENTRY *chain_find_prev_ent(han, accmode, key_str, k_len, pkt)
     HAND *han;
     int accmode;
     unsigned char *key_str;
     int k_len;
     int *pkt;
{
  ENTRY *ent = (cache_ent_enable_P)
      && (han_last(han))
    ?try_get_ent(han_seg(han), han_last(han), accmode)
    :0;
  if ((ent)
      && (leaf_P(ent_blk(ent)))
      && ((blk_top_id(ent_blk(ent)))==(han_id(han)))
      && (blk_find_pos(ent_blk(ent), key_str, k_len, pkt))
      && (((pkt_match_type(pkt))==(match))
	  || ((pkt_match_type(pkt))==(matchend))
	  || ((((pkt_match_type(pkt))==(pastp))
	       || ((pkt_match_type(pkt))==(qpastp)))
	      && ((pkt_match_pos(pkt))>(blk_data_start)))))
    {
      tce_ct = (tce_ct)+0x1L;
      return ent;
    }
  else {
    if (ent)
      release_ent(ent, accmode);
    tce_fct = (tce_fct)+0x1L;
    ent = prev_find_prev_ent(get_ent(han_seg(han), han_id(han), accnone), leaf,  -1, key_str, k_len);
    if (!(ent))
      ;
    else if ((accmode)==(accread))
      ;
    else if (ents_ent_update_access_P(ent, accread, accmode))
      ;
    else {
      release_ent(ent, accread);
      ent = 0;
    }
    return ent;
  }
}

/* (REL-BUK! (SEG:ID seg) blk-num) */
/* (edprintf "all ents in use!\n") */


ENTRY *get_ent(seg, blk_num, accmode)
     SEGD *seg;
     long blk_num;
     int accmode;
{
  if (0 > (blk_num))
    {
      dprintf((diagout, ">>>>ERROR<<<< negative block number %ld\n", blk_num));
      return 0;
    }
  else if ((blk_num)>=(seg_used(seg)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< bad block number %ld:%ld (>= %ld)\n",
	 seg_id(seg),
	 blk_num,
	 seg_used(seg)));
      return 0;
    }
  else {
    ENTRY *ent = get_buk_wait(seg_id(seg), blk_num);
L_entloop:
    if (!(ent))
      {
	ent = try_get_free_ent(seg, blk_num);
	if (ent)
	  {
	    ent_set_next(ent, get_buk(seg_id(seg), blk_num));
	    set_buk(seg_id(seg), blk_num, ent);
	    ent_set_acc(ent, accpend);
	    ent_set_seg(ent, seg);
	    ent_set_id(ent, blk_num);
	    ent_set_age(ent,  -127);
	    ent_set_dty(ent, 0);
	    ent_set_pus(ent, 0);
	    ent_set_ref(ent, 1);
	    rel_buk(seg_id(seg), blk_num);
	    if ((accmode)==(accwrite))
	      {
		ent_set_acc(ent, accwrite);
		ent_set_dty(ent, !0);
		init_leaf_blk(ent_blk(ent), blk_num, ind_typ);
		ge_ct = 0x1L+(ge_ct);
		return ent;
	      }
	    else if (blkio_read(seg_port(seg), ent_blk(ent), seg_bsiz(seg), blk_num))
	      {
		ent_set_acc(ent, accmode);
		if ((blk_id(ent_blk(ent)))!=(blk_num))
		  dprintf((diagout,
	 ">>>>ERROR<<<< corrupted blk %ld:%ld <> %ld\n",
	 seg_id(ent_seg(ent)),
	 blk_num,
	 blk_id(ent_blk(ent))));
		ge_ct = 0x1L+(ge_ct);
		return ent;
	      }
	    else {
	      ent_set_ref(ent, 0);
	      ent_set_acc(ent, accnone);
	      ge_fct = 0x1L+(ge_fct);
	      return 0;
	    }
	  }
	else {
	  ent = get_buk_wait(seg_id(seg), blk_num);
	  goto L_entloop;
	}
      }
    else if (!(((seg)==(ent_seg(ent)))
	  && ((blk_num)==(ent_id(ent)))))
      {
	ent = ent_next(ent);
	goto L_entloop;
      }
    else if ((blk_id(ent_blk(ent)))!=(blk_num))
      {
	rel_buk(seg_id(seg), blk_num);
	dprintf((diagout,
	 ">>>>ERROR<<<< corrupted buffer %ld:%ld <> %ld\n",
	 seg_id(ent_seg(ent)),
	 blk_id(ent_blk(ent)),
	 blk_num));
	ge_fct = 0x1L+(ge_fct);
	return 0;
      }
    else if ((accnone)==(accmode))
      {
	ent_set_ref(ent, 1+(ent_ref(ent)));
	rel_buk(seg_id(seg), blk_num);
	ge_ct = 0x1L+(ge_ct);
	return ent;
      }
    else if ((accnone)==(ent_acc(ent)))
      {
	ent_set_acc(ent, accmode);
	ent_set_ref(ent, 1+(ent_ref(ent)));
	rel_buk(seg_id(seg), blk_num);
	ge_ct = 0x1L+(ge_ct);
	return ent;
      }
    else {
      rel_buk(seg_id(seg), blk_num);
      ge_fct = 0x1L+(ge_fct);
      return 0;
    }
  }
}


ENTRY *switch_ent(old_ent, oldacc, new_num, newacc)
     ENTRY *old_ent;
     int oldacc;
     long new_num;
     int newacc;
{
  {
    ENTRY *new_ent = get_ent(ent_seg(old_ent), new_num, accnone);
    release_ent(old_ent, oldacc);
    if (new_ent)
      ents_ent_update_access_P(new_ent, accnone, newacc);
    return new_ent;
  }
}

/* minimum real NUM-ENTS-CT is 12*number of processes */
/* minimum NUM-BUKS IS 2, MAYBE 3 (due to how get-free-ent works) */
/* minimum real BLK-SIZE is 1.5K */


int init_wb(max_num_ents_cnt, max_num_buks, max_blk_size)
     int max_num_ents_cnt;
     int max_num_buks;
     int max_blk_size;
{
  if (!(0xc<=(max_num_ents_cnt)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< %s: %s (%d) must be %d or greater.\n",
	 "init_wb",
	 "max_num_ents_cnt",
	 max_num_ents_cnt,
	 0xc));
      return argerr;
    }
  else if (!(0xc<=(max_num_buks)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< %s: %s (%d) must be %d or greater.\n",
	 "init_wb",
	 "max_num_buks",
	 max_num_buks,
	 0xc));
      return argerr;
    }
  else if (!(0x80<=(max_blk_size)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< %s: %s (%d) must be %d or greater.\n",
	 "init_wb",
	 "max_blk_size",
	 max_blk_size,
	 0x80));
      return argerr;
    }
  else if ((lck(seg_chain_lck),try_lck_P(seg_chain_lck)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< %s: lck (mutex) is not self-blocking!\n",
	 "init_wb"));
      return strangerr;
    }
  else if (!(lck_tab))
    {
      blkio_init();
      clear_stats();
      seg_cntr = 4;
      num_buks = max_num_buks;
      blk_size = max_blk_size;
      free_buk_cntr = 0;
      flush_ent_cntr = 0;
      buk_tab = calloc(num_buks, (sizeof (void *)));
      lck_tab = calloc(num_buks, (sizeof (void *)));
      ent_tab = calloc(ent_tab_inc, (sizeof (void *)));
      {
	int i = num_buks;
	while (i) {
	  lck_tab[(i)-1] = make_lck((i)-1);
	  {
	    i = (i)-1;
	  }
	}
      }
      lck(free_ent_lck);
      {
	int i = max_num_ents_cnt;
	ENTRY *bent = 0;
	while (i) {
	  {
	    ENTRY *newent = ent_make_ent(num_ents_ct);
	    if (newent)
	      {
		ent_set_next(newent, bent);
		bent = newent;
		ent_tab[num_ents_ct] = newent;
		ent_set_tag(newent, num_ents_ct);
		num_ents_ct = 0x1L+(num_ents_ct);
		if (!((num_ents_ct)%(ent_tab_inc)))
		  {
		    ENTRY **tmp_ent_tab = realloc(ent_tab, ((ent_tab_inc)+(num_ents_ct)) * (sizeof (void *)));
		    if (tmp_ent_tab)
		      {
			ent_tab = tmp_ent_tab;
		      }
		    else i = 1;
		  }
	      }
	    else i = 1;
	  }
	  {
	    i = (i)-1;
	  }
	}
	free_ents = bent;
      }
      unlck(free_ent_lck);
      unlck(seg_chain_lck);
      return num_ents_ct;
    }
  else if ((max_blk_size)>(blk_size))
    {
      dprintf((diagout,
	 "\
>>>>ERROR<<<< %s: already initialized with smaller blk-size: %d>%d\n",
	 "init_wb",
	 max_blk_size,
	 blk_size));
      unlck(seg_chain_lck);
      return argerr;
    }
  else {
    dprintf((diagout, "%s: already initialized\n", "init_wb"));
    unlck(seg_chain_lck);
    return notpres;
  }
}


int final_wb()
{
  if (lck_tab)
    {
      {
	SEGD *seg = seg_chain;
	while (seg) {
	  lck(seg_chain_lck);
	  close_seg(seg, !0);
	  unlck(seg_chain_lck);
	  {
	    seg = seg_chain;
	  }
	}
      }
      {
	int i = num_ents_ct;
	while (i) {
	  free(ent_tab[ -1+(i)]);
	  ent_tab[ -1+(i)] = 0;
	  num_ents_ct =  -1+(num_ents_ct);
	  {
	    i = (i)-1;
	  }
	}
      }
      {
	int i = num_buks;
	while (i) {
	  free(lck_tab[(i)-1]);
	  lck_tab[(i)-1] = 0;
	  {
	    i = (i)-1;
	  }
	}
      }
      free(ent_tab);
      ent_tab = 0;
      free(lck_tab);
      lck_tab = 0;
      free(buk_tab);
      buk_tab = 0;
      blkio_final();
      return success;
    }
  else {
    dprintf((diagout, "%s: already finaled\n", "final_wb"));
    blkio_final();
    return success;
  }
}


void check_blk(blk)
     unsigned char *blk;
{
  {
    int b_end = blk_end(blk);
    {
      int b_pos = blk_data_start;
L_lp:
      {
	int s_pos = next_field(blk, 1+(b_pos));
	if ((s_pos)==(b_end))
	  
	  return;
	else if ((s_pos)<(b_end))
	  {
	    {
	      b_pos = next_cnvpair(blk, b_pos);
	      goto L_lp;
	    }
	  }
	else {
	  dprintf((diagout, ">>>>ERROR<<<< %s: blk %ld past end %d\n", "check_blk", blk_id(blk), s_pos));
	  
	  return;
	}
      }
    }
  }
}


void check_key_order(blk)
     unsigned char *blk;
{
  unsigned char split_str[0x100];
  int spos = split_key_pos(blk);
  if (0 < (spos))
    recon_this_key(blk, spos, split_str, 0, 0x100);
    return;
}


int do_seg_buffers(seg, func)
     SEGD *seg;
     int_function func;
{
  {
    int i = num_buks;
    ENTRY *ent = 0;
L_lp:
    if (!(ent))
      if (!(i))
	return success;
      else {
	int T_i = (i)-1;
	ent = buk_tab[(i)-1];
	i = T_i;
	goto L_lp;
      }
    else if ((!(seg))
        || ((seg)==(ent_seg(ent))))
      {
	int ans = func(ent);
	if (success_P(ans))
	  {
	    ent = ent_next(ent);
	    goto L_lp;
	  }
	else return ans;
      }
    else {
      ent = ent_next(ent);
      goto L_lp;
    }
  }
}


int check_buffer(ent)
     ENTRY *ent;
{
  if (ent_ref(ent))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< Entry still referenced: %ld:%ld\n",
	 seg_id(ent_seg(ent)),
	 ent_id(ent)));
      ent_set_ref(ent, 0);
    }
  if ((accnone)!=(ent_acc(ent)))
    {
      dprintf((diagout,
	 ">>>>ERROR<<<< Entry still lcked: %ld:%ld\n",
	 seg_id(ent_seg(ent)),
	 ent_id(ent)));
      ents_ent_update_access_P(ent, ent_acc(ent), accnone);
    }
  return success;
}


void check_access()
{
  check_lcks();
  do_seg_buffers(0, check_buffer);
  return;
}


/*  This routine needs to deal with lck issues. */
/*  TBD needs to give error if lcked. */

int flush_buffer(ent)
     ENTRY *ent;
{
  if ((accnone)!=(ent_acc(ent)))
    return terminated;
  else if (ent_dty_P(ent))
    if (ents_ent_write(ent))
      return success;
    else return retryerr;
  else return success;
}


int purge_buffer(ent)
     ENTRY *ent;
{
  if (ent_dty_P(ent))
    {
      if (((ent_acc(ent))==(accwrite))
	  || ((ent_acc(ent))==(accpend)))
	dprintf((diagout, "  Purging %s entry: %ld:%ld\n", 
		 ((ent_acc(ent))==(accwrite)
		   ?"ACCWRITE"
		   :"ACCPEND"), seg_id(ent_seg(ent)), ent_id(ent)));
      ents_ent_write(ent);
    }
  amnesia_ent(ent);
  return success;
}
