/* minit Minix INITializer . Freely distributable Minix filesys creator.
 * Copyright S N Henson 1991,1992,1993.
 * Use entirely at your own risk ! If it trashes your hard-drive then
 * it isn't my fault ! 
 */

/* Version 0.27 */


#include <mintbind.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <alloc.h>

#include "minixfs/hdio.h"
#include "minixfs/xhdi.h"
#include "minixfs/pun.h"

#define MNAME_MAX 14

int shift,drive=-1,no_phys;
long numblocks,numinodes,incr=1;

/* various flags */
char protect,sonly,v2,lrecno,tst,no_size;

/* tosfs restore flags */
char restore_tosfs;

struct hdinfo hdinf,hdinf2;

unsigned char block_buf[1024];

/* Structures we will need */

typedef struct  {
  unsigned short  s_ninodes;	/* # usable inodes on the minor device */
  unsigned short  s_nzones;	/* total device size, including bit maps etc */
  unsigned short s_imap_blks;	/* # of blocks used by inode bit map */
  unsigned short s_zmap_blks;	/* # of blocks used by zone bit map */
  unsigned short s_firstdatazn;	/* number of first data zone */
  short int s_log_zsize;	/* log2 of blocks/zone */
  unsigned long s_max_size;	/* maximum file size on this device */
  short s_magic;		/* magic number to recognize super-blocks */
  short pad;			/* padding */
  long s_zones;			/* equivalent to 's_nzones' for V2 */
} super_block;

super_block *sblk=(super_block *)block_buf;

/* This structure is used to restore the original TOS filesystem */

struct tfsav {
	unsigned char bstart[64]; /* Start of bootsector */
#define SAV_MAGIC 0xcaba1372l
	unsigned sav_datrec;	  /* First data record */
	unsigned long sav_magic;  /* Contains SAV_MAGIC if info valid */
} *sav_tosfs=(struct tfsav *)(block_buf+256),sav_tmp;

typedef struct {		/* directory entry */
  unsigned short d_inum;	/* inode number */
  char d_name[MNAME_MAX];		/* character string */
} dir_struct;

typedef struct {		/* disk inode. */
  unsigned short i_mode;		/* file type, protection, etc. */
  unsigned short i_uid;			/* user id of the file's owner */
  unsigned long  i_size;		/* current file size in bytes */
  unsigned long  i_mtime;		/* when was file data last changed */
  unsigned char i_gid;			/* group number */
  unsigned char i_nlinks;		/* how many links to this file */
  unsigned short i_zone[9];	/* block nums for direct, ind, and dbl ind */
} d_inode;

typedef struct {
unsigned short i_mode;
unsigned short i_nlinks;
unsigned short i_uid;
unsigned short i_gid;
unsigned long i_size;
unsigned long i_atime;
unsigned long i_mtime;
unsigned long i_ctime;
long i_zone[10];
} d_inode2;

_BPB *bpb;

/* prototypes */

#ifdef __STDC__
# define	P(s) s
#else
# define P(s) ()
#endif

/* Useful macro: DIV, divide a by b rounded up to nearest integer */
#define DIV(a,b) ((a+b-1)/b)

void main P((int argc , char **argv ));
int nrwabs P((int rw , void *buf , unsigned count , long recno , int drive ));
void get_block P((long num ));
void put_block P((long num ));
void check_lrecno P((void ));
void warn P((void ));
void make_tosfs P((void ));

#undef P

