/*****
 * ObjDB.c
 *
 *	Methods for a DataBase Object.
 {
 *****/
#import "ObjDB.h"
#import <objc/Storage.h>
#import <streams/streams.h>
#define	MAXLINE 80
#define TRUE -1
#define FALSE !TRUE
#define ISNEW	FALSE
#define EXISTS TRUE
#define BIGNUM 0
#define LASTISN 999999

@implementation ObjDB

- init: (char *)dbname data_use_fraction: (float)dFraction
{
	char	tmp_buffer[BUFFSIZE], name[BUFFSIZE], *space_locator;
	int i, *isns;
	[self setDescriptors];
	myDFD = [[Storage alloc] initCount:0 elementSize:sizeof(FIELD)
		description:field_desc];
	storInfo = [[Storage alloc] initCount:0 elementSize:sizeof(DBINFO)
		description:storage_desc];
	isnTable = [[Storage alloc] initCount:0 elementSize:BUFFSIZE
		description:isns_desc];
	spaceTable =[[Storage alloc] initCount:0 elementSize:BUFFSIZE
		description:space_table_desc];
	buffers = [[Storage alloc] initCount:0 elementSize:sizeof(BUFFER)
		description:buffer_desc];
	cacheCalls=0;
	cacheHits=0;
	cacheWrites=0;
	[self pushList:0];
	myInfo = (DBINFO *)malloc(sizeof(DBINFO));
	[storInfo addElement:(DBINFO *)myInfo];
	strcpy(myInfo->dBaseName, dbname);
	myInfo->data_file.blks = 1;
	myInfo->data_file.rover = 0;
	myInfo->max_isns = NUM_ISNS;
	myInfo->max_keys = 0;
	myInfo->num_keys = 0;
	myInfo->last_isn = 0;
	myInfo->data_frac = dFraction;
	myInfo->buff_size = BUFFSIZE;
	myInfo->version = VERSION;
	myInfo->nbuf = 10;
	
	if (BUFFSIZE < 256)
	{
		printf("Error:  Buffer sizes less than 256 not allowed\n");
		exit(0);
	}
	if (ISNRECSIZE <= 1)
	{
		printf("Error:  ISNRECSIZE <= 1\n");
		exit(0);
	}
	if (ISNRECSIZE > ISNSIZE)
	{
		printf("Error:  ISNRECSIZE > ISNSIZE\n");
		exit(0);
	}
	
/*
	i = BUFFSIZE;
	printf("%02x",i);
	put_num(tmp_buffer,&i);
	printf("    Buffersize is %d\n",(int)get_num(tmp_buffer));

*/
	for (i=0; i<BUFFSIZE; i++)
		tmp_buffer[i] = '\0';
		
	strcpy(name,myInfo->dBaseName);
	strcat(name,".dat");
	myInfo->data_file.ptr = fopen(name,"w");
	for (i=0;i<myInfo->data_file.blks; i++)
		fwrite(tmp_buffer, sizeof(char), BUFFSIZE, myInfo->data_file.ptr);
	fclose(myInfo->data_file.ptr);
	myInfo->data_file.ptr = fopen(name,"r+");
	
	isns = (int *)calloc(NUM_ISNS,ISNSIZE);
	space_locator = (char *)calloc(BUFFSIZE,sizeof(char));
	
	for (i=0; i<NUM_ISNS; i++)
			isns[i] = -1;
	[isnTable addElement:(int *)isns];
	
	for (i=0; i<BUFFSIZE; i++)
		space_locator[i] = '\0';
	[spaceTable addElement:(char *)space_locator];
		
	buffer = (BUFFER *)malloc(sizeof(BUFFER));
	buffer->block = -1;
	buffer->count = 0;
	buffer->update = FALSE;
	
	for (i=0;i<myInfo->nbuf;i++)
		[buffers addElement:buffer];

	printf("Allocated %d buffers\n",[buffers count]);

	free(buffer);		
	
	
	free(isns);
	free(space_locator);
	return self;
}

- setDescriptors
{

	sprintf(field_desc,"{i[3c]c[16c]c[12c]!!}");
	sprintf(storage_desc,"{[100c]{!ii}iiiifiiiiiiiii}");
	sprintf(isns_desc,"[%di]",NUM_ISNS);
	sprintf(space_table_desc,"[%dc]",BUFFSIZE);
	sprintf(buffer_desc,"{iii[%dc]}",BUFFSIZE);
	sprintf(hash_desc,"{[24c]ii}");
	return self;
}

- close
{
	int i;
	
	for (i=0;i<10;i++)
	{
		buffer = [buffers elementAt:i];
		if (buffer->update == TRUE)
		{
/*
			printf("Writing block %d\n",buffer->block);
*/
			writeRandom(buffer->data,myInfo->data_file.ptr,
					buffer->block,BUFFSIZE);
		}
	}
	[storInfo replace:(DBINFO *)myInfo at:0];
	fclose(myInfo->data_file.ptr);
	return self;
}

- free
{
	[super free];
	return self;
}

- awake
{
	char	name[BUFFSIZE];
	int i;

	[super awake];
	[self setDescriptors];
	myInfo = [storInfo elementAt:0];
	if ( myInfo->buff_size != BUFFSIZE )
		{
			printf("Error: Database %s created with buffersize = %d, not %d\n",
				myInfo->dBaseName,myInfo->buff_size,BUFFSIZE);
			exit(0);
		}
	strcpy(name,myInfo->dBaseName);
	strcat(name,".dat");
	if ((myInfo->data_file.ptr = fopen(name,"r+")) == NULL)
		{
			printf("Error: No data file for DB %s\n",myInfo->dBaseName);
			exit(0);
		}
	cacheCalls=0;
	cacheHits=0;
	cacheWrites=0;
	[self pushList:0];
	
	buffers = [[Storage alloc] initCount:0 elementSize:sizeof(BUFFER) description:buffer_desc];
		buffer = (BUFFER *)malloc(sizeof(BUFFER));
		buffer->block = -1;
		buffer->count = 0;
		buffer->update = FALSE;
		for (i=0;i<myInfo->nbuf;i++)
			[buffers addElement:buffer];
/*
		printf("Allocated %d buffers\n",[buffers count]);
*/
	free(buffer);
/*
	printf("Buffer data addresses are:\n");
	for (i=0;i<myInfo->nbuf;i++)
		{
		buffer = [buffers elementAt:i];
		printf("%d) %02x \n",i,&buffer->data);
		}
*/
	return self;
}

- prtCacheStats
{
	printf("--------Cache Statistics--------\n");
	printf("Calls : %d\n",cacheCalls);
	printf("Writes: %d\n",cacheWrites);
	printf("Hits:   %d\n\n",cacheHits);
	return self;
}

- (int)numberFlds
{
	return [myDFD count];
}

- addFldDesc:(FIELD *)newFldDesc

{
	int *isns, i;
	
	if (newFldDesc->keyed == 'K')	{
	/*
		Allocate key storage isn-lists, chain them
		Allocate heads of hash tables for this key
	*/
		newFldDesc->hash = [[Storage alloc] initCount:0 
			elementSize:sizeof(KEY_VALUE) description:hash_desc];
		newFldDesc->key_isns = [[Storage alloc] initCount:0 
			elementSize:BUFFSIZE description:isns_desc];
		
 		isns = (int *)calloc(NUM_ISNS,ISNSIZE);
		/*	Initialize isns for keys	*/
		for (i=0; i<NUM_ISNS;i++)
			isns[i] = 0;

 		[newFldDesc->key_isns addElement:(int *)isns];
		myInfo->num_keys++;
		[myDFD addElement:(FIELD *)newFldDesc];
		free(isns);
		}
	else
		[myDFD addElement:(FIELD *)newFldDesc];
	return self;
}

- (FIELD *) getFldDesc:(int)theFldNum
{
	if ((theFldNum >= 0) && (theFldNum < [self numberFlds]))
		return [myDFD elementAt:theFldNum];
	else return NULL;
}

- (int)getFldNumForAbbrev:(char *)fldAbbrev
{
	int i = [self numberFlds], j;
	FIELD *myField;
	
	for (j = 0; j<i; j++)
	{
		myField = [self getFldDesc:j];
		if ( !strcmp(myField->abbrev,fldAbbrev))
			return j;
	} 
		return -1;
}

- (int)numKeyedFlds
{
	return myInfo->num_keys;
}

