#

/*
 * incremental dump
 * dump fisbuodh filesystem
 * f take output tape from arglist
 * i from date in /etc/dtab
 * s specify tape size in feet 
 * b specify tape size in blocks
 * u update /etc/dtab to current date
 * 0 dump from the epoch
 * d dump specified number of days
 * h dump specified number of hours
 * a on incremental dump, dump files even >= MAXSIZE
 * p old format 512 byte records
 * n new format specify blocks per record
 * q quick format 1600 bpi
 */

char	*dargv[]
{
	0,
	"i",
	"/dev/rp0",
	0
};

#include "../head/ino.h"
#include "../head/filsys.h"
#define	MAXSIZE	1000
struct filsys sblock;
struct
{
	char	name[16];
	int	date[2];
} dtab[10];
char	*dfile	"/etc/dtab";
char	*ofile  "/dev/rmt0";	/* defalt output */
char	*qfile  "/dev/rmt2";	/* 1600 bpi output */
char	*pfile  "/dev/mt0";	/* buffered output */
int	*talist;
int	fi;
int	*bigbuf;
int	bufsize;
int	inum;
int	buf[256];
int	dbuf[256];
int	ibuf[256];
int	vbuf[256];
char	*date[2];
char	*ddate[2];
char	*barg;
char	*sarg;
char	*narg;
int	fo	-1;
int	pher;
int	dflg;
int	iflg;
int	cflg;
int	aflg;
int	bflg;
int	sflg;
int	pflg;
int	nflg;
int	qflg;
int	recsiz;
char	*tsize;
char	*taddr;

