static char SCCS_ID [] = "@(#)dumpdir.c    	3.2	 20:02:48 - 82/01/07 ";

#define MAXINO	3000
#define BITS	8
#define MAXXTR	60
#define NCACHE	3

#include <stdio.h>
#include <sys/local.h>
#include <sys/param.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/fblk.h>
#include <sys/filsys.h>
#include <sys/dir.h>
#ifndef DIR512
#include <dumprestor.h>
#else	DIR512

#undef  BSIZE
#define BSIZE  512

/* old <dumprestor.h> */
#define NTREC   	20
#define MLEN    	16
#define MSIZ    	4096

#define TS_TAPE 	1
#define TS_INODE	2
#define TS_BITS 	3
#define TS_ADDR 	4
#define TS_END  	5
#define TS_CLRI 	6
#define MAGIC   	(int)60011
#define CHECKSUM	(int)84446
struct	spcl
{
	int	c_type;
	time_t	c_date;
	time_t	c_ddate;
	int	c_volume;
	daddr_t	c_tapea;
	ino_t	c_inumber;
	int	c_magic;
	int	c_checksum;
	struct	dinode	c_dinode;
	int	c_count;
	char    c_addr[BSIZE];
} spcl;

struct	idates
{
	char	id_name[16];
	char	id_incno;
	time_t	id_ddate;
};

/* end of old <dumprestor.h> */
#endif	DIR512

#define	MWORD(m,i) (m[(unsigned)(i-1)/MLEN])
#define	MBIT(i)	(1<<((unsigned)(i-1)%MLEN))
#define	BIS(i,w)	(MWORD(w,i) |=  MBIT(i))
#define	BIC(i,w)	(MWORD(w,i) &= ~MBIT(i))
#define	BIT(i,w)	(MWORD(w,i) & MBIT(i))

int	mt;
char	tapename[] = "/dev/rmt0";		/***/
char	*magtape = tapename;

daddr_t	seekpt;
int	ofile;
FILE	*df;
char	dirfile[] = "rstXXXXXX";

struct {
	ino_t	t_ino;
	daddr_t	t_seekpt;
} inotab[MAXINO];
int	ipos;

#define ONTAPE	1
#define XTRACTD	2
#define XINUSE	4

short	dumpmap[MSIZ];
short	clrimap[MSIZ];


int bct = NTREC+1;
char tbf[NTREC*BSIZE];

char prebuf[512];

int volno;

int	pdpflag;

#ifdef interdata
/*
 * Mapping from pdp11 header structure to Interdata version
 */
short spcl11[] = {
	2, 0, 2, 2,	/* c_type */
	4, 2,		/* c_date */
	4, 2,		/* c_ddate */
	2, 0, 2, 2,	/* c_volume */
	4, 2,		/* c_tapea */
	2, 2,		/* c_inumber */
	4, 0, 2, 2,	/* c_magic */
	2, 0, 2, 2,	/* c_checksum */
		2, 2,		/* di_mode */
		2, 2,		/* di_nlink */
		2, 2,		/* di_uid */
		2, 2,		/* di_gid */
		4, 2,		/* di_size */
		40, 1,		/* di_addr */
		12, 2,		/* di_atime, di_mtime, di_ctime */
	2, 0, 2, 2,	/* c_count */
	BSIZE, 1,	/* c_addr */
	0
};
#endif

main(argc, argv)
char *argv[];
{
	extern char *ctime();

	mktemp(dirfile);
	if (argc > 1) {
		register char *cp;

		for (cp = argv[1]; *cp; cp++) switch (*cp) {
			case 'p':
				pdpflag++;
				break;

			case 'f':
				if (argc > 2)
					magtape = argv[2];
				else {
					printf("Missing filename argument.\n");
					exit(1);
				}
				break;

			default:
				printf("Illegal key character %c.\n", *cp);
				exit(1);
		}
	}
	df = fopen(dirfile, "w");
	if (df == NULL) {
		printf("dumpdir: %s - cannot create directory temporary\n", dirfile);
		exit(1);
	}

	if ((mt = open(magtape, 0)) < 0) {
		printf("%s: cannot open tape\n", magtape);
		exit(1);
	}
	if (readhdr(&spcl) == 0) {
		printf("Tape is not a dump tape\n");
		exit(1);
	}
	printf("Dump   date: %s", ctime(&spcl.c_date));
	printf("Dumped from: %s", ctime(&spcl.c_ddate));
	if (checkvol(&spcl, 1) == 0) {
		printf("Tape is not volume 1 of the dump\n");
		exit(1);
	}
	pass1();  /* This sets the various maps on the way by */
	freopen(dirfile, "r", df);
	strcpy(prebuf, "/");
	printem(prebuf, (ino_t) 2);
	exit(0);
}
	i = 0;
/*
 * Read the tape, bulding up a directory structure for extraction
 * by name
 */