- loadDFD:(FILE *)dfd_file
{
	char		name[BUFFSIZE];
	FIELD *myField = (FIELD *)malloc(sizeof(FIELD));
	
	strcpy(name,myInfo->dBaseName);
	strcat(name,".dfd");
	if((dfd_file = (FILE *)fopen(name,"r")) == NULL)
	{
		printf("Error: loadDFD...descriptor file %s not found\n",name);
		return NULL;
	}

	while(!feof(dfd_file))
	{
	fscanf(dfd_file,"%s %c %d %s %c %s\n",
	myField->abbrev,&(myField->kind),&(myField->display_len)
		,myField->name,&(myField->keyed),myField->edit_mask);
	[self addFldDesc:(FIELD *)myField];
	}
	free(myField);
	close(dfd_file);
	return self;
}

- (char *)getBlk:(int) blkreq
{
	BUFFER *myBuffer;
	
	if (blkreq < myInfo->data_file.blks)
		myBuffer = [self locBlk:blkreq];
	else myBuffer = [self newBlk:blkreq];
	return myBuffer->data;
	
}

- (BUFFER *)newBlk:(int) blkreq
{
	BUFFER *myBuffer;
	int i;
	char *space;
	
	myBuffer = [self locBlk:blkreq];
	
	for (i=0; i<BUFFSIZE; i++)
		myBuffer->data[i] = '\0';
		
	if (blkreq/BUFFSIZE == [spaceTable count])
	{
		space = (char *)malloc(BUFFSIZE);
		for (i=0; i<BUFFSIZE; i++)
			space[i] = '\0';
		[spaceTable addElement:(char *)space];
		free(space);
	}
	
	[self allocSpace:0 in:blkreq];
	myInfo->data_file.blks++;
	return myBuffer;
}

- (BUFFER *)locBlk:(int) blkreq
{
	int i, j, k, lowcount;
	BUFFER *myBuffer;
	
		cacheCalls++;
	
	j = [buffers count];
	for (i=0; i<j; i++)
	{
			myBuffer = [buffers elementAt:i];
			/*	Adjust useage counts */
			myBuffer->count--;
	}

	/*	See if block is already here */
	for (i=0;i<j;i++)
		{
			myBuffer = [buffers elementAt:i];
			if (myBuffer->block == blkreq)
			{
				myBuffer->count++;
				cacheHits++;
				return myBuffer;
			}
		}
	
		myBuffer = [buffers elementAt:0];
		k = 0;
		lowcount = myBuffer->count;
		
		for (i=0;i<j;i++)
		{
			myBuffer = [buffers elementAt:i];
			if (myBuffer->count < lowcount)
			{
				lowcount = myBuffer->count;	
				k = i;
			}
		}
		myBuffer = [buffers elementAt:k];
		if (myBuffer->update == TRUE)
			{
			writeRandom(myBuffer->data,myInfo->data_file.ptr,
					myBuffer->block,BUFFSIZE);
/*
			printf("Block %d written out\n",myBuffer->block);
*/
				cacheWrites++;
			}
		if (blkreq < myInfo->data_file.blks)
			readRandom(myBuffer->data,myInfo->data_file.ptr,
				blkreq,BUFFSIZE);
		myBuffer->block = blkreq;
		myBuffer->update = FALSE;
		myBuffer->count = 0;

		return myBuffer;
}

- write:(NXTypedStream *)typedStream
{
	int i, num_flds;
	FIELD *myField;
	
	[super write:typedStream];
	NXWriteObject(typedStream,myDFD);
	NXWriteObject(typedStream,storInfo);
	NXWriteObject(typedStream,isnTable);
	NXWriteObject(typedStream,spaceTable);
	
	num_flds = [self numberFlds];
	for (i=0; i<num_flds; i++)
	{
		myField = [self getFldDesc:i];
		if (myField->keyed == 'K')
		{
			NXWriteObject(typedStream,myField->key_isns);
			NXWriteObject(typedStream,myField->hash);
		}
	}
	return self;
}

- read:(NXTypedStream *)typedStream
{
	int i, num_flds;
	FIELD *myField;
	
	[super read:typedStream];
	myDFD = NXReadObject(typedStream);
	storInfo = NXReadObject(typedStream);
	isnTable = NXReadObject(typedStream);
	spaceTable = NXReadObject(typedStream);
	num_flds = [self numberFlds];
	for (i=0; i<num_flds; i++)
	{
		myField = [self getFldDesc:i];
		if (myField->keyed == 'K')
		{
			myField->key_isns = NXReadObject(typedStream);
			myField->hash = NXReadObject(typedStream);
		}
	}
	return self;
}

- (int)numberIsns
{
	return myInfo->last_isn;
}

int
locisn(isn,buffer)
	int	isn;
	char buffer[];
{
	int	isnptr,isnlen,isnnum;
		isnptr = 0;
		while ((isnlen = 
			nxtisn(&isnnum,&buffer[isnptr])) != 0) {
		if (isnnum == isn) return(isnptr);
		isnptr = isnptr + isnlen;
		}
		
	return(-1);
}

int
nxtisn(isn,buffer)
	int	*isn;
	char buffer[];
{
	int	isnlen;
	
		isnlen =(int)get_num(&buffer[0]);
		*isn = (int)get_num(&buffer[0+ISNRECSIZE]);
	return(isnlen);
}

int
loc_eob(buffer)
	char buffer[];
{
	int	rec_len, rec_ptr;
	
	rec_ptr = 0;
	while((rec_len = (int)get_num(&buffer[rec_ptr])) != 0)
		rec_ptr = rec_ptr + rec_len;
	return(rec_ptr);
}

int
get_num(buffer)
	char buffer[];
{
	int i, j, *k;
	char tmp[ISNSIZE];
	
	if(ISNRECSIZE != ISNSIZE)
		for(i=0;i<ISNSIZE;i++)
			tmp[i] = '\0';
		
	for(i=0;i<ISNRECSIZE;i++)
	{
		j = ISNSIZE - ISNRECSIZE + i;
		tmp[j] = buffer[i]; ;
	}
		k = &tmp[0];
		return(*k);
}

void
put_num(buffer,val)
	char	buffer[];
	char	val[];
{
	int i, j, k, *l;
	
	for(i=0;i<ISNRECSIZE;i++)
	{	
		k = ISNSIZE - i - 1;
		j = ISNRECSIZE - i - 1;
		buffer[j] =val[k];
	}
	if(ISNRECSIZE < ISNSIZE)
		for(i=ISNRECSIZE;i<ISNSIZE;i++)
			if(val[ISNSIZE-i-1] != '\0')
			{
				l = &val[0];
				printf("\nError: put_num...value %d would be truncated\n",*l);
				printf("         ISNRECSIZE is too small\n");
				exit(0);
			}
	return;	
}

int
pn_N_fld(recordptr)		/* Store a null field		*/
	char	recordptr[];
{
			recordptr[0] = 0x01;
			recordptr[1] = '\0';
			return(1);
}

int
pn_I_fld(j,recordptr)
	char	recordptr[], j[];
{
	int i;
	int val;
		if( sizeof(i) == 2)
				recordptr[0] = 0x03;
				else recordptr[0] = 0x05;
		val=0;
		for (i=0; i < sizeof(i); i++)	{		
			recordptr[i+1] = j[i];
			val = val + j[i];
			}
		if( val == 0)	{
			recordptr[0] = 0x01;
			recordptr[1] = '\0';
			return(1);
		}
			
		/* signals last field */
		recordptr[sizeof(i)+1] = '\0';
		return(sizeof(i)+1);
}
int
pn_L_fld(j,recordptr)
	char	recordptr[], j[];
{
	int i;
	long m;
	int val;	
			if( sizeof(m) == 2)
				recordptr[0] = 0x03;
				else recordptr[0] = 0x05;
		val=0;
		for (i=0; i < sizeof(m); i++)	{		
			recordptr[i+1] = j[i];
			val = val + j[i];
			}
		if( val == 0)	{
			recordptr[0] = 0x01;
			recordptr[1] = '\0';
			return(1);
		}
			
		recordptr[sizeof(m)+1] = '\0';
		return(sizeof(m)+1);
}
int
pn_A_fld(j,recordptr,recordlen)
	char	recordptr[], j[];
	int	recordlen;
{
	int len, i;
	
/*	This should trim off trailing blanks	*/
	if ( (len = strlen(j)) > 0 )	{
		while(j[len] == ' ' )
			if ( --len == 0 ) break;
		}
	/* get length of input string	*/
	len = min(++len,recordlen);
	/* length will not include terminal 0	*/
	recordptr[0]=len+1;
	for (i=0;i<len;i++)	recordptr[i+1] = j[i];
	/* store (semi-)permanent end of record there */
	recordptr[len+1] = '\0';
	return(len+1);
}

