/*

	FILE.C - file creation and maintainance module for df.c

*/


#include	<exec/io.h>
#include	<exec/devices.h>
#include	<devices/trackdisk.h>
#include	<devices/bootblock.h>
#include	"df.h"


char	bb_name[] = "bad.blocks";

extern	void	df_exit();		/* exit routine			*/
extern	char	*calloc();		/* clear & alloc function	*/

extern	char	*dsk_buf;		/* buffer for disk writes	*/
extern	BYTE	write_blocks();		/* write blocks to the disk	*/

extern	struct	RootBlock	rb;	/* structure for root block	*/
struct	FileHeaderBlock		fb;	/* structure for file header	*/
extern	struct	disk_parm	dp;	/* structure for disk_parms	*/
struct	extension_block		*extb;	/* ptr to extension block list	*/
struct	bitmap_block		*bmb;	/* ptr to bitmap block list	*/
ULONG	next_avail_block;		/* next block to write to	*/
ULONG	fh_key;				/* file header key		*/

extern	struct	bad_block	*bad_block_list;
extern	int			bad_blocks;
struct	file_ext_block		*fextb; /* ptr to file ext block list	*/



/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	add_bad_block( block )
ULONG	block;
	{
	/*
	  Allocate and add the block to the bad_block_list. Make sure the
	  block is in range for this disk as well...
	*/
	struct	bad_block	*p, *q;

	if ( block > dp.upper_key )
		df_exit( EBADBLK );

	if ( !(p = (struct bad_block *) calloc( sizeof( struct bad_block ), 1 )) )
		{
		df_exit( ENOBLKS );
		}

	q = bad_block_list;
	p->key  = block;

	bad_block_list = p;
	p->next = q;
	}


/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
int	block_already_bad( block )
ULONG	block;
	{
	/*
	  Check the supplied block against the list of bad blocks
	  If it is in there, return true, else return false...
	*/
	struct	bad_block	*p;

	for( p = bad_block_list; p; p = p->next )
		{
		if ( p->key == block )
			return( 1 );
		}
	return( 0 );
	}



/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
ULONG	get_next_avail_block()
	{
	/*
	  Just set next_avail_block to its successor unless we happen
 	  to find it in the used block list. In that case, keep going
	  till we find a good/free block. 
	*/

	while ( block_already_bad( next_avail_block ) )
		{
		next_avail_block++;
		} 

	return( next_avail_block++ );
	}


/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	create_file_extension_block_list( blocks )
int	blocks;
	{
	/* 
	  Create a linked list of file extension blocks...
	*/
	int	i, blocks_left;
	struct	file_ext_block	*p, *q;

	/*
	  calculate blocks_left for filling in the HighSeq field.
	*/
	blocks_left = bad_blocks;

	for( i = 0, q = 0L; i < blocks; i++ )
		{
		blocks_left -= 72;

		if ( !(p = (struct file_ext_block *) calloc( sizeof( struct file_ext_block ), 1 ) ) )
			{
			df_exit( ENOMEM );
			}

		if ( !fextb )
			{
			fextb 	= p;
			}
		else	
			{
			q->next = p;
			}

		p->next 		= 0L;
    		p->key 			= get_next_avail_block();
		p->fhb.Type 		= 16L;
		p->fhb.OwnKey 		= p->key;
		p->fhb.SecondaryType 	= -3L;
		p->fhb.Parent  		= (q) ? q->fhb.OwnKey : fh_key;
		p->fhb.HighSeq 		= (blocks_left > 72) ? 72 : blocks_left;
		q			= p;
		}
	}



/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	get_next_block_ptr( key )
ULONG	key;
	{
	/*
	  Stick the key in the next slot we have in the header or
	  extension block, and update our next slot pointer.
	*/
	static	int	fh_ptr = 71, eblock_ptr = 71;
	static	struct	file_ext_block	*p = 0L;

	if ( !p )
		{
		fb.DataBlocks[fh_ptr--] = key;
		if ( fh_ptr < 0 )
			{
			p = fextb;
			fb.Extension = p->key;
			}
		}
	else
		{
		p->fhb.DataBlocks[eblock_ptr--] = key;
		if ( eblock_ptr < 0 )
			{
			p->fhb.Extension = p->next->key;
			p = p->next;
			eblock_ptr = 71;
			}
		}
	}