main(argc, argv)
char **argv;
{
	char *key;
	int s, i, nfil, nblk, f;
	register *tap;
	register struct inode *ip;
	int ino;
	long n;

	time(date);
	if(argc == 1) {
		argv = dargv;
		for(argc = 1; dargv[argc]; argc++);
	}

	argc--;
	argv++;
	key = *argv;
	while(*key)
	switch(*key++) {

	default:
		printf("bad character in key\n");
		exit(0);

	case 'a': /* dump all (even large) */
		aflg++;
		continue;

	case '-':
		continue;

	case 'c': /* increment file name */
		cflg++;
		continue;

	case 'f': /* file name from arg list */
		argc--;
		argv++;
		qfile = *argv;
		ofile = qfile;
		continue;

	case 'i': /* date from date file */
		iflg++;
		continue;

	case 's': /* tape size */
		sarg = argv[1];
		sflg++;
		argv++;
		argc--;
		continue;

	case 'b': /* tape size */
		barg = argv[1];
		bflg++;
		argv++;
		argc--;
		continue;

	case 'u': /* rewrite date */
		dflg++;
		continue;

	case '0': /* dump all */
		ddate[0] = ddate[1] = 0;
		continue;

	case 'd': /* dump some number of days */
		i = 21600;
		goto sd;

	case 'h': /* dump some number of hours */
		i = 900;
		goto sd;

	case 'p': /* dump 512 byte records */
		pflg++;
		qfile = pfile;
		ofile = qfile;
		continue;

	case 'n': /* dump recsiz*512 byte records */
		narg = argv[1];
		nflg++;
		argv++;
		argc--;
		continue;

	case 'q': /* 1600 bpi */
		ofile = qfile;
		qflg++;
		continue;

	sd:
		ddate[0] = date[0];
		ddate[1] = date[1];
		s = number(argv[1])*4;
		argv++;
		argc--;
		while(s) {
			if(i > ddate[1])
				ddate[0]--;
			ddate[1] =- i;
			s--;
		}
		continue;
	}
	if(argc <= 1) {
		printf("no file system specified\n");
		exit(0);
	}
	info();
	bigbuf = sbrk(bufsize);
	if(bigbuf == -1){
		printf("No memory\n");
		exit(0);
	}
	if(iflg) {
		f = open(dfile, 0);
		if(f >= 0) {
			read(f, dtab, sizeof dtab);
			close(f);
			for(i=0; i<10; i++)
				if(equal(dtab[i].name, argv[1])) {
					ddate[0] = dtab[i].date[0];
					ddate[1] = dtab[i].date[1];
				}
		}
	}
	printf("%s:\n", argv[1]);
	fi = open(argv[1], 0);
	if(fi < 0) {
		printf("cannot open %s\n", argv[1]);
		exit(0);
	}
	printf("incremental dump from\n");
	pdate(ddate);
	sync();
	bread(1, &sblock);
	talist = sbrk(size(0, sblock.s_isize*32)*512);
	tap = talist;
	if(tap == -1) {
		printf("No memory\n");
		exit(0);
	}
	nfil = 0;
	nblk = size(0, sblock.s_isize*32);
	n = nblk;
	ino = 0;
	for(i=0; i<sblock.s_isize; i++) {
		bread(i+2, buf);
		for(ip = &buf[0]; ip < &buf[256]; ip++) {
			ino++;
			if(ip->i_mode == 0 || ip->i_nlink == 0) {
				*tap++ = -1;
				continue;
			}
			if(ip->i_mtime[0] < ddate[0])
				goto no;
			if(ip->i_mtime[0] == ddate[0] &&
			   ip->i_mtime[1] <  ddate[1])
				goto no;
			s = size(ip->i_size0&0377, ip->i_size1) + 1;
			if (s>MAXSIZE && aflg==0 && iflg!=0) {
				printf("%l big; not dumped.\n", ino);
				goto no;
			}
			nfil++;
			n =+ s;
			*tap++ = s;
			continue;
		no:
			*tap++ = 0;
		}
	}
	printf("%l files\n%D blocks\n", nfil, n);
	i = tsize;
	n = n*100l;
	i = (n/i)/recsiz;
	s = i%100;
	printf("%l.%l%l tapes\n", i/100, s/10, (s-(s/10)*10));
	tap = buf;
	clrbuf(tap);
	*tap++ = sblock.s_isize;
	*tap++ = sblock.s_fsize;
	*tap++ = date[0];
	*tap++ = date[1];
	*tap++ = ddate[0];
	*tap++ = ddate[1];
	*tap++ = tsize;
	*tap++ = recsiz;
	swrite(buf);
	i = size(0, sblock.s_isize*32);
	tap = talist;
	while(i--) {
		bwrite(tap);
		tap =+ 256;
	}
	tap = talist;
	for(i=0; i<sblock.s_isize; i++) {
		bread(i+2, buf);
		s = 1;
		for(ip = &buf[0]; ip < &buf[256]; ip++) {
			inum = i*16 + s;
			if(*tap && *tap != -1)
				dump(ip, *tap-1);
			tap++;
			s++;
		}
	}
	rawout();
	printf("%l phase errors\n", pher);
	if(!dflg)
		exit(0);
	for(i=0; i<10; i++)
		dtab[i].name[0] = 0;
	f = open(dfile, 2);
	if(f < 0) {
		f = creat(dfile, 0666);
		if(f < 0) {
			printf("cannot create %s\n", dfile);
			exit(0);
		}
	} else
		read(f, dtab, sizeof dtab);
	for(i=0; i<10; i++)
		if(dtab[i].name[0] == 0 || equal(dtab[i].name, argv[1]))
			goto found;
	printf("%s full\n", dfile);
	exit(0);

found:
	for(s=0; s<15; s++) {
		dtab[i].name[s] = argv[1][s];
		if(argv[1][s] == 0)
			break;
	}
	dtab[i].date[0] = date[0];
	dtab[i].date[1] = date[1];
	seek(f, 0, 0);
	write(f, dtab, sizeof dtab);
	printf("date updated\n");
	pdate(date);
}

pdate(d)
int *d;
{

	if(d[0] == 0 && d[1] == 0)
		printf("the epoch\n"); else
		printf(ctime(d));
}

dump(ip, sz)
struct inode *ip;
{
	register *p, *q, *r;