- (int)allocSpace: (int)space in:(int) block_allocated
{
	int	charno, i, comp_fact;
	int block;
	char *mybuffer;
	
	/*	byte which represents amount of space used
		depends upon Buffer Size ( 0xFF = 256)
	*/
		comp_fact = BUFFSIZE/256;
	if (space > BUFFSIZE)
	{
		printf("Error: allocSpace...space %d not available\n",space);
		exit(0);
	}

		/* mark space used	*/
	
		charno = block_allocated;
		block = charno/BUFFSIZE;
		charno = mod(charno,BUFFSIZE);
		mybuffer = (char *)[spaceTable elementAt:block];
		/* get space used in block	*/
		i = (mybuffer[charno] & 0xFF) * comp_fact ;
		i = i + space ;		
		if (i < 0 ) {
			printf("Error: allocSpace...deallocation resulted in < 0\n");
			exit(0);
			}
		if (i > BUFFSIZE) {
			printf("Error: allocSpace...requested allocation not possible\n");
			exit(0);
			}
		/* space is divided by comp_fact when stored */
		i = ( i + (comp_fact - 1))/comp_fact;
		mybuffer[charno]  =( i & 0xFF);
	/* Return amount of buffer space used	*/
		myInfo->data_file.rover = block_allocated;
		return((int)i*comp_fact);

}

- (int)findSpace: (int)space :(float)data_frac 
{
	int	rover, charno, i, comp_fact;
	int old_rover, old_block, block;
	char *mybuffer;

	/*	byte which represents amount of space used
		depends upon Buffer Size ( 0xFF = 256)
	*/	
	comp_fact = BUFFSIZE/256;
	if (space > BUFFSIZE)
	{
		printf("Error: findSpace...space %d not available\n",space);
		exit(0);
	}
	
	/*	Find a block big enough to contain a record of length "space"	*/
	rover = myInfo->data_file.rover;
	old_rover = rover;
	old_block = -1;
	i = 0;
	
	while(TRUE)	{
		charno = rover;
		block = charno/BUFFSIZE;
		charno = mod(charno,BUFFSIZE);
		if ( block != old_block )
		{
			mybuffer = (char *)[spaceTable elementAt:block];
			old_block = block;
		}
		i = (mybuffer[charno] & 0xFF) ;
	/* space is divided by comp_fact when stored in space avail list	*/
		i = i * comp_fact;
		if (max(2*ISNSIZE+1,space) + i  <  BUFFSIZE * data_frac )	
		return((int)rover); /* space + used space is adequate	*/
		rover = mod((myInfo->data_file.rover)++,myInfo->data_file.blks);
		if (rover == old_rover)
			return(-1L);
		}
}

- updBuf:(int)block
{
	BUFFER *myBuffer;

	myBuffer = [self locBlk:block];
	myBuffer->update = TRUE;
	return self;	
}

- addKey:(char *)value isnNo:(int)isn field:(FIELD *)myField
{
	KEY_VALUE	*keys;
	KEY_VALUE *fnd_keys;
	int i, j, blk, next, old_blk, *isns;
	
	if ((i =[self findKey:value field:myField]) != -1)
		{
			fnd_keys = [myField->hash elementAt:i];
			fnd_keys->count++;
		if (isn < fnd_keys->first_isn)
			{
				blk = isn/(NUM_ISNS);
				j = mod(isn,(NUM_ISNS));
				isns = [myField->key_isns elementAt:blk];
				isns[j] = fnd_keys->first_isn;
				fnd_keys->first_isn = isn;
			}
			else
			{
		next = fnd_keys->first_isn;
		old_blk = -1;
		while ( next < isn )
		{
			blk = next/(NUM_ISNS);
			j = mod(next, (NUM_ISNS));
			if (blk != old_blk)
			{
				isns = [myField->key_isns elementAt:blk];
				old_blk = blk;
			}
			if((next = isns[j]) == 0)
			{
				isns[j] = isn;
				return self;
			}
		}
		isns[j] = isn;
		blk = isn/(NUM_ISNS);
		j = mod(isn,(NUM_ISNS));
		isns = [myField->key_isns elementAt:blk];
		isns[j] = next;
		}
			return self;
		}
	keys  = (KEY_VALUE *)malloc(sizeof(KEY_VALUE));
	strcpy(keys->key_value,value);
	keys->count = 1;
	keys->first_isn = isn;
	[myField->hash addElement:(KEY_VALUE *)keys];
	free(keys);
	return self;
}

- (int)findKey:(char *)value field:(FIELD *)myField
{
	KEY_VALUE *keys;
	int j, i;
	
	j = [myField->hash count];
	for (i=0; i<j; i++)
	{
		keys = [myField->hash elementAt:i];
		if (!strcmp(keys->key_value,value))
			return i;
	}
	return -1;
}

- deleteKey:(char *)value isnNo:(int)isn field:(FIELD *)myField
{
	KEY_VALUE *fnd_keys;
	int i, j, *isns, blk, old_blk, prev, current, next;
	
	if ((i =[self findKey:value field:myField]) != -1)
		{
			fnd_keys = [myField->hash elementAt:i];
			fnd_keys->count--;
			if (fnd_keys->count == 0)
			{
			[myField->hash removeAt:i];
			return self;
			}
		if (isn == fnd_keys->first_isn)
		{
			blk = fnd_keys->first_isn/(NUM_ISNS);
			j = mod(fnd_keys->first_isn,(NUM_ISNS));
			isns = [myField->key_isns elementAt:blk];
			fnd_keys->first_isn = isns[j];
			isns[j] = 0;
			return self;
		}
		prev = fnd_keys->first_isn;
		current = prev;
		next = current;
		old_blk = -1;
		while (next <= isn)
		{
			blk = next/(NUM_ISNS);
			j = mod(next,(NUM_ISNS));
			if (blk != old_blk)
			{
				isns = [myField->key_isns elementAt:blk];
				old_blk = blk;
			}
			prev = current;
			current = next;
			next = isns[j];
			if (next == 0) break;
		}
		blk = prev/(NUM_ISNS);
		j = mod(prev,(NUM_ISNS));
		isns = [myField->key_isns elementAt:blk];
		isns[j] = next;
		blk = current/(NUM_ISNS);
		j = mod(current,(NUM_ISNS));
		isns = [myField->key_isns elementAt:blk];
		isns[j] = 0;
			return self;
		}
		printf("Warning: deleteKey...Key %s not found\n",value);
		return self;
}

- setKeyField:(char *)abbrev
{
	int i;
	FIELD *myField;
	int *isns;	
	
	if ((myField = 
		[self getFldDesc:[self getFldNumForAbbrev:abbrev]]) == NULL)
	{
		printf("Field %s not in data base\n",abbrev);
		return self;
	}
	
	if (myField->keyed == 'K')
	{
		return self;
	}
	myField->hash = [[Storage alloc] initCount:0 elementSize:sizeof(KEY_VALUE) description:hash_desc];
	myField->key_isns = [[Storage alloc] initCount:0 elementSize:BUFFSIZE description:isns_desc];
	myField->keyed = 'K';
	isns = (int *)calloc(NUM_ISNS, ISNSIZE);
	/* initialize space for key isns */
	for (i=0; i<NUM_ISNS; i++)
		isns[i] = 0;
	
	for (i=0; i<myInfo->max_isns/(NUM_ISNS);i++)
		[myField->key_isns addElement:(int *)isns];
		
	free(isns);
	myInfo->num_keys++;
	[self keyFld:abbrev];
	return self;
}

- unKeyField:(int)fldNum
{
	FIELD *myField;
	
	if ((myField = 
		[self getFldDesc:fldNum]) == NULL)
	{
		printf("Field %d not in data base\n",fldNum);
		return self;
	}
	
	if (myField->keyed != 'K')
		return self;
	printf("Freeing keys for %s\n",myField->abbrev);
	[myField->hash free];
	[myField->key_isns free];
	myField->keyed = 'N';
	[myDFD replace:(FIELD *)myField at:fldNum];
	myInfo->num_keys--;
	[storInfo replace:(DBINFO *)myInfo at:0];
	return self;
}

- unKeyDB
{
	int i, nfields;
	
	if([self numKeyedFlds] == 0)
	{
		printf("Database not keyed\n");
		return self;
	}
	nfields = [self numberFlds];
	
	for (i=0; i<nfields; i++)
		[self unKeyField:i];
	return self;
}