pass1()
{
	register i;
	struct dinode *ip;
	int	putdir(), null();

	while (gethead(&spcl) == 0) {
		printf("Can't find directory header!\n");
	}
	for (;;) {
		if (checktype(&spcl, TS_BITS) == 1) {
			readbits(dumpmap);
			continue;
		}
		if (checktype(&spcl, TS_CLRI) == 1) {
			readbits(clrimap);
			continue;
		}
		if (checktype(&spcl, TS_INODE) == 0) {
finish:
			flsh();
			close(mt);
			return;
		}
		ip = &spcl.c_dinode;
		i = ip->di_mode & IFMT;
		if (i != IFDIR) {
			goto finish;
		}
		inotab[ipos].t_ino = spcl.c_inumber;
		inotab[ipos++].t_seekpt = seekpt;
		getfile(spcl.c_inumber, putdir, null, spcl.c_dinode.di_size);
		putent("\000\000/");
	}
}

printem(prefix, inum)
char *prefix;
ino_t	inum;
{
	struct direct dir;
	register int i;

	for (i = 0; i < MAXINO; i++)
		if (inotab[i].t_ino == inum) {
			goto found;
		}
	printf("PANIC - can't find directory %d\n", inum);
	return;
found:
	mseek(inotab[i].t_seekpt);
	for (;;) {
		getent((char *) &dir);
		if (direq(dir.d_name, "/"))
			return;
		if (search(dir.d_ino) != 0 && direq(dir.d_name, ".") == 0 && direq(dir.d_name, "..") == 0) {
			int len;
			FILE *tdf;

			tdf = df;
			df = fopen(dirfile, "r");
			len = strlen(prefix);
			strncat(prefix, dir.d_name, sizeof(dir.d_name));
			strcat(prefix, "/");
			printem(prefix, dir.d_ino);
			prefix[len] = '\0';
			fclose(df);
			df = tdf;
		}
		else
			if (BIT(dir.d_ino, dumpmap))
				printf("%5d	%s%-.14s\n", dir.d_ino, prefix, dir.d_name);
	}
}
/*
 * Do the file extraction, calling the supplied functions
 * with the blocks
 */
getfile(n, f1, f2, size)
ino_t	n;
int	(*f2)(), (*f1)();
long	size;
{
	register i;
	struct spcl addrblock;
	char buf[BSIZE];

	addrblock = spcl;
	goto start;
	for (;;) {
		if (gethead(&addrblock) == 0) {
			printf("Missing address (header) block\n");
			goto eloop;
		}
		if (checktype(&addrblock, TS_ADDR) == 0) {
			spcl = addrblock;
			return;
		}
start:
		for (i = 0; i < addrblock.c_count; i++) {
			if (addrblock.c_addr[i]) {
				readtape(buf);
				(*f1)(buf, size > BSIZE ? (long) BSIZE : size);
			}
			else {
				clearbuf(buf);
				(*f2)(buf, size > BSIZE ? (long) BSIZE : size);
			}
			if ((size -= BSIZE) <= 0) {
eloop:
				while (gethead(&spcl) == 0)
					;
				if (checktype(&spcl, TS_ADDR) == 1)
					goto eloop;
				return;
			}
		}
	}
}

/*
 * Do the tape i\/o, dealling with volume changes
 * etc..
 */
readtape(b)
char *b;
{
	register i;
	struct spcl tmpbuf;

	if (bct >= NTREC) {
		for (i = 0; i < NTREC; i++)
			((struct spcl *)&tbf[i*BSIZE])->c_magic = 0;
		bct = 0;
		if ((i = read(mt, tbf, NTREC*BSIZE)) < 0) {
			exit(1);
		}
		if (i == 0) {
			bct = NTREC + 1;
			volno++;
loop:
			flsht();
			close(mt);
			printf("Mount volume %d\n", volno);
			while (getchar() != '\n')
				;
			if ((mt = open(magtape, 0)) == -1) {
				printf("Cannot open tape!\n");
			}
			if (readhdr(&tmpbuf) == 0) {
				printf("Not a dump tape.Try again\n");
				goto loop;
			}
			if (checkvol(&tmpbuf, volno) == 0) {
				printf("Wrong tape. Try again\n");
				goto loop;
			}
			readtape(b);
			return;
		}
	}
	copy(&tbf[(bct++*BSIZE)], b, BSIZE);
}

flsht()
{
	bct = NTREC+1;
}

copy(f, t, s)
register char *f, *t;
{
	register i;

	i = s;
	do
		*t++ = *f++;
	while (--i);
}

clearbuf(cp)
register char *cp;
{
	register i;

	i = BSIZE;
	do
		*cp++ = 0;
	while (--i);
}

/*
 * Put and get the directory entries from the compressed
 * directory file
 */