/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
int	hash_file_name( name )
char	*name;
	{
	/*
	  Hash the file name to an address suitable for the root block.
	  I got this out of a CATs newsletter on the FFS. It does not
	  work, so I fudge the hash value for bad.blocks later...
	*/
	int 	i, val;

	val = sizeof( name ) - 1;
	for( i = 0; *name; i++ )
		val = (( val * 13 ) + toupper( *name++ )) & 0x7ff;

	return( val % 72 );
	}

/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	initialize_header_block( my_key )
ULONG	my_key;
	{
	/*
	  Initialize the needed fields in the file header block prior to
	  writing it to the disk.
	*/
	int	i;
	ULONG	chksum;
	long	*p;
	
	fb.Type		= 2L;
	fb.OwnKey	= my_key;
	fb.Parent	= dp.root_key;
	fb.Protect	= 15L;

	/*
  	  If we have extension blocks, set fb.HighSeq to 72, else
	  set it to bad_blocks;
	*/
	fb.HighSeq	= (fextb) ? 72 : bad_blocks;

	fb.FirstBlock	= bad_block_list->key;
	movmem( (char *) &rb.DiskMade, (char *) &fb.Date, 12);

	fb.FileName[0] = sizeof( bb_name ) - 1;
	strcpy( &fb.FileName[1], bb_name );
	fb.SecondaryType = -3L;

	/*
	   Calculate the checksum for this block.
	*/
	for( i = 0, chksum = 0, p = (long *) &fb; i < (sizeof( fb )/4); i++ )
		{
		chksum += *p++;
		}
	fb.Checksum = -chksum;
	}


/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	update_bad_blocks()
	{
	printf( "Sorry, not implemented yet...\n" );
	}


/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	print_bad_blocks()
	{
	printf( "Sorry, not implemented yet...\n" );
	}

/*-----------------------------------------------------------------------------
------------------------------------------------------------------------------*/
void	create_bad_block_file()
	{
	/*
	  Create the header and attach the blocks to the bad.block file.
	*/
	int	ext_blocks_needed, i;
	struct	bad_block	*p;
	struct	file_ext_block	*q;
	long			chksum, *tmp;


	/*
 	  Set next_avail block to dp.lower_key, because this is where
	  we are going to write the file header.
	*/
	next_avail_block = dp.lower_key;


	/*
	  Use the first available block for the File header...
	*/
	fh_key = get_next_avail_block();


	/*
	  Calculate number of extension blocks needed for bad block file.
	  bad_blocks contains the number of blocks in the bad_block_list,
	  and since a FileHeaderBlock can only hold 72 entries, we will 
	  need bad_blocks / 72 extension blocks.
	*/
	ext_blocks_needed = (bad_blocks / 72) - 1;
	if ( (bad_blocks % 72) != 0 )
		ext_blocks_needed++;


	/*
	  Create the extension block list if we need it.
	*/
	if ( ext_blocks_needed )
		create_file_extension_block_list( ext_blocks_needed );


	/*
	  For every block in the used block list, stick its key
	  in either the fileheader or extension block.
	*/
	for( p = bad_block_list; p; p = p->next )
		{
		get_next_block_ptr( p->key );
		}


	/*
	  Initialize the rest of the fields in the FileHeader block,
	  then write this baby to the disk!
	*/
	initialize_header_block( fh_key );

	movmem( (char *) &fb, dsk_buf, sizeof( fb ) );
	if ( !write_blocks( fh_key, 1 ) )
		{
		df_exit( ENOHDR );
		}

	/*
	  Update the root block with the address of this file header
	  block. We would normally hash it, but our hash function does
	  not work. Once again, we will cheat!!!
	*/
	rb.HashTable[21] = fh_key;


	/*
	  Complete and write the file extension blocks to the disk.
	*/
	for ( q = fextb; q; q = q->next )
		{
		/*
		   Calculate the checksum for this block.
		*/
		for( i = 0, chksum = 0, tmp = (long *) &(q->fhb); i < 128; i++ )
			{
			chksum += *tmp++;
			}
		q->fhb.Checksum = -chksum;
	
		movmem( (char *) &(q->fhb), dsk_buf, 512);

		if ( !write_blocks( q->key, 1 ) )
			{
			df_exit( ENEXTB );
			}


		}
	
	}