	p = dbuf;
	q = ip;
	clrbuf(p);
	while(q < &ip->i_mtime[2])
		*p++ = *q++;
	swrite(dbuf);
	if(ip->i_mode & (IFBLK&IFCHR)) {
		if(sz != 0)
			printf("special\n");
		return;
	}
	for(p = &ip->i_addr[0]; p < &ip->i_addr[8]; p++)
	if(*p) {
		if(ip->i_mode&ILARG) {
			bread(*p, ibuf);
			for(q = &ibuf[0]; q < &ibuf[256]; q++)
			if(*q) {
				/*this section dumps huge files*/
				/*if(p == &ip->i_addr[7]) {
					bread(*q, vbuf);
					for(r = &vbuf[0]; r < &vbuf[256]; r++)
					if(*r) {
						if(--sz < 0)
							goto pe;
						bread(*r, dbuf);
						bwrite(dbuf);
					}
					continue;
				}*/
				if(--sz < 0)
					goto pe;
				bread(*q, dbuf);
				bwrite(dbuf);
			}
		} else {
			if(--sz < 0)
				goto pe;
			bread(*p, dbuf);
			bwrite(dbuf);
		}
	}
	if(sz)
		goto pe;
	return;

pe:
	clrbuf(dbuf);
	while(--sz >= 0)
		bwrite(dbuf);
	pher++;
}

bread(bno, b)
{

	seek(fi, bno, 3);
	if(read(fi, b, 512) != 512) {
		printf("read error %l\n", bno);
	}
}

clrbuf(b)
int *b;
{
	register i, *p;

	p = b;
	i = 256;
	while(i--)
		*p++ = 0;
}

swrite(b)
int *b;
{
	register i, s, *p;

	i = 253;
	s = taddr + 1 + inum;
	p = b;
	while(i--)
		s =+ *p++;
	*p++ = inum;
	*p++ = taddr + 1;
	*p = 031415 - s;
	bwrite(b);
}

bwrite(b)
{
	register *p,*g;
	int f,j;
	static int i;
	g = b;
	p = bigbuf + i*(256);
	for(j = 0;j < 256; j++) *p++ = *g++;
	if(++i == recsiz) {
		rawout();
		i = 0;
	}
}
rawout()
{

static int nrec;
	int *ta ,*bi;
	if(taddr == 0) {
		if(fo != -1) {
			printf("change tapes\n");
			close(fo);
			if(!cflg)
			rdline();
		}
		otape();
	}
	ta = taddr;
	bi = bigbuf;
	while(write(fo, bi, bufsize) != bufsize) {
		printf("write error\n");
		printf("no. of records = %d\n",ta);
		rdline();
	}
	taddr++;
	if(taddr >= tsize)
		taddr = 0;
	for(ta = bigbuf; ta < bigbuf + (bufsize/2); ta++) 
		*ta = 0;
}

rdline()
{
	char c;

	while(read(0,&c,1) != 0){
		if(c == '\n')
			return;
	}
	exit(0);
}

number(s)
char *s;
{
	register n, c;

	n = 0;
	while(c = *s++) {
		if(c<'0' || c>'9')
			continue;
		n = n*10+c-'0';
	}
	return(n);
}

size(s0, s1)
{
	register s;
	extern ldivr;

	s = ldiv(s0&0377, s1, 512);
	if(ldivr)
		s++;
	return(s);
}

otape()
{
	register char *p;

	fo = creat(ofile, 0666);
	if(fo < 0) {
		printf("can not open %s\n", ofile);
		exit(0);
	}
	if(!cflg)
		return;
	p = ofile;
	while(*p++)
		;
	p[-2]++;
}

equal(a, b)
char *a, *b;
{

	while(*a++ == *b)
		if(*b++ == 0)
			return(1);
	return(0);
}
info()
{
	int div, feet, i;
	char *bsize;
	long n;
	bufsize = 5120;
	feet = 2200;
	i = 64;
	if(pflg)
		bufsize = 512;
	else if(nflg)
		bufsize = 512*number(narg);
	if(sflg)
		feet = number(sarg);
	if(qflg && !pflg)
		i = 32;
	recsiz = bufsize/512;
	div = ((i*recsiz)+60)/10;
	if(tsize = (feet/div)*120);
	else tsize = 1;
	if(bflg){
		n = number(barg);
		n = n & 0177777l;
		bsize = n/recsiz;
		if(bsize > tsize){
			printf("too many blocks for this tape\n");
			exit(0);
		}
		tsize = bsize;
		feet = (tsize/120)*div;
	}
	printf("tape size = %d records\n",tsize);
	printf("each record is %d blocks long\n",recsiz);
	printf("the tape must be longer than %d feet\n",feet);
}