putent(cp)
char	*cp;
{
	register i;

#ifdef interdata
	if (pdpflag) {
		writec(cp[1]);
		writec(cp[0]);
		cp += 2;
	} else {
		for (i = 0; i < sizeof(ino_t); i++)
			writec(*cp++);
	}
#endif
	for (i = 0; i < DIRSIZ; i++) {
		writec(*cp);
		if (*cp++ == 0)
			return;
	}
	return;
}

getent(bf)
register char *bf;
{
	register i;

	for (i = 0; i < sizeof(ino_t); i++)
		*bf++ = readc();
	for (i = 0; i < DIRSIZ; i++)
		if ((*bf++ = readc()) == 0)
			return;
	return;
}

/*
 * read/write te directory file
 */
writec(c)
char c;
{
	seekpt++;
	fwrite(&c, 1, 1, df);
}

readc()
{
	char c;

	fread(&c, 1, 1, df);
	return(c);
}

mseek(pt)
daddr_t pt;
{
	fseek(df, pt, 0);
}

flsh()
{
	fflush(df);
}

/*
 * search the directory inode ino
 * looking for entry cp
 */
search(inum)
ino_t	inum;
{
	register low, high, probe;

	low = 0;
	high = ipos-1;

	while (low != high) {
		probe = (high - low + 1)/2 + low;
/*
printf("low = %d, high = %d, probe = %d, ino = %d, inum = %d\n", low, high, probe, inum, inotab[probe].t_ino);
*/
		if (inum >= inotab[probe].t_ino)
			low = probe;
		else
			high = probe - 1;
	}
	return(inum == inotab[low].t_ino);
}

direq(s1, s2)
register char *s1, *s2;
{
	register i;

	for (i = 0; i < DIRSIZ; i++)
		if (*s1++ == *s2) {
			if (*s2++ == 0)
				return(1);
		} else
			return(0);
	return(1);
}

/*
 * read the tape into buf, then return whether or
 * or not it is a header block.
 */
gethead(buf)
struct spcl *buf;
{
#ifdef interdata
	static struct spcl buf11;
#endif

	readtape((char *)buf);
#ifdef interdata
	if (pdpflag) {
		read11((char *)buf, (char *)&buf11, spcl11);
		if (buf11.c_magic != MAGIC || checksum((int *) buf) == 0)
			return(0);
		copy((char *)&buf11, (char *)buf, sizeof(*buf));
		return(1);
	}
#endif
	if (buf->c_magic != MAGIC || checksum((int *) buf) == 0)
		return(0);
	return(1);
}

/*
 * return whether or not the buffer contains a header block
 */
checktype(b, t)
struct	spcl *b;
int	t;
{
	return(b->c_type == t);
}


checksum(b)
int *b;
{
	register i, j;

#ifdef interdata
	if (pdpflag) {
		register char *p = (char *)b;
	
		j = BSIZE/2;
		i = 0;
		do {
			i += *p++;
			i += (*p++)<<8;
		} while (--j);
		if ((i&0xffff) != (CHECKSUM&0xffff)) {
			printf("Checksum error %o\n", i);
			return(0);
		}
		return(1);
	}
#endif
	j = BSIZE/sizeof(int);
	i = 0;
	do
		i += *b++;
	while (--j);
	if (i != CHECKSUM) {
		printf("Checksum error %o\n", i);
		return(0);
	}
	return(1);
}

checkvol(b, t)
struct spcl *b;
int t;
{
	if (b->c_volume == t)
		return(1);
	return(0);
}

readhdr(b)
struct	spcl *b;
{
	if (gethead(b) == 0)
		return(0);
	if (checktype(b, TS_TAPE) == 0)
		return(0);
	return(1);
}

putdir(b)
char *b;
{
	register struct direct *dp;
	register i;

	for (dp = (struct direct *) b, i = 0; i < BSIZE; dp++, i += sizeof(*dp)) {
		if (dp->d_ino == 0)
			continue;
		putent((char *) dp);
	}
}

/*
 * read a bit mask from the tape into m.
 */
readbits(m)
short	*m;
{
	register i;

	i = spcl.c_count;

	while (i--) {
		readtape((char *) m);
		m += (BSIZE/(MLEN/BITS));
	}
	while (gethead(&spcl) == 0)
		;
}

null() { ; }

#ifdef interdata
/*
 * Translate pdp11 structure to Interdata version
 */
read11(from, to, format)
register char *from, *to;
register short *format;
{
	register n;

	while (n = *format++) switch (*format++) {

		case 0:		/* zero fill */
			do {
				*to++ = '\0';
			} while (--n);
			break;

		case 1:		/* copy */
			do {
				*to++ = *from++;
			} while (--n);
			break;

		case 2:		/* byteswap */
			do {
				*to++ = from[1];
				*to++ = from[0];
				from += 2;
			} while ((n -= 2) > 0);
			break;
		}
}
#endif
