/*
	Mini-UNIX file I/O support code
*/

#include <stdio.h>
#include "mxfilsys.h"

int fsd = 0;				/* file system descriptor	*/
struct super *sp;			/* super block pointer	*/
struct super super;			/* super block buffer	*/

extern char dflag;			/* debug flag			*/

/*
	Routine to set up the file system super block
*/

void fsinit(fname)
char *fname;
{
	char block[BLOCK];

	sp = &super;
	super.fsize = 2;
	if((fsd = open(fname,0)) == ERR) {
		printf("Can't open file system %s\n",fname);
		exit(1);
	}
	getblock(1,block);
	_move(sizeof super,block,&super);
	if(dflag)
		printf("fsinit: file system %s has size %d\n",fname,super.fsize);
}

/*
	Routine to undo the above
*/
void fsclose()
{
	close(fsd);
}

/*
	Routine to read thru the blocks of a file.
*/
int fblock(b,ip,block)
int b;
struct inode *ip;
char block[BLOCK];
{
	short iblock[256];
	char i;
	int bnum;
	long filesize,offset;

	offset = (long)b * BLOCK;
	filesize = ((long)(ip->size0) << 16) + (long)(ip->size1);
	if(dflag)
		printf("fblock: get block %d of file.\n",b);
	if(offset > filesize) {
		if(dflag)
			printf("fblock: offset %ld greater then file size %ld\n",
				offset,filesize);
		return(emptyblock(block));
	}
	if(b < 0) {
		printf("fblock: illegal block number: %d\n",b);
		return(EOF);
	}
	if((ip->flags & 0100000) == 0) {
		printf("flbock: i-node is marked free.\n");
		return(EOF);
	}
	if(ip->flags & 020000) {
		printf("fblock: bad file type: %o\n",ip->flags);
		return(EOF);
	}
	if((ip->flags & 010000) == 0) {		/* small file */
		if(b > 7) {
			if(dflag)
				printf("flbock: outside range of small file.\n");
			return(emptyblock(block));
		}
		bnum = ip->addr[b];
		if(bnum == 0)
			return(emptyblock(block));
		getblock(bnum,block);
		return(TRUE);
	}

	/*
		Large or HUGE file
	*/
	i = b / (BLOCK/2);
	if(i < 7) {	/* large file */
		if(ip->addr[i] == 0)
			return(emptyblock(block));
		getblock(ip->addr[i],iblock);
		if(iblock[b % (BLOCK/2)] == 0)
			return(emptyblock(block));
		getblock(iblock[b % (BLOCK/2)],block);
		return(TRUE);
	}

	/*
		HUGE file
	*/
	if(ip->addr[7] == 0)
		return(emptyblock(block));
	printf("I DO NOT SUPPORT HUGE FILES YET...\n");
	return(emptyblock(block));
}

/*
	routine to return a clear empty block and EOF
*/
int emptyblock(block)
char *block;
{
	_setmem(block,BLOCK,'\0');
	return(EOF);
}

/*
	routine to get an inode given the inode number
*/
void geti(i,ip)
int i;
struct inode *ip;
{
	char block[BLOCK];

	if(dflag)
		printf("geti: read inode %d\n",i);
	if(i > sp->ninode*(BLOCK/sizeof(struct inode))) {
		printf("geti: bad inode %d.  max is %d\n",
			i,sp->ninode);
		exit(4);
	}
	getblock((i+31)/16,block);
	_move(sizeof(struct inode),block+32*((i+31)%16),ip);
}

/*
	Routine to read a block
*/
void getblock(n,block)
int n;
char *block;
{
	if(dflag)
		printf("getblock: read block %d\n",n);
	if(n > sp->fsize || n < 0) {
		printf("getblock: illegal block #: %d\n",n);
		exit(4);
	}
	if(lseek(fsd,(long)n * BLOCK,0) == ERR) {
		printf("getblock: lseek error.  block #: %d\n",n);
		exit(4);
	}
	if(read(fsd,block,BLOCK) != BLOCK) {
		printf("getblock: read error.  block #: %d\n",n);
		exit(4);
	}
}