// IEMappedFile.m by Monty Zukowski 1/21/93 version 1.0
//Copyright 1993 Intuitive Edge
//You are free to use and modify this as long as you acknowledge me in your 
//program somewhere

#import"IEMappedFile.h"
#import<objc/HashTable.h>
#import<objc/hashtable.h>
#import <libc.h>

@implementation IEMappedFile

static HashTable *hash=nil;

+_removeFromHashTable:(IEMappedFile *)obj
{
	[hash removeKey:[obj name]];
	return self;
}

+_addName:(NXAtom) theName andObject:obj
{//returns nil if name is already in the table, otherwise
	// it puts name and obj in the table and returns self.
	if (!hash)
		hash=[[HashTable alloc] initKeyDesc:"%" valueDesc:"@"];
	if (![hash valueForKey:theName])
	{
		[hash insertKey: theName value:obj];
		return self;
	}
	else
		return nil;
}

+(HashTable *) instances	{	return hash;	}

+newForFilename:(const char *) filename
{
	NXAtom theAtom=NXUniqueString(filename);
	IEMappedFile *instance;
	
	if (!hash)
		hash=[[HashTable alloc] initKeyDesc:"%" valueDesc:"@"];
	if (instance=[hash valueForKey:theAtom])
		return instance;
	else
	{
		instance=[super new];
		instance->name=theAtom;
		if ([instance _initFromName])
		{
			[hash insertKey:theAtom value:instance];
			return instance;
		}
		else
		{
			[instance free];
			return nil;
		}
	}
}

-free
{
	[IEMappedFile _removeFromHashTable:self];
	if (fd>=0)
		close(fd);
	return [super free];
}

-(const void *) data	{	return data;	}

-(const void *) dataUpdated
{
	struct stat sbuf;
	
	if (fd>=0)
	{
		if (fstat(fd,&sbuf)!=0)  //!=0 means fstat failed
		{
			[self _inactivate];
		}
		else
			if (sbuf.st_mtime!=lastModifyTime)
			{
				[self _initFromName];
			}
	}
	else 
	{	//fd was invalid, see if it is fixed now
		[self _initFromName];
	}
	return data;
}

-(int) fd	{	return fd;	}

-(int) size	{	return size;	}

-(NXAtom) name	{	return name;	}

-(time_t) lastModifyTime	{	return lastModifyTime;	}


-write:(NXTypedStream *) typedStream
{
	[super write:typedStream];
	NXWriteType(typedStream,"%",&name);
	return self;
}

-read:(NXTypedStream *) typedStream
{
	NXReadType(typedStream,"%",&name);
	[self _initFromName];
	return self;
}

-finishUnarchiving
{
/* This is needed to guarantee that if an IEMappedFile object gets archived
	more than once, on unarchiving there will still only be one object per
	filename.
*/

	if ([IEMappedFile _addName:name andObject:self])
		return nil;//returning nil means ok to use self, see Object docs
	else
	{//dont use [self free] because it would remove the name from the hashtable
		NXAtom theName=[self name];
		if (fd>=0)
			close(fd);
		[super free];
		return [IEMappedFile newForFilename:theName];
	}
}

-_initFromName
{//returns self if successful, nil otherwise
	struct stat	sbuf;
		
	if (stat(name,&sbuf)==0)
	{
		if ((fd=open(name,O_RDONLY,0444))>=0)
		{
			if (map_fd(fd,(vm_offset_t) 0, &(vm_offset_t)data,TRUE,
					(vm_size_t) sbuf.st_size) == KERN_SUCCESS)
			{
				size=sbuf.st_size;
				lastModifyTime=sbuf.st_mtime;
			}
			else
			{	//map_fd failed
				[self _inactivate];
				return nil;
			}
		}
		else
		{	//couldn't open() name
			[self _inactivate];
			return nil;
		}
	}
	else	//couldn't stat name
	{
		[self _inactivate];
		return nil;
	}
	return self;
}
-_inactivate
{
	fd = -1;
	size = 0;
	data = NULL;
	lastModifyTime = 0;
	return self;
}
@end