void main(argc,argv)
int argc;
char **argv;
{
	extern int optind,opterr;
	extern char *optarg;
	extern int __mint;
	long hderr;
	int c ;
	static char err=0;
	int i,j;
	unsigned short ioff,zone1;
        super_block csblk;
        d_inode *rip=(d_inode *)block_buf;
	d_inode2 *ripn=(d_inode2 *)block_buf;
        dir_struct *dir=(dir_struct *)block_buf;
	unsigned short *srt=(unsigned short *)block_buf;
	long icount, zcount;

	struct
	{
		long start;
		long finish;
		char shadow;
		char scsiz;
	} pp;

	if(__mint==0)
	{
		fprintf(stderr,"Error: MiNT not active.\n");
		exit(1);
	}

	/* Parse command-line options */
	opterr=0;
	while((c=getopt(argc,argv,"b:B:i:I:n:pPSZztVeRr"))!=EOF)
	{
        	switch(c){

        		case 'B':
			case 'b':
			numblocks=atol(optarg);
			break;

			case 'n':
			incr=atol(optarg);
			break;

			case 'i':
			case 'I':
			numinodes=atol(optarg);
			break;

			case 'P':
			protect=1;
			break;

			case 'p':
			protect=2;
			break;

			case 'S':
			sonly=1;
			break;

			case 'Z':
			protect=1;
			break;

			case 'z':
			protect=2;
			break;

			case 'V':
			v2=1;
			break;

			case 't':
			tst=1;
			break;

			case 'e':
			no_size=1;
			break;			

			case 'R':
			case 'r':
			restore_tosfs=1;
			break;

			case '?':
			err=1;
			break;
		}
	}

	if(argc-optind!=1 || err)
	{
		fprintf(stderr,"Minix-compatible filesystem initializer\n");
		fprintf(stderr,"Copyright S N Henson 1991,1992,1993,1994\n");
		fprintf(stderr,"Version 0.27\n");
		fprintf(stderr,"Usage\t(auto)\t: minit drive\n");
		fprintf(stderr,"\t(manual): minit -b blocks -i inodes drive\n");
		fprintf(stderr,"Also :\t-S only write out super-block\n");
		fprintf(stderr,"\t-P/-Z protect filesystem with null disk\n");
		fprintf(stderr,"\t-p/-z make null disk of existing filestystem\n");
		fprintf(stderr,"\t-V make a V2 filesystem\n");
		fprintf(stderr,"\t-n dirincrement\n");
		fprintf(stderr,"\t-t test mode (no writing)\n");
		fprintf(stderr,"\t-e find size by reading past partition end\n");
		fprintf(stderr,"\t-r restore a tos filesystem to partition\n");
		exit(1);
	}
	drive=(argv[optind][0] & ~32)-'A' ;

	bpb=Getbpb(drive);

	if(restore_tosfs) make_tosfs();

	if(tst)
	{
		if(bpb)
		{
			printf("Sector size %d\n",bpb->recsiz);
			printf("Cluster size in sectors: %d\n",bpb->clsiz);
			printf("Cluster size in bytes: %d\n",bpb->clsizb);
			printf("Root dir length %d\n",bpb->rdlen);
			printf("Fat size %d\n",bpb->fsiz);
			printf("Second Fat start %d\n",bpb->fatrec);
			printf("First data record %d\n",bpb->datrec);
			printf("Flags %d\n",bpb->bflags);
			printf("Total Clusters %d\n",bpb->numcl);
		}
		else printf("Invalid BPB\n");
	}

	/* Sanity checking time */

	if((incr < 1) || (incr > 16) || ( (incr) & (incr-1) ) )
	{
		fprintf(stderr,"Dirincrement must be a power of two between\n");
		fprintf(stderr,"1 and 16 (inclusive)\n");
		exit(1);
	}

	if( (numinodes < 0) || (numinodes > 65535) )
	{
		fprintf(stderr,"Need at least 1 and no more than 65535 inodes\n");
		exit(1);
	}

	if(protect==2 && bpb->recsiz!=512)
	{
		fprintf(stderr,"Sorry can't add protection to this filesytem.\n");
		fprintf(stderr,"Sector size not 512 bytes.\n");
		exit(1);
	}

	/* Test for physical partition */
	if(!Dcntl(0x109,argv[optind],&pp) && pp.start!=-1) 
	{
		long tstack;
		hdinf.start=pp.start;
		hdinf.size=pp.finish-hdinf.start+1;
		hdinf.scsiz=pp.scsiz;
		hdinf.major = pp.shadow;
		hdinf.drive = drive;
		tstack=Super(0l);
		if(init_icd()==2)
		{
			Super(tstack);
			fprintf(stderr,"Can't kludge ICD bug\n");
			exit(1);
		}
		Super(tstack);
		hdinf.rwmode = RW_PHYS;
		if(hdinf.start > 0xfffe)
		{
			if(no_plrecno(hdinf.major))
			fprintf(stderr,"Cannot access Drive %c:\n",drive+'A');
			hdinf.rwmode |= RW_LRECNO;
		}
	}
	else 
	{
		pp.start=-1;
		if( (hderr=get_hddinf(drive,&hdinf,0)) )
		{
			fprintf(stderr,"Cannot access Drive %c: %s\n",drive+'A',
								hdd_err[hderr]);
			exit(1);
		}
	}
	if(tst)
	{
		unsigned int llrecno,xhret;
		no_phys=0;
		if(pp.start==-1)
		{
			llrecno=no_lrecno(drive);
			if(llrecno && llrecno!=3)
				fprintf(stderr,"Logical lrecno error\n");
			else fprintf(stderr,"Logical lrecno OK\n");
		}
		else
		{
			fprintf(stderr,"Physical Partition\n");
			hdinf2=hdinf;
		}
		if( (no_phys=get_hddinf(drive,&hdinf2,1) ) )
	fprintf(stderr,"Can't get physical drive info: %s\n",hdd_err[no_phys]);
		else if( (hdinf2.rwmode & RW_MODE) != RW_XHDI )
		{
		    llrecno=no_plrecno(hdinf2.major);
		    if(llrecno && llrecno!=3)
				fprintf(stderr,"Physical lrecno error\n");
			else fprintf(stderr,"Physical lrecno OK\n");
		}
		if( (xhret=XHGetVersion()) ) fprintf(stderr,"XHDI supported"
				   " version %x.%02x\n", xhret>>8,xhret & 0xff);

		else fprintf(stderr,"XHDI not supported\n");
		if(!no_phys)
		{
		  fprintf(stderr,"Partition start: %ld\n",hdinf2.start);
		  if(hdinf2.size)
			fprintf(stderr,"Partition size: %ld\n",hdinf2.size);
		  if( (hdinf2.rwmode & RW_MODE) == RW_PHYS)
		  {
			unsigned ddev;
			char *sbus;
			ddev=hdinf2.minor;
			fprintf(stderr,"Unit number %d\n",ddev);
			if( ddev & PUN_IDE) sbus="IDE";
			else
			{
				if( ddev & PUN_SCSI) sbus="SCSI";
				else sbus="ACSI";
			}
			fprintf(stderr,"%s bus: device %d.(",sbus,
							       ddev & PUN_UNIT);
			if(ddev & PUN_REMOVABLE) fprintf(stderr,"Removable)\n");
			else fprintf(stderr,"Non-removable)\n");
		  }
		  else fprintf(stderr,"Major number %d Minor number %d\n",
						     hdinf2.major,hdinf2.minor);
		}

	}

	if(!numblocks) numblocks=hdinf.size>>hdinf.scsiz;
	else if(hdinf.size && (numblocks > hdinf.size>>hdinf.scsiz))
	{
		fprintf(stderr,"Partition has only %ld blocks\n",
						       hdinf.size>>hdinf.scsiz);
		exit(1);
	}

	if(!numblocks && no_size)
	{
		if( ( hderr=get_size(drive,&numblocks) ) )
			fprintf(stderr,"get_size Warning: %s\n",size_err[hderr]);
	}

	/* read in boot sector */
	get_block(0);

	/* Fill in TOS save info */
	bcopy(block_buf,sav_tmp.bstart,64l);
	sav_tmp.sav_magic=SAV_MAGIC;
	if(bpb) sav_tmp.sav_datrec=bpb->datrec;
	else sav_tmp.sav_datrec=0;

	/* If size of partition not known try BPB/boot sector */
	if(!numblocks)
	{
		if(!bpb || (bpb->recsiz & 511))
		{
			fprintf(stderr,"Can't figure out partition size: "
							 "try the -b option\n");
			exit(1);
		}

		numblocks=( (block_buf[19]+( ((long)block_buf[20])<<8))*
							       bpb->recsiz)>>10;
		if(numblocks < 40 )
		{
	     		fprintf(stderr,"%ld blocks ? Is that bootsector OK ?\n",numblocks);
	     		exit(1);
		}
	}

	if(!v2 && (numblocks > 65535) )
	{
		fprintf(stderr,"V1 filesystems can be at most 65535 blocks\n");
		exit(1);
	}

	if( (hderr=set_lrecno(&hdinf,numblocks)) )
	{
		fprintf(stderr,"Can't access partition: %s\n",hdd_err[hderr]);
		exit(1);
	}

	if(tst)
	{
		fprintf(stderr,"\nHard disk access information:\nAccess mode: ");
		switch(hdinf.rwmode & RW_MODE)
		{
		  case RW_NORMAL:
		  fprintf(stderr,"Normal mode\n");
		  break;

		  case RW_XHDI:
		  fprintf(stderr,"XHDI mode\n");
		  break;

		  case RW_PHYS:
		  fprintf(stderr,"Physical mode\n");
		  break;
		}

		if(hdinf.rwmode & RW_LRECNO)
					    fprintf(stderr,"Lrecno access\n\n");
		else fprintf(stderr,"Non lrecno access\n\n");
	}

	/* Special test to check PHYS mode is OK:
	 * read in block 0 with Rwabs and our routines. If they are
	 * different then signal error and halt.
	 */

	if( ( ( (hdinf.rwmode & RW_MODE) == RW_PHYS ) || ( tst && !no_phys ) )
									&& bpb )
	{
		char *tmp1,*tmp2;

		tmp1=malloc(1024);
		tmp2=malloc(bpb->recsiz);
		if(!tmp1 || !tmp2)
		{
			fprintf(stderr,"Memory allocation error\n");
			exit(1);
		}

		if( Rwabs(2,tmp2,1,0,hdinf.drive) )
		{
			fprintf(stderr,"Rwabs failure\n");
			exit(1);
		}

		if(tst) block_rwabs(2,tmp1,1,0,&hdinf2);
		else block_rwabs(2,tmp1,1,0,&hdinf);
		
		if( bcmp(tmp1,tmp2,512) )
		{
			fprintf(stderr,"Physical Comparison error!\n");
			if(!tst) exit(1);
		}
		else if(tst) fprintf(stderr,"Physical Comparison OK\n");
		free(tmp1);
		free(tmp2);
	}

	if(numinodes==0)
	{
		numinodes = numblocks/3;
	
		/* Round up inode number to nearest block */

		if(v2) numinodes = (numinodes + 15) & ~15;

		else numinodes=(numinodes + 31 ) & ~31;
	}

	if(numinodes > 65535) numinodes = 65535;

	if(protect==2)
	{
		get_block(1);
		if( sblk->s_magic!=0x137f && sblk->s_magic!=0x2468 
			&& sblk->s_magic!=0x138f)

		{
			fprintf(stderr,"Fatal: bad magic number\n");
			exit(1);
		}
	}

	if(!tst) warn();

	/* OK lets work out some stuff */

	if(protect==2) get_block(1);
	else
	{
		bzero(block_buf,1024l);
		/* Super block */
        	sblk->s_ninodes=numinodes;
        	if(v2) sblk->s_zones=numblocks;
		else sblk->s_nzones=numblocks;
        	sblk->s_imap_blks=(numinodes+8192)/8192;
		sblk->s_zmap_blks=(numblocks+8191)/8192;
		sblk->s_firstdatazn=2+sblk->s_imap_blks+sblk->s_zmap_blks
			+ ( v2 ? ((numinodes+15)/16) : ((numinodes+31)/32)) ;
		sblk->s_log_zsize=0;
		sblk->s_max_size= v2 ? 0x7fffffffl : 0x10081c00l;
		if(v2) sblk->s_magic=0x2468;
		else
		{
			if(incr==2) sblk->s_magic=0x138f;
			else sblk->s_magic=0x137f;
		}
	}

	/* If we have a valid sav_tosfs structure in the superblock leave
	 * it there. Otherwise copy our structure across.
	 */
	if(sav_tosfs->sav_magic!=SAV_MAGIC) *sav_tosfs=sav_tmp;

	/* If sector size 512 bytes handle protection now */
	if(protect)
	{
		unsigned pseudo_root,root_entries;
		/* 512 bytes pseudo root after super block */
		if(bpb->recsiz==512)
		{
			bzero(&block_buf[512],512);
			for(i=512;i<1024;i+=32)
			{
				strncpy(block_buf+i,"MINIXFS    ",11);
				block_buf[i+11]=0x08;
			}
			put_block(1);
			csblk=*sblk;
			pseudo_root=3;
		}
		else
		/* Not 512 bytes: insert pseudo root after inodes but 
	 	 * before first data zone.
	 	 */
		{
			unsigned secblocks;	/* blocks per log. sector */
 			secblocks = bpb->recsiz/1024;
			/* Round up first data zone to sector boundary + 1 */
			sblk->s_firstdatazn += 2*secblocks - 1;
			sblk->s_firstdatazn /= secblocks;
			sblk->s_firstdatazn *= secblocks;
			put_block(1);
			pseudo_root = sblk->s_firstdatazn / secblocks - 1;
			csblk=*sblk;
			/* Pseudo root directory : whole block */
			for(i=0;i<1024;i+=32)
			{
				strncpy((char *)(block_buf+i),"MINIXFS    ",11);
				block_buf[i+11]=0x8;
			}
			/* Write them all out */
			for(i = - secblocks ; i < 0 ;i++)
					     put_block(csblk.s_firstdatazn+i);
		}
		/* Set up boot sector to point to pseudo root */
		get_block(0);
		strcpy((char *)(block_buf+2),"MINIX");
		block_buf[16] = 2;	/* 2 FAT's */
		root_entries = bpb->recsiz/32; /* Root directory: 1 sector */
		block_buf[17] = root_entries & 0xff;
		block_buf[18] = root_entries >> 8;
		block_buf[22] = 1;	/* FAT = 1 sector */
		block_buf[23] = 0;
		pseudo_root-=2;
		block_buf[14] = pseudo_root & 0xff;
		block_buf[15] = pseudo_root >> 8;
		put_block(0);
	}
	else
	{
		put_block(1);
		csblk=*sblk;
	}

	if( sonly || protect==2 ) exit(0);

	ioff=2+csblk.s_imap_blks+csblk.s_zmap_blks;
	zone1=csblk.s_firstdatazn;

	bzero(block_buf,1024l);

	/* Inode bitmaps */

	icount = numinodes + 1;
	for(i=2;i<2+csblk.s_imap_blks;i++)
	{
		if(i==2)
		{
			srt[0]=3;
		}
		if(icount < 8192) /* Need to mark dead inodes as used */
		{
			if(icount & 15)
			{
				srt[icount/16] = 0xffff << (icount & 15);
				icount+= 16 - (icount & 15);
			}
			for(j=icount/16;j<512;j++)srt[j]=0xffff;
		}
		put_block(i);
		if(i==2)srt[0]=0;
		icount-=8192;
	}

	bzero(block_buf,1024l);

        /* Zone bitmaps */

	zcount = numblocks + 1 - csblk.s_firstdatazn;
	for(i=2+csblk.s_imap_blks;i<ioff;i++)
	{
		if(i==2+csblk.s_imap_blks)
		{
			srt[0]=3;
		}
		if (zcount < 8192) /* Need to mark dead zones as used */
		{
			if(zcount < 0) zcount=0;
			if(zcount & 15)
			{
				srt[zcount/16] = 0xffff << (zcount & 15);
				zcount+= 16 - (zcount & 15);
			}
			for(j=zcount/16;j<512;j++)srt[j]=0xffff;
		}
		put_block(i);
		if(i==2+csblk.s_imap_blks)srt[0]=0;
		zcount-=8192;
	}

	bzero(block_buf,1024l);

	/* Initialise inodes */
	
	for(i=ioff;i<ioff+
			(v2 ? ((numinodes +15)/16) : ((numinodes+31)/32 )) ;i++)
	{
		if(i==ioff) /* Root inode , initialise it properly */
		{
			if(v2)
			{
				ripn->i_mode=040777; /* Directory */
				ripn->i_size=32*incr;
				ripn->i_mtime=time((time_t *)0);
				ripn->i_ctime=ripn->i_mtime;
				ripn->i_atime=ripn->i_mtime;
				ripn->i_nlinks=2;
				ripn->i_zone[0]=zone1;
			}
			else
			{
				rip->i_mode=040777; /* Directory */
				rip->i_size=32*incr;
				rip->i_mtime=time((time_t *)0);
				rip->i_nlinks=2;
				rip->i_zone[0]=zone1;
			}
		}	
		put_block(i);
		if(i==ioff)bzero(block_buf,1024l);
	}

	bzero(block_buf,1024l);
	/* And finally the root directory */
	dir[0].d_inum=1;
	strcpy(dir[0].d_name,".");
	dir[incr].d_inum=1;
	strcpy(dir[incr].d_name,"..");
	put_block(zone1);
	if(!tst) fprintf(stderr,"Initialised OK.\n");
	fprintf(stderr,"%ld Blocks, %ld Inodes.\n",numblocks,numinodes);

	/* Force media change on specified drive */
	if(Dlock(1,drive)) 
		fprintf(stderr,"Can't Lock Drive, Reboot to be sure\n");

	exit(0);
}