- keyDB
{
	int i,j;
	
	if([self numKeyedFlds] > 0)
	{
		printf("Database already keyed\n");
		return self;
	}
	j = [self numberIsns];
	for(i=0; i<j; i++)
	[self keyRecord:i];
	return self;
}

- prtKeyTableFor:(char *)abbrev
{
	FIELD *myField;
	int i, k, *fnd_isns;
	
	if ((myField = 
		[self getFldDesc:[self getFldNumForAbbrev:abbrev]]) == NULL)
	{
		printf("Field %s not in data base\n",abbrev);
		return self;
	}
	
	if (myField->keyed != 'K')
	{
		printf("Field Not keyed\n");
		return self;
	}

		fnd_isns = [myField->key_isns elementAt:0];
		printf("Data for field %s \n",abbrev);
		printf("----------Dump of first block----------\n");
		for (k=0;k<10;k++)
		{
			printf("%d ) ",k*10);
			for (i=0;i<10;i++)
				printf("%6d ",fnd_isns[i+k*10]);
			printf("\n");
		}
	return self;
}

- (int *)newList:(int)size
{
	int	*tmp;

	tmp = (int *)calloc(size+2,ISNSIZE);
	*tmp = size;
	tmp[size+1] = LASTISN;
	return tmp;
}

- (int *)pushList:(int)size
{
	if(Hold) free(Hold);
	if(Last) Hold = Last;
		else Hold = [self newList:0];
	if(Isns) Last = Isns;
		else Last = [self newList:0];
	Isns = [self newList:size];
	return Isns;
}

- (int *)getIsnListforField:(char *)abbrev value:(char *)value 
{
	KEY_VALUE *keys;
	int j, i, k, *fnd_isns, blk, old_blk, count, next;
	FIELD *myField;
	
	if (!strcmp(abbrev,"*"))
		if(!strcasecmp(value,"ALL"))
		{
			Isns = [self pushList:myInfo->last_isn];
			k = 1;
			for (i=0; i<myInfo->last_isn; i++)
				if([self getBlockwithIsn:i] != -1)
					Isns[k++] = i;
			Isns[k] = LASTISN;
			*Isns	= k - 1;
			return Isns;
		}

	if ((myField = 
			[self getFldDesc:[self getFldNumForAbbrev:abbrev]]) == NULL)
	{
		printf("Field %s not in data base\n",abbrev);
		return [self pushList:0];
	}
	
	if (myField->keyed != 'K')
	{
		printf("Field Not keyed\n");
		return [self pushList:0];
	}

	if((j = [self findKey:value field:myField]) != -1)
	{
		keys = [myField->hash elementAt:j];
		count = keys->count;
		old_blk = -1;
		Isns = [self pushList:count];
		next = keys->first_isn;
		i = 0;
		while(1)
		{
			Isns[i+1] = next;
			i++;	/* i points to location just stored into */
			blk = next/(NUM_ISNS);
			k = mod(next,(NUM_ISNS));
			if (blk != old_blk)
			{
				fnd_isns = [myField->key_isns elementAt:blk];
				old_blk = blk;
			}
			next = fnd_isns[k];
			if (next == 0) break;
		}
		Isns[0] = count;
		Isns[i+1] = LASTISN;
		if(count != i)
			printf("Warning: getIsnListforField...count=%d, #found = %d\n",
				count,i);
		return Isns;
	}
		return [self pushList:0];	
}

- (int *)getIsnListforField:(char *)abbrev value:(char *)value operator:(char *)oper;
{
	FIELD *myField;
	int	i, j, *new, *isns;
	char source[BUFFSIZE], field[256];
	
	if ((myField = 
			[self getFldDesc:[self getFldNumForAbbrev:abbrev]]) == NULL)
	{
		printf("Field %s not in data base\n",abbrev);
		return [self pushList:0];
	}
	
	switch(oper[0])	{
		case '=':
		case '^':
			break;
		default:
			printf("Operator %s not implemented for search\n",oper);
			return [self pushList:0];
	}
	
	Isns = [self pushList:*Isns];
	new = Isns;
	*new = 0;
	isns = Last;
	
	++isns; ++new;
	
	strcpy(field,abbrev);
	strcat(field,",128,");
	
		switch(oper[0])	{
			case '=':			/* string equality	*/	
	for (i=0;i<*Last;i++)	{
		[self getData:source atIsn:*isns describedBy: field];
				if(!strcmp(source,value))	{
					*new = *isns;
					(*Isns)++;
					new++;
					}
				isns++;
				}
				*new = LASTISN;
				break;
			case '^':			/*	string contains */
	for (i=0;i<*Last;i++)	{
		[self getData:source atIsn:*isns describedBy: field];
		for (j=0;j<=strlen(source)-strlen(value);j++)	{
				if(!strncmp(&source[j],value,strlen(value)))	{
					*new = *isns;
					(*Isns)++;
					new++;
					break;
					}
				}
		isns++;
		}
				*new = LASTISN;
				break;
			}
	return Isns;
}

- listKeysForField:(char *)abbrev
{
	KEY_VALUE *keys;
	int j, i;
	FIELD *myField;
	
	if ((myField = 
		[self getFldDesc:[self getFldNumForAbbrev:abbrev]]) == NULL)
	{
		printf("Field %s not in data base\n",abbrev);
		return self;
	}
	
	if (myField->keyed != 'K')
	{
		printf("Field Not keyed\n");
		return self;
	}
	j = [myField->hash count];
	for (i=0; i<j; i++)
	{
		keys = [myField->hash elementAt:i];
		printf("%d) %s - %d -> %d\n",
			i,keys->key_value,keys->count,keys->first_isn);
	}
	return self;
}

- (int) addData:(char *)source:(char *)descript
{
	int i, j, blk_alloc, use_isn;
	
	i = get_reclen(descript);
	if((blk_alloc = 
		[self findSpace:i :myInfo->data_frac]) == -1L)
	{
		[self getBlk:myInfo->data_file.blks];
		if((blk_alloc = 
			[self findSpace:i :myInfo->data_frac]) == -1L)
		{
			printf("Error: addData...Unable to allocate new space\n");
			exit(0);
		}
/*
		printf("Allocated new data block %d\n",blk_alloc);		
*/
	}
	use_isn = [self geta_isn];
	j = [self addrec:blk_alloc:use_isn:source:descript];
	[self setIsn:use_isn ToBlock:blk_alloc];
	[self updBuf:blk_alloc];
	[self allocSpace:j in:blk_alloc];
	if([self numKeyedFlds] > 0)
		[self keyRecord:use_isn];
	return use_isn;
}

- (fld_index *)bldFldIndex:(char *)descript
{
	static char old_descript[BUFFSIZE] = "";
	static	int	*myfld;
	static	int	*myofset;
	static	int	l;
	int i,j,k, fldnum, fldlen;
	char	fld_name[3];
	static fld_index *myIndex;
	
	if (strcmp(old_descript,descript) != 0)	{
		strcpy(old_descript,descript);
	
	l = [self numberFlds];
	if(strcmp(old_descript,""))
	{
		myfld = (int *)calloc(l,sizeof(int));
		myofset = (int *)calloc(l,sizeof(int));
		myIndex = (fld_index *)malloc(sizeof(fld_index));
	}
	for (i=0; i<l;i++)	{
		myfld[i] = 0;
		myofset[i] = 0;
		}
	i = 0;
	j = 0;
	l = 0;
	k = 0;
	while(i < strlen(descript))	{
		j = get2(&descript[i],&fld_name,&fldlen);
		i = i + j;
		if ((fldnum = [self getFldNumForAbbrev:fld_name]) == -1 )
		{
		 printf("Error: addrec...bad field %s in descript\n",fld_name);
		 exit(0);
		}
		l = max(l,fldnum);
		myfld[fldnum] = fldlen;
		myofset[fldnum] = k;
		k = k + fldlen;
		}
		
	myIndex->fld = myfld;
	myIndex->ofset = myofset;
	myIndex->l = l + 1;
	}
	return myIndex;

}

