#include <sys/types.h>
#include <sys/param.h>
#include <sys/filsys.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/dir.h>


short	ifd, ofd;
short	vflg = 0;
short	badflag = 0;
short	lastflg = 0;
unsigned	bs=100;
unsigned	obs, lbs;
long	cnt = 0L;
long	curblk;
long	nxtblk;
long	badlist[NADDR];
long	atol();
char	*malloc();

main(argc, argv)
char **argv;
{
	register short i;
	register char *bp;
	register long *pl;
	int j;
	long	tmp;

	argv++;
	while(argc > 3 && **argv == '-'){
		argc--;
		switch(*++*argv){
	case 'v':
		vflg++;
		break;

	case 'B':
		badflag++;
		break;

	case 'b':
		bs = atoi(*++argv);
		argc--;
		break;

	case 'c':
		cnt = atol(*++argv);
		argc--;
		break;
		}
		argv++;
	}
	if(argc != 3){
		printf("Usage: dcopy filesys1 filesys2\n");
		exit(1);
	}
	if((ifd = open(*argv, 0)) == -1){
		printf("Can't open input file system %s\n", *argv);
		exit(2);
	}

	if((ofd = open(*++argv, 2)) == -1){
		printf("Can't open output file system %d\n", *argv);
		exit(3);
	}

	lbs = bs;
	obs = (bs <<= 9);
	if((bp = malloc(bs)) < 1){
		printf("No available memory\n");
		exit(4);
	}

	if(vflg)
		printf("Transfer buffer = %u bytes\n", bs);
	if(!cnt){
		lseek(ifd, 512L, 0);
		if(read(ifd, bp, sizeof (struct filsys)) != sizeof (struct filsys)){
			printf("Can't read super block of input file\n");
			exit(4);
		}
		cnt = ((struct filsys *)bp)->s_fsize;
	}

	curblk = 0L;
	nxtblk = 0L;
	pl = badlist;
	if(!badflag){
		badblocks();
		for(i = 0; badlist[i]; i++){
			for(j = i+1; badlist[j]; j++)
				if(badlist[i] > badlist[j]){
					tmp = badlist[j];
					badlist[j] = badlist[i];
					badlist[i] = tmp;
				}
		}
	}
	while(1){
		lseek(ifd, curblk << 9, 0);
		lseek(ofd, curblk << 9, 0);
printf("%D->%D  (%D)\n", curblk, curblk+lbs, *pl);
		if(*pl >= curblk && *pl < curblk + (lbs)){
			nxtblk = curblk + lbs;
			bs = 512;
			break;
		}
		if(*pl == curblk){
			pl++;
			printf("Bad block %D (from bad list) -- padding with nulls\n", curblk);
			goto errfix;
		}
		if(read(ifd, bp, bs) != bs){
			if(bs != 512){
				nxtblk = curblk + lbs;
				bs = 512;
				continue;
			}
			printf("Bad block %D -- will use zeros\n", curblk);
errfix:
			nxtblk = 0L;
			bs = obs;
			for(i = 0; i < 512; i++)
				bp[i] = 0;
			lseek(ofd, curblk << 9, 0);
/*
			write(ofd, bp, 512);
*/
			curblk++;
			continue;
/*
		} else	if(write(ofd, bp, bs) != bs){
				printf("Warning: write error block %D\n",
					curblk << 9);
			}
*/		}
		curblk += (bs >> 9);
		if(curblk == nxtblk){
			printf("Error detected somewhere after block %D but not found\n",
				nxtblk - lbs);
			bs = obs;
			nxtblk = 0L;
		}
		if(lastflg)
			break;

		if(curblk + (bs >> 9) >= cnt){
			lastflg++;
			if((bs = (cnt-curblk) << 9) == 0)
				break;
		}
	}
	close(ifd);
	close(ofd);
	exit(0);
}

static	char tbuf[BSIZE];
badblocks()
{
	register long *pl;
	register struct direct	*dp;

	getblocks(2);
	for(pl = badlist; *pl; pl++){
		readblock(*pl);
		for(dp = tbuf; dp < &tbuf[BSIZE]; dp++){
			if(dp->d_ino == 0)
				continue;
			if(strncmp(dp->d_name, ".badblocks", 10) == 0){
				getblocks(dp->d_ino);
				printf("Bad blocks that will not be copied:\n");
				for(pl = badlist; *pl; pl++){
					printf("%6D\n", *pl);
				}
				return;
			}
		}
	}
	printf("No bad blocks.\n");
}

getblocks(ino)
ino_t ino;
{
	register struct dinode *pi;
	ino--;
	readblock(2L + (long)(ino)/INOPB);
	pi = &tbuf[(ino%INOPB)*(sizeof (struct dinode))];
	l3tol(badlist, pi->di_addr, NADDR);
}

readblock(bno)
long bno;
{
	lseek(ifd, bno*512L, 0);
	if(read(ifd, tbuf, 512) != 512){
		printf("Read error on block: %D\n", bno);
		exit(4);
	}
}