void get_block(num)
long num;
{
	long r;

	if( (r = block_rwabs(2,block_buf,1,num,&hdinf)) )
	{
		fprintf(stderr,"Fatal Read Error %ld block %ld\n",r,num);
		exit(2);
	}
}

void put_block(num)
long num;
{
	long r;

	if(tst) return;

	if( (r = block_rwabs(3,block_buf,1,num,&hdinf)) )
	{
		fprintf(stderr,"Fatal Write Error %ld block %ld\n",r,num);
		exit(2);
	}
}

void warn()
{
	int ch;
	fprintf(stderr,"WARNING ! THIS %s TOTALLY DESTROY ANY DATA ON ",
			      (sonly || protect==2 ) ? "MAY":"WILL");
	fprintf(stderr,"DRIVE %c !\n",drive+'A');
	fprintf(stderr,"Are you ABSOLUTELY SURE you want to do this (y/n)?\n");
	ch=Crawcin() & 0xff ;
	if((ch & ~32) !='Y')
	{
		fprintf(stderr,"Aborted\n");
		exit(0);
	}
}

/* Restore a tosfs filesystem. Using the tfsav structure.
 */

void make_tosfs()
{
	int hderr,i;
	long clear_blk;		/* Number of blocks to clear */
	char ch;

	if( (hderr=get_hddinf(drive,&hdinf,0)) )
	{
		fprintf(stderr,"Cannot access Drive %c: %s\n",drive+'A',
								hdd_err[hderr]);
		exit(1);
	}

	get_block(1);
	if(sblk->s_magic!=0x2468 && sblk->s_magic!=0x137f && 
		sblk->s_magic!=0x138f )
	{
		fprintf(stderr,"Can't restore: not a Minixfs filesystem\n");	
		exit(1);
	}

	if( sav_tosfs->sav_magic!=SAV_MAGIC )
	{
		fprintf(stderr,"Can't recreate filesystem: perhaps you made the\
\nfilesystem with an older version of minit?\n");
		exit(1);
	}

	fprintf(stderr,"WARNING: You are about to Restore a TOS filesystem\n");
	fprintf(stderr,"on a Minixfs Filesystem. This will TOTALLY DESTROY\n");
	fprintf(stderr,"the Minixfs files!\n");
	fprintf(stderr,"ARE YOU ABSOLUTELY SURE YOU WANT TO DO THIS ? (y/n)\n");
	ch=Crawcin() & 0xff;
	if((ch & ~32) != 'Y') exit(0);


	sav_tmp=*sav_tosfs;
	get_block(0);
	bcopy(sav_tmp.bstart,block_buf,64l);

	if(sav_tmp.sav_datrec) clear_blk = (sav_tmp.sav_datrec*block_buf[12])/2;
	else /* Work out datrec from other parameters */
	{
		int nphys;	/* 512 byte sectors per logical sector */
		long dirent;	/* root dir logical sectors */
		nphys = block_buf[12]/2;
		clear_blk = nphys;	/* Boot sector */

		/* Add the FATs */
		clear_blk += nphys*block_buf[16]*
				(block_buf[22]+(block_buf[23]<<8));

		/* Root dir 512 byte sectors (16 entries per sector) */
		dirent = DIV(block_buf[17]+(block_buf[18]<<8),16);		
		/* Now round up to nearest physical sector */
		dirent = DIV(dirent,nphys);
		clear_blk += nphys*dirent;
	}

	/* convert clear_blk to blocks */
	clear_blk = DIV(clear_blk,2);

	bzero(block_buf+512,512);
	put_block(0);

	/* Now we zero 2 FATs and root directory */
	bzero(block_buf,512);

	for(i=1;i < 1+clear_blk; i++) put_block(i);

	/* Force disk change */
	(void)Dlock(1,drive);

	fprintf(stderr,"TOS filesystem recreated: Reboot to be sure\n");
	exit(0);

}
