/* This file is part of 'fsck' Copyright S.N. Henson */

/* Functions Common to V1 and V2 Filesystems */

#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <stdlib.h>
#include "fs.h"
#include "global.h"
#include "proto.h"

/* Parse a comma separated list of integers, return number of integers read.
 * Fill in elements in a linked list. 
 */

int comma_parse(str,list)
char *str;
llist **list;
{
	char *tstr,*fstr,*comma;
	int count;
	count=0;
	if( !(tstr=strdup(str)) ) fatal("Out of memory");
	fstr=tstr;

	do
	{
		llist *p;
		comma=strchr(tstr,',');
		if(comma) *comma=0;
		if( !(p=malloc(sizeof(list)) ) ) fatal("Out of memory");
		p->member=atol(tstr);		
		p->next=*list;
		*list=p;
		count++;
		if(comma) tstr=++comma;
	}
	while(comma);

	free(fstr);

	return count;
}

void read_zone(zone,buff)
long zone;
void *buff;
{
	read_blocks(zone,1,buff);
}

void write_zone(zone,buff)
long zone;
void *buff;
{
	if(zone==0)
	{
		printf("Illegal Write Zone\n");
		return;
	}
	write_blocks(zone,1,buff);
}

/* Check a zone number is legitimate */
int chk_range(zone)
long zone;
{
	if( (zone > maxzone) || (zone < minzone) ) return 0;
	return 1;
}


/* Bitmap stuff */

void setbit(zone,map)
long zone;
unsigned *map;
{
	map[zone>>4] |= (1<<(zone & 0xf));
}

void clrbit(zone,map)
long zone;
unsigned *map;
{
	map[zone>>4] &= ~(1<<(zone & 0xf));
}

int isset(zone,map)
long zone;
unsigned *map;
{
	return( ( map[zone>>4] & ( 1<<(zone & 0xf) ) ) ? 1:0 );
}

/* Find the first Zero bit in bitmap */
long findbit(map,limit)
unsigned *map;
long limit;
{
	long ret;
	long i;
	int j;

	for(i=0;i<(limit+15)/16;i++)
	{
		if(map[i]!=0xffff)
		{
			for(j=0;j<16;j++) 
			{
				if(!(map[i] & (1<<j)))
				{
							
					ret=i*16+j;
					if(ret < limit)
					{
						setbit(ret,map);
						return ret;
					}
					return 0;
				}
			}
		}
	}
	return 0;
}

long alloc_zone()
{
	long ret;
	ret=findbit(szbitmap,maxzone-minzone);
	if(!ret) return 0;
	ret+=minzone-1;
	return ret;
}

int mark_zone(zone)
long zone;
{
	unsigned ret;
	zone-=minzone-1;
	ret= isset(zone,szbitmap);
	setbit(zone,szbitmap);
	return ret;
}

void unmark_zone(zone)
long zone;
{
	zone-=minzone-1;
	clrbit(zone,szbitmap);
}

int ask(str,alt)
char *str;
char *alt;
{
	char ans[20];
	if(alln) return 0;
	if(ally)
	{
		printf("(%s)",alt);
		return 1;
	}
	printf("%s",str);
	fgets(ans,20,stdin);
	if( (ans[0] & ~32) == 'Y') return 1;
	return 0;
}

void fatal(str)
char *str;
{
	printf("Fatal error: %s\n",str);
	close_device();
	exit(1);
}

void sfatal(str)
char *str;
{
	printf("Bad Superblock: %s\n",str);
	close_device();
	exit(1);
}


void usage()
{
	printf("Usage : fsck device\n");
	printf("Filesystem consistency checker. Copyright S.N. "
	"Henson 1992,1993,1994.\nAll Rights Reserved\n");
	printf("Version 0.1\n");
	exit(1);
}

/* Read in superblock, perform some sanity checks on it, then read in the 
 * bitmaps.
 */

void read_tables()
{
	long expect;
	Super=(super_block *)malloc(BLOCKSIZE);
	read_zone(1,Super);
	/* Sanity checks on super block */
	if(Super->s_magic!=SUPER_V1 && Super->s_magic!=SUPER_V1_30)
	{

		if(Super->s_magic==SUPER_V2) 
		{
			version=1;
			maxzone=Super->s_zones;
		}
		else sfatal("Invalid Magic Number");
	}
	else maxzone=Super->s_nzones;

	if(set_size(maxzone)) fatal("Cannot access filesystem");

	expect=(Super->s_ninodes+8192l)/8192;

	if( expect > Super->s_imap_blks)
		sfatal("Inode Bitmap too Small");
	if( expect < Super->s_imap_blks) fprintf(stderr,"Warning: inode bitmap"
 			"%d blocks, expected %ld\n",Super->s_imap_blks,expect);
	expect=(maxzone+8191)/8192;
	if(expect > Super->s_zmap_blks )
		sfatal("Zone Bitmap too Small");
	if(expect < Super->s_zmap_blks ) fprintf(stderr,"Warning: zone bitmap"
			"%d blocks, expected %ld\n",Super->s_zmap_blks,expect);
	/* Set up some variables */

	maxino=Super->s_ninodes;
	minzone=Super->s_firstdatazn;
	maxzone--;

}

int do_trunc()
{
	printf("Inode %ld Contains Too Many Zones\n",cino);
	done_trunc=1;
	if(ask("Truncate?","Truncated"))
	{
		trunc=1;
		return 1;
	}
	return 0;
}


int chk_irange(inum)
unsigned inum;
{
	if(inum && inum <=maxino) return 1;
	return 0;
}


void inerr(name)
char *name;
{
	printf("Inode %ld: %s\n",cino,name);
}

int badname(name)
char *name;
{
	unsigned short n = MMAX_FNAME(incr);
	if(!*name) return 1;
	while(n-- && *name ) if( *name++ == '/' ) return 1;
	return 0;
}

/* Copy a directory entry */
void cpdir(dest,src)
dir_struct *dest,*src;
{
	bcopy((char*)src,(char *)dest,DSIZE*incr);
}

/* Show FS info */
void showinfo()
{
	if(info)
	{
		printf("\nDrive %s\n",drvnam);
		printf("V%d Filesystem\n",version+1);
		printf("Directory Increment %d\n",incr);
		printf("%ld\t\tInodes\n",maxino);
		printf("%ld\t\tFree Inodes\n",ifree);
		printf("%ld\t\tData Zones\n",maxzone-minzone+1);
		printf("%ld\t\tFree Data Zones\n",zfree);
		printf("%ld\t\tDirectories\n",ndir);
		printf("%ld\t\tRegular Files\n",nreg);
		printf("%ld\t\tSymbolic Links\n\n",nsym);
		printf("%ld\t\tFIFO's\n",nfifo);
		printf("%ld\t\tCharacter Special Files\n",nchr);
		printf("%ld\t\tBlock Special Files\n",nblk);
	}

	if(info>1)
	{
		printf("First Inode Block  %ld\n",ioff);
		printf("First Datazone     %ld\n\n",minzone);
	}
	if(modified) 
	{
		printf("**************************************\n");
		printf("*    FILESYSTEM HAS BEEN MODIFIED    *\n");
		printf("**************************************\n\n");
	}
}