- keyRecord:(int)isn
{
	char *mybuffer;
	char	num_str[BUFFSIZE];
	int j, k, nfields, blk;
	FIELD *myField;
	
	/* key data for record in isn */
	if ((blk = [self getBlockwithIsn:isn]) == -1)
		return NULL;	
	mybuffer = (char *)[self getBlk:blk];
	j = locisn(isn,&mybuffer[0]);
	nfields = [self numberFlds];
	for (k = 0; k < nfields; k++)	{
				myField = [self getFldDesc:k];
		if ( myField->keyed == 'K' )
			switch (myField->kind)	{
				case	'I':
					if(gn_I_fld(num_str,k,&mybuffer[j]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"0" isnNo:isn field:myField];
					break;
				case	'L':
					if(gn_L_fld(num_str,k,&mybuffer[j]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"0" isnNo:isn field:myField];
					break;
				case	'A':
					if(gn_A_fld(num_str,k,BUFFSIZE,&mybuffer[j]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"" isnNo:isn field:myField];
					break;
			}
			
	}
	return self;
}

- deKeyRecord:(int)isn
{
	char *mybuffer;
	char	num_str[BUFFSIZE];
	int j, k, nfields, blk;
	FIELD *myField;
	
	/* de-key data for record in isn */	
	if((blk = [self getBlockwithIsn:isn]) == -1)
		return NULL;
	mybuffer = (char *)[self getBlk:blk];
	j = locisn(isn,&mybuffer[0]);
	nfields = [self numberFlds];
	for (k = 0; k < nfields; k++)	{
				myField = [self getFldDesc:k];
		if ( myField->keyed == 'K' )
			switch (myField->kind)	{
				case	'I':
					if(gn_I_fld(num_str,k,&mybuffer[j]) != 0)
					[self deleteKey:num_str isnNo:isn field:myField];
					else [self deleteKey:"0" isnNo:isn field:myField];
					break;
				case	'L':
					if(gn_L_fld(num_str,k,&mybuffer[j]) != 0)
					[self deleteKey:num_str isnNo:isn field:myField];
					else [self deleteKey:"0" isnNo:isn field:myField];
					break;
				case	'A':
					if(gn_A_fld(num_str,k,BUFFSIZE,&mybuffer[j]) != 0)
					[self deleteKey:num_str isnNo:isn field:myField];
					else [self deleteKey:"" isnNo:isn field:myField];
					break;
			}
			
	}
	return self;
}

- keyFld:(char *)abbrev
{
	int	fldNum, i, j;
	
	if((fldNum = [self getFldNumForAbbrev:abbrev]) == -1)
	{
		printf("Error: keyFld...field %s not defined\n",abbrev);
		exit(0);
	}
	
	j = [self numberIsns];
	for (i=0; i<j; i++)
		[self keyFld:fldNum forIsnNum:i];
	return self;
}

- keyFld:(int)fldNum forIsnNum:(int)isn
{
	char *mybuffer;
	char	num_str[BUFFSIZE];
	int j, k, nfields, blk;
	FIELD *myField;
	
	/* key data for record in isn */
	if ((blk = [self getBlockwithIsn:isn]) == -1)
		return NULL;	
	mybuffer = (char *)[self getBlk:blk];
	j = locisn(isn,&mybuffer[0]);
	nfields = [self numberFlds];
	k = fldNum;
	myField = [self getFldDesc:k];
		if ( myField->keyed == 'K' )
			switch (myField->kind)	{
				case	'I':
					if(gn_I_fld(num_str,k,&mybuffer[j]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"0" isnNo:isn field:myField];
					break;
				case	'L':
					if(gn_L_fld(num_str,k,&mybuffer[j]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"0" isnNo:isn field:myField];
					break;
				case	'A':
					if(gn_A_fld(num_str,k,BUFFSIZE,&mybuffer[j]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"" isnNo:isn field:myField];
					break;
			}
	return self;
}

- (int)addrec:(int)blkno:(int)isn:(char *)source:(char *)descript
{
	char	*mybuffer;
	FIELD	*myField;
	int	hold,i,j, k, l, zero;
	fld_index *myFlds;
	
	myFlds = [self bldFldIndex:descript];
	zero = 0;
	l = [self numberFlds];
	i = 0;
	mybuffer = (char *)[self getBlk:blkno];
	i = loc_eob(&mybuffer[0]);
	hold = i;
	put_num(&mybuffer[i+ISNRECSIZE],&isn);
	/* point past rec len and isn num	*/
	i = i + ISNRECSIZE + ISNRECSIZE;
	/* reclen is stored in first ISNRECSIZE bytes */
	/* isn is stored in next ISNRECSIZE bytes	*/
	j = i;
	
	for (k = 0; k <l; k++)	{
				myField = [self getFldDesc:k];
				if (myFlds->fld[k] == 0)
				{
					i = pn_N_fld(&mybuffer[j]);
					j = i+j;
				}
	else	switch (myField->kind)	{
			case	'I':
				i = pn_I_fld(&source[myFlds->ofset[k]],&mybuffer[j]);
					j=i+j;
					break;
			case	'L':
				i = pn_L_fld(&source[myFlds->ofset[k]],&mybuffer[j]);
					j=i+j;
					break;
			case	'A':
				i = pn_A_fld(&source[myFlds->ofset[k]],
						&mybuffer[j],myFlds->fld[k]);
					j=i+j;
					break;
			}
			
	}
	/* store record len	*/
	k = j - hold;
	put_num(&mybuffer[hold+0],&k);	
	/* return record len	*/
	return(k);
}

- getData:(char *)source atIsn:(int)isn describedBy:(char *)descript 
{
	char	*mybuffer;
	FIELD	*myField;
	int blkno;
	int	i,j, k, zero;
	fld_index *myFlds;
	
	if ((blkno = [self getBlockwithIsn:isn]) == -1)
		return NULL;
	
	myFlds = [self bldFldIndex:descript];
	zero = 0;
	i = 0;
	mybuffer = (char *)[self getBlk:blkno];
	j = locisn(isn,&mybuffer[0]);
	for (k = 0; k < myFlds->l; k++)	{
			myField = [self getFldDesc:k];
			if (myFlds->fld[k] != 0) 
			switch (myField->kind)	{
			case	'I':
				gn_I_fld(&source[myFlds->ofset[k]],k,&mybuffer[j]);
					break;
			case	'L':
				gn_L_fld(&source[myFlds->ofset[k]],k,&mybuffer[j]);
					break;
			case	'A':
				gn_A_fld(&source[myFlds->ofset[k]],
					k,myFlds->fld[k],&mybuffer[j]);
					break;
			}
			
	}
	return self;
}

- (int)updateDataAtIsn:(int)isn with:(char *)data
	describedBy:(char *)description
{
	int i, j, k, space_used, blk_alloc, use_isn;
	char tmp_buffer[BUFFSIZE];
	
	if((j = [self getBlockwithIsn:(int)isn]) == -1) return -1;
	k = - [self delrec:j:isn:tmp_buffer];
	[self updBuf:j];
	space_used = [self allocSpace:k in:j];
	i = get_reclen(description) - k;
	if((blk_alloc = [self findSpace:i :(float)0.99]) == -1L)
	{
		[self getBlk:myInfo->data_file.blks];
		if((blk_alloc = [self findSpace:i :(float)0.99]) == -1L)
		{
		printf("Error: updateDataAtIsn...Unable to allocate new space\n");
			exit(0);
		}
	}
	use_isn = isn;
	j = [self updrec:blk_alloc:use_isn:data:description:tmp_buffer];
	[self setIsn:use_isn ToBlock:blk_alloc];
	[self updBuf:blk_alloc];
	space_used = [self allocSpace:j in:blk_alloc];
	return use_isn;
}

- (int)updrec:(int)blkno:(int)isn:(char *)source:(char *)
	descript:(char *)old_buffer
{
	char	*mybuffer;
	char	num_str[BUFFSIZE];
	FIELD	*myField;
	int	hold,i,j, k, zero;
	fld_index *myFlds;

	int nfields = [self numberFlds], mm;
	
	myFlds = [self bldFldIndex:descript];
	zero = 0;

	i = 0;
	mybuffer = (char *)[self getBlk:blkno];
		
	/* get place to put updated record */
	i = loc_eob(&mybuffer[0]);
	hold = i;
	put_num(&mybuffer[i+ISNRECSIZE],&isn);
	/* point past rec len and isn num	*/
	i = i + ISNRECSIZE + ISNRECSIZE;
	/* reclen is stored in first ISNRECSIZE bytes */
	/* isn is stored in next ISNRECSIZE bytes	*/
	j = i;
	for (k = 0; k < nfields; k++)	{
			myField = [self getFldDesc:k];
			if  (myFlds->fld[k] == 0)
				if (( mm = loc_fld(k,&old_buffer[0])) != -1)	{
						i = old_buffer[mm];
						memcpy(&mybuffer[j],&old_buffer[mm],i);				
						j = i+j;
						}
					else	{
						i = pn_N_fld(&mybuffer[j]);
						j=i+j;
						}
		else	switch (myField->kind)	{
			case	'I':
				i = pn_I_fld(&source[myFlds->ofset[k]],
					&mybuffer[j]);
				j=i+j;
				if(myField->keyed == 'K')
				{
					if((gn_I_fld(num_str, k, &old_buffer[0])) != 0)
					[self deleteKey:num_str isnNo:isn field:myField];
					else [self deleteKey:"0" isnNo:isn field:myField];
					if(gn_I_fld(num_str,k,&mybuffer[hold]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"0" isnNo:isn field:myField];
				}
					break;
			case	'L':
				i = pn_L_fld(&source[myFlds->ofset[k]],
					&mybuffer[j]);
					j=i+j;
				if(myField->keyed == 'K')
				{
					if((gn_L_fld(num_str, k, &old_buffer[0])) != 0)
					[self deleteKey:num_str isnNo:isn field:myField];
					else [self deleteKey:"0" isnNo:isn field:myField];
					if(gn_L_fld(num_str,k,&mybuffer[hold]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"0" isnNo:isn field:myField];
				}
					break;
			case	'A':
			i = pn_A_fld(&source[myFlds->ofset[k]],
				&mybuffer[j],myFlds->fld[k]);
					j=i+j;
				if(myField->keyed == 'K')
				{
					if((gn_A_fld(num_str, k, BUFFSIZE, &old_buffer[0])) != 0)
					[self deleteKey:num_str isnNo:isn field:myField];
					else [self deleteKey:"" isnNo:isn field:myField];
					if(gn_A_fld(num_str,k,BUFFSIZE, &mybuffer[hold]) != 0)
					[self addKey:num_str isnNo:isn field:myField];
					else [self addKey:"" isnNo:isn field:myField];
				}
					break;
			}
			
	}
	/* store record len	*/
	k = j - hold;
	put_num(&mybuffer[hold+0],&k);
	/* return record len	*/
	return(k);
}


- deleteDataAtIsn:(int)isn
{
	int j, k, space_used;
	char tmp_buffer[BUFFSIZE];
	
	if((j = [self getBlockwithIsn:(int)isn]) == -1) return NULL;
	[self deKeyRecord:isn];
	k = - [self delrec:j:isn:tmp_buffer];
	[self setIsn:isn ToBlock:-1L];
	[self updBuf:j];
	space_used = [self allocSpace:k in:j];
	return self;
}

-(int)delrec:(int)block:(int)isn:(char *)tmp_buffer
{
	char *buff_ptr;
	int	i,j,k,isnlen, nxt_isn,len_of_remainder;
/*
	FIELD *myField;
*/
	i = 0;
	j = 0;
	
	buff_ptr= (char *)[self getBlk:block];
	if ((j = locisn(isn,&buff_ptr[0]) ) == -1)
	{
		printf("Error: delrec...isn %d not located\n",isn);
	}
	isnlen = (int)get_num(&buff_ptr[j]);
	
	for (i=0;i<=isnlen;i++)
		tmp_buffer[i] = buff_ptr[j+i];
		
		/* points to next record or eob	*/
		nxt_isn = j + isnlen;
		/* points to eob	*/
		len_of_remainder = loc_eob(&buff_ptr[j]);
		
	k = nxt_isn;
	
	for(i=0;i<len_of_remainder;i++)
		buff_ptr[j++] = buff_ptr[k++];
	k = 0;
		
	put_num(&buff_ptr[j],&k);
			
	return(isnlen);
}

int
readRandom(p,fptr,blknum,blksize)
	int blksize;
	int blknum;
	char *p;
	FILE *fptr;
{
	long offset, curloc;
	
	if((curloc = ftell(fptr)) == -1L) {
	printf("Error: readRandom...ftell error at block %ld\n", blknum);
		exit(0);
		}
	offset = blknum * blksize - curloc;
	if(fseek(fptr, offset, 1) != 0)	{
	printf("Error: readRandom...fseek error at block %ld\n", blknum);
		exit(0);
		}
	fread(p, blksize, 1, fptr);
	if (ferror(fptr) != 0)	{
	printf("Error: readRandom...read error at block %ld\n", blknum);
		exit(0);
		}
	return(TRUE);		
}

int
writeRandom(p,fptr,blknum,blksize)
	int blksize;
	int blknum;
	char *p;
	FILE *fptr;
{
	long offset, curloc;
	
	if((curloc = ftell(fptr)) == -1L)	{
	printf("Error: writeRandom...ftell error at block %ld\n", blknum);
		exit(0);
		}
	offset = blknum * blksize - curloc;
	if(fseek(fptr, offset, 1) != 0)	{
	printf("Error: writeRandom...fseek error at block %ld\n", blknum);
		exit(0);
		}
	fwrite(p, blksize, 1, fptr);
	if (ferror(fptr) != 0)	{
	printf("Error: writeRandom...fwrite error at block %ld\n", blknum);
		exit(0);
		}
	return(TRUE);		
}

- (int)getBlockwithIsn:(int)isn
{
	int	k, block;
	int	j;
	int  *isns;
	
	if ( isn < 0 )
	{
		printf("Error: getisn...isn < 0\n");
		return(-1);
	}
	if (isn >= myInfo->last_isn )
	{
		printf("Error: getisn...isn > last_isn\n");
		return(-1);
	}
	
	j = isn/(NUM_ISNS);
	/* get offset into isn_block	*/
	k = (isn - j * (NUM_ISNS));
		
	isns = [isnTable elementAt:j];
	block = isns[k];
	return(block- 1);		
	/* block is stored +1 to allow 0 to mean no block for that isn	*/
}

- setIsn:(int)isn ToBlock:(int)block
{
	int	k;
	int	j;
	int  *isns;
	
	if ( isn < 0 )
	{
		printf("Error: setisn...isn < 0\n");
		exit(0);
	}
	if (isn > myInfo->max_isns )
	{
		printf("Error: setisn...isn > max_isns\n");
		exit(0);
	}
	
	j = isn/(NUM_ISNS);
	/* get offset into isn_block	*/
	k = (isn - j * (NUM_ISNS));
		
	isns =[isnTable elementAt:j];
	/* block is stored +1 to allow 0 to mean no block for that isn	*/
	isns[k] = block + 1;
	return self;		
}

- (int)geta_isn
{
	int	i;
	/* 0 is stored to mean empty.. getisn subs 1 to allow
		access to block 0 , i.e. block 0 is stored as 1
	*/	
	for (i=0;i<myInfo->last_isn;i++)
		if ([self getBlockwithIsn:i] == -1) return(i);
	i = myInfo->last_isn;
	if (myInfo->last_isn == myInfo->max_isns)
		[self newIsnBlock];
	myInfo->last_isn++;
	return(i);
}
		
- newIsnBlock
{
	int *isns = (int *)calloc(NUM_ISNS,ISNSIZE);
	int *newisns = (int *)calloc(NUM_ISNS,ISNSIZE);
	int i,j,k, old_max;
	FIELD *myField;
/*
	KEY_VALUE *myKeys;
	int *key_isns;
*/
	
	old_max = myInfo->max_isns;
	for (i=0; i< NUM_ISNS; i++)
		isns[i] = -1;
	
/*
	printf("***Adding isn Table element ***\n");
*/
	[isnTable addElement:(int *)isns];
	myInfo->max_isns = myInfo->max_isns + NUM_ISNS;

	j = [self numberFlds];
	for (k=0;k<j;k++)
	{
		myField = [self getFldDesc:k];
	/* For each keyed field */
	if (myField->keyed == 'K')
		{
		/* Initializing new keyisn space */
			for (i=0; i< NUM_ISNS; i++)
				newisns[i] = 0;
		/* Store new block */
/*
	printf("***Adding isn Table element for key %s***\n",myField->abbrev);
*/
		[myField->key_isns addElement:(int *)newisns];
		}
	}
	free(isns);
	free(newisns);
	return self;
}
	
int
get_reclen(descript)
	char	 descript[];
{
	int	i, j, k, fldlen;
	char fld_name[3];
	
	k = 0;
	i = 0;
	
	while(i < strlen(descript))	{
		j = get2(&descript[i],&fld_name,&fldlen);
		i = i + j;
		/* need room to store length	*/
		k = k + fldlen+1;
		}
	/* room for isn, record len, and final byte	*/
	return(k+ISNRECSIZE+ISNRECSIZE+1);
}

int
get2(descript,fld,fld_len)
	char	fld[], descript[];
	int  *fld_len;
{
	int m;
	long k;

	m = 0;
	while (descript[m] != '\0')	{
		fld[0] = descript[m++];
		fld[1] = descript[m++];
		fld[2] = '\0';
		m++;	/* skip past comma	*/
		switch (descript[m])	{
			case 'I':
				m++;
				*fld_len = sizeof(m);
				return(++m);
			case 'L':
				m++;
				*fld_len = sizeof(k);
				return(++m);
			default:
				*fld_len = (descript[m++] & 0x0f);
				if ( descript[m] == '\0' ) break;
				while (descript[m] != ',')
				*fld_len = *fld_len * 10 + (descript[m++] & 0x0f);
				return(++m);
			}
	}
	return(m);
}

- prtRecAtIsn:(int)isn
{
	int i, ii, j, k, blkno;
	int	nfields = [self numberFlds];
	long ll;
	char *mybuffer, prt_str[BUFFSIZE];
	FIELD *myField;
	
	i = 0;
	if ((blkno = [self getBlockwithIsn:isn]) == -1) return self;
	mybuffer = (char *)[self getBlk:blkno];
	j = locisn(isn,&mybuffer[0]);
	for (k = 0; k <  nfields; k++)
		{
			myField = [myDFD elementAt:k];	
			switch (myField->kind)	{
				case	'I':
					ii = 0;
					gn_I_fld(&ii,k,&mybuffer[j]);
					printf("%s : %d\n",myField->name,ii);
					break;
				case	'L':
					ll = 0;
					gn_L_fld(&ll,k,&mybuffer[j]);
					printf("%s : %ld\n",myField->name,ll);
					break;
				case	'A':
				strcpy(prt_str,"");
		gn_A_fld(prt_str,k,
			myField->display_len+1,&mybuffer[j]);
			printf("%s : %s\n",myField->name,prt_str);
					break;
			}
			
		}
	return self;
}

- status
{
	int	i,k,isns_used;
	FIELD *myField;
	
	isns_used = 0;
	
	printf("------- Status Report for Data Base ---- %s ---\n",
		myInfo->dBaseName);
	for (i = 0;i<myInfo->last_isn;i++)
		if ( [self getBlockwithIsn:i] != -1) isns_used++;
		
	printf(" Created using version %d of software\n", myInfo->version);
	printf(" %d ISNs used out of a maximum of %d - next isn = %d\n",
		isns_used,myInfo->max_isns,myInfo->last_isn);
	printf(" %d Buffers allocated, size = %d\n",
		myInfo->nbuf, BUFFSIZE);
	printf(" %d isns stored per block\n",NUM_ISNS);
	printf(" Update space : %d percent\n",(int)((1.-myInfo->data_frac)*100));
	printf(" Last dump date and time were : %d %d\n"
		,myInfo->dump_date,myInfo->dump_time);
	printf(" There are %d fields defined for the database\n"
		,[self numberFlds]);
	printf(" There are %d keyed fields \n",
		[self numKeyedFlds]);
	printf("------- FILE USAGE--------------------------\n");
	printf("      FILE         ");
	for(i=0;i<8;i++)		printf("<%-5d",(i+1)*BUFFSIZE/8-1);
	printf(" TOTAL\n");
	printf("------------------------------------------------------------\n");
	[self prt_one_status];
	printf("------------------------------------------------------------\n");
	k = 0;
	for (i=0;i<[self numberFlds];i++)
	{
		myField = [self getFldDesc:i];
		if (myField->keyed == 'K')
			k = k + [myField->hash count] * sizeof(KEY_VALUE);
	}
	
	printf("Key value storage = %d bytes\n",
		k);
	printf("Isn storage = %d bytes\n",BUFFSIZE*[isnTable count]);
	printf("Key list storage = %d bytes\n",BUFFSIZE*[isnTable count]
		* [self numKeyedFlds]);
	printf("\n");
	return self;
}
	
- prt_one_status
{
		int	charno,i,j,k, comp_fact;
		int	block, old_block, use[8], total_avail;
		char *mybuffer;
		file_data *file_info = &(myInfo->data_file);

		comp_fact = BUFFSIZE/256;
		/*	amount of space represented by "1" in
			in block alloc table					*/
										
		old_block = -1;
		for (i = 0; i<8; i++)	use[i] = 0;
		total_avail = file_info->blks;
		
		for (k =0;k<file_info->blks;k++)	{
			charno = k;
			block = charno/BUFFSIZE;
			charno = mod(charno,BUFFSIZE);
			if ( block != old_block )
			{
				mybuffer = [spaceTable elementAt:block];
				old_block = block;
			}
		i = (mybuffer[charno] & 0XFF);
		i = i * comp_fact;
		/* space is divided by comp_fact in space avail list*/
		j =i /(BUFFSIZE/8);
		use[j] = use[j] + 1;
		}
		printf("%10.10s:      ",myInfo->dBaseName);
		for (i=0;i<8;i++)
			printf("%6d",use[i]);
		printf("%6d\n",total_avail);
		return self;
}
		
- prtStructure
{
		int i, j= [self numberFlds];
		FIELD *myField;
		
printf("--------- Record Structure  -----%s-------\n",myInfo->dBaseName);
	for(i=0;i<j;i++)	{
	myField = [myDFD elementAt:i];
	printf("%2d>%s:%c:%d \t%-16s %c %s\n",i,myField->abbrev,
			myField->kind,myField->display_len,
			myField->name,myField->keyed,
			myField->edit_mask);
		}
	printf("---------------------------------------------- \n");

	return self;
}

int
gn_I_fld(dptr,j,recordptr)
	int	j;
	char	dptr[],recordptr[];
{
	int i,k,l,field_len;

	if ((i = loc_fld(j,&recordptr[0])) == -1)
		field_len = 1;
		else	field_len = recordptr[i];
	if ((field_len-1) == 0)	{
			for (l = 0; l < sizeof(k); l++)
		 		dptr[l] = '\0';
		 	return(0);
		}
		for (l = 0; l < sizeof(k); l++)
		 dptr[l] = recordptr[l+i+1];
	return(1);
}

int
gn_L_fld(dptr,j,recordptr)
	int	j;
	char	dptr[],recordptr[];
{
	int i,l,field_len;
	long k;
	
	if ((i = loc_fld(j,&recordptr[0])) == -1)
		field_len = 1;
		else		field_len = recordptr[i];
	if ((field_len-1) == 0)	{
			for (l = 0; l < sizeof(k); l++)
		 		dptr[l] = '\0';
		 	return(0);
		}
	for (l = 0; l < sizeof(k); l++)
		 dptr[l] = recordptr[l+i+1];
	return(1);
}

int
gn_A_fld(dptr,j,lenreq,recordptr)
	int j,lenreq;
	char	dptr[],recordptr[];
{
	int	i,field_len,k;

	if ((i = loc_fld(j,&recordptr[0])) == -1)	
		field_len = 1;
		else	field_len = recordptr[i] - 1;
	if ((field_len - 1) == 0)	{
		strcpy(&dptr[0],"");
		return(0);
		}
	field_len= min(field_len,lenreq-1);
	/* be sure to leave room for terminating null */
		for (k=0; k<field_len; k++)	{
			dptr[k] = recordptr[i+k+1];
			dptr[k+1] = '\0';
	/* append null at end since not stored in record */
			}
	return(1);
}

- dumpSpace
{
	int i,j, k;
	char	*mySpace;
	
	j = [spaceTable count];
	printf("Dumping %d blocks of space table info\n",j);
	for (i=0; i<j; i++)
	{
		printf("Block %d ---------------\n",i);
		mySpace = (char *)[spaceTable elementAt:i];
		for (k=0; k<BUFFSIZE; k++)
		{
			if (mod (k,16) == 0)
				printf("\n");
			printf("%02x",(mySpace[k] & 0xff));
		}
	printf("\n");
	}
	return self;
}

- dumpIsns
{
	int i,j, k;
	int	*myIsns;
	
	j = [isnTable count];
	printf("Dumping %d blocks of isn table info\n",j);
	for (i=0; i<j; i++)
	{
		printf("Block %d ---------------\n",i);
		myIsns = (int *)[isnTable elementAt:i];
		for (k=0; k<NUM_ISNS; k++)
		{
			if (mod (k,10) == 0)
				printf("\n");
			printf("%5d ",*myIsns++);
		}
	printf("\n");
	}
	return self;
}

- dumpKeys
{
	return self;
}

int
loc_fld(i,buffer)
	int	i;
	char	buffer[];
{
	int field_ptr,k,field_len;
	
	/*	step past reclen and isn#	*/
	field_ptr = ISNRECSIZE + ISNRECSIZE;	
	if (i == 0) return(field_ptr);
		for (k=1; k <= i; k++)	{
		field_len = buffer[field_ptr];
		if (field_len == '\0') return(-1);
		field_ptr = field_ptr + field_len;
		}
	if (buffer[field_ptr] == '\0') return(-1);
		else		return(field_ptr);
}

- dumpIsns:(int *)isns toFile:(char *)dump_file_name
{
	char	*mybuffer;
	char	a_out[256];
	int	i,j,k,l,len, i_out, l_out, isn;
	FILE	*dump_file;
	char str[3] = "|";
	FIELD *myField;
	
	dump_file = (FILE *)fopen(dump_file_name,"w");
	
	myInfo->dump_date = 0101;
	myInfo->dump_time = 151500;

	fprintf(dump_file,"%s%d%s%d\n",str,myInfo->dump_date,str,
		myInfo->dump_time,str);

/*
	if (myInfo->journal_flag)
		journal(dbname,tst_info->last_isn,DU,
			&buffer[0],tst_info->data_file.ptr);
*/
	for (i=0;i<isns[0];i++)
		{
		isn = isns[i+1];
		if ((j = [self getBlockwithIsn:isn]) != -1 )	{
		mybuffer = (char *)[self getBlk:j];
		j = locisn(isn,&mybuffer[0]);
		fprintf(dump_file,"%s\n%d",str,isn);
	for (k = 0; k < [self numberFlds]; k++)
			{
				myField = [self getFldDesc:k];
				switch (myField->kind)	{
				case	'I':
						i_out = 0;
					if ((l = gn_I_fld(&i_out,k,&mybuffer[j])) != 0)
							fprintf(dump_file,"%s%d",str,i_out);
						else
							fprintf(dump_file,"%s",str);
					break;
				case	'L':
						l_out = 0;
					if ((l = gn_L_fld(&l_out,k,&mybuffer[j])) != 0)
							fprintf(dump_file,"%s%ld",str,l_out);
						else
							fprintf(dump_file,"%s",str);
					break;
				case	'A':
					len = 256;
					strcpy(a_out,"");
					if ((l = gn_A_fld(a_out,k,len,&mybuffer[j])) != 0)
							fprintf(dump_file,"%s%s",str,a_out);
						else
							fprintf(dump_file,"%s",str);
					break;
			}
		}	
	}
	}
	fprintf(dump_file,"\n");
	fclose(dump_file);
	
/*	Write completion of dump at date time to journal if required 	*/
	return self;
}


- loadDBfromFile:(char *)file_name
{
	FILE *fptr;
	char *desc_str = (char *)calloc(BUFFSIZE,sizeof(char));
	char *data_str = (char *)calloc(BUFFSIZE,sizeof(char));
	char delim;
	
	if(!(fptr = fopen(file_name,"r")))
	{
		printf("Error: loadDBFromFile...file %s not found\n",file_name);
		exit(0);
	}
	
	fgets(desc_str,128,fptr);	/* get dump dates */
	fgets(data_str,128,fptr);	/* get next line */
	delim = desc_str[0];
	
	while([self loadDB:fptr:desc_str:data_str:delim])
	{
		[self addData:data_str:desc_str];
	}	
	free(desc_str);
	free(data_str);
	return self;
}

- (int)loadDB:(FILE *)fptr:(char *)desc_str:(char *)data_str:(char)delim
{
	int i, ii, k;
	long ll;
	char *mybuffer, prt_str[1024], tmp_str[1024],
		 *tmp, *hold_desc, *hold_data;
	FIELD *myField;
		
	hold_desc = desc_str;
	hold_data = data_str;
	strcpy(desc_str,"");
	strcpy(data_str,"");
	
	if(fgets(prt_str,1023,fptr) == NULL) return(FALSE);
	mybuffer = prt_str;
	while(*++mybuffer != delim);
	k = 0; 
	while(*mybuffer != '\0' && *mybuffer != '\n')	{
			if(*++mybuffer == delim || *mybuffer == '\n') {
				if(++k >  [self numberFlds]) break;
				continue;
			}
				myField = [self getFldDesc:k];
				*desc_str++ = myField->abbrev[0];
				*desc_str++ = myField->abbrev[1];
				*desc_str++ = ',';
				tmp = tmp_str;
				strcpy(tmp_str,"");
				while(*mybuffer != delim && *mybuffer != '\0')
					*tmp++ = *mybuffer++;
				*tmp = '\0';
				mybuffer--;
			switch (myField->kind)	{
				case	'I':
					*desc_str++ = 'I';
					*desc_str++ = ',';
					ii = atoi(tmp_str);
					memcpy(data_str,&ii,sizeof(ii));
					for(ii=0;ii<sizeof(ii);ii++)
						data_str++;
					break;
				case	'L':
					*desc_str++ = 'L';
					*desc_str++ = ',';
					ll = atol(tmp_str);
					memcpy(data_str,&ll,sizeof(ll));
					for(ii=0;ii<sizeof(ll);ii++)
						data_str++;
					break;
				case	'A':
					i = strlen(tmp_str) + 1;
					if ( (i/100) > 0) *desc_str++ = (i/100) + 48;
					if ((i-i/100)/10 > 0) *desc_str++ = mod(i,100)/10 + 48;
					*desc_str++ = mod(i,10) + 48;
					*desc_str++ = ',';
					memcpy(data_str,tmp_str,i);
					for(ii=0;ii<i;ii++)
						data_str++;
					break;
			}
		}
	*desc_str = '\0';
	*data_str = '\0';
	desc_str = hold_desc;
	data_str = hold_data;
	return(TRUE);
}

- (int *)andIsns
{
/* AND */
	int *last, *isns, *new;
	
	Isns = [self pushList:min(*Last,*Isns)];
	isns = Last;
	last = Hold;
	new = Isns;
	*new = 0;
	++last;	++isns; ++new;
	
	while(1)
		if ( *last < *isns )
   		last++;
    	else if ( *last > *isns )
        		isns++;
        		else    /* *last == *isns */
        		{
            			*new++ = *last++;  isns++; (*Isns)++;
            			if ( *(new-1) == LASTISN ) {
					if(*Isns > 0) (*Isns)--;
						else	new[1] = LASTISN;
					return Isns;
					}
        		}
}

- (int *)orIsns
{
/* OR */
	int *last, *isns, *new;
	
	Isns = [self pushList:*Last+*Isns];
	new = Isns;
	*new = 0;
	last = Hold;
	isns = Last;
	++last;	++isns; ++new;
	
	while(1)
		if ( *last < *isns )
   		{
			*new++ = *last++;	(*Isns)++;
		}
    	else if ( *last > *isns )
        		{
					*new++ = *isns++; (*Isns)++;
				}
        		else    /* *last == *isns */
        		{
            			*new++ = *last++;  isns++; (*Isns)++;
            			if ( *(new-1) == LASTISN ) {
					(*Isns)--;
					return Isns;
					}
        		}
}

- (int *)xorIsns
{
/* XOR */
	int *last, *isns, *new;
	
	Isns = [self pushList:*Last+*Isns];
	new = Isns;
	*new = 0;
	last = Hold;
	isns = Last;
	++last;	++isns; ++new;
	
	while(1)
		if ( *last < *isns )
   		{
			*new++ = *last++;	(*Isns)++;
		}
    	else if ( *last > *isns )
        		{
					*new++ = *isns++; (*Isns)++;
				}
        		else    /* *last == *isns */
        		{
            			last++;  isns++;
            			if ( *(last-1) == LASTISN ) {
					if(*Isns > 0) *new = LASTISN;
						else	new[1] = LASTISN;
					return Isns;
					}
        		}
}

- (int *)notIsns
{
	/*	set Last = all and xor */
	[self getIsnListforField:"*" value:"ALL"];
	return [self xorIsns];
}

- (int *)getIsnList
{
	return Isns;
}

- (int *)getLastList
{
	return Last;
}

- (int)getListCount
{
	return *Isns;
}

int	max(i,j)
	int i,j;
{
	if ( i  >=  j) return (i);
	else return(j);
}

int mod(m,n)
	int	m,n;
{	
	
	if (n == 0)	{
		printf("Error: mod...modulus of 0 undefined\n");
		exit(0);
		}
	
	return( m - (m/n)*n );
	
}

int min(i,j)
	int	i,j;
{
	if ( i <= j ) return(i);
		else return(j);
}
@end
