/*
 * John Buck -- cat file directly from disk, avoid OS intervention and
 *	side effects. (Setting dates, etc.)
 */
#include	"stdio.h"
#include	"sys/types.h"
#include	"sys/param.h"
#include	"sys/stat.h"
#include	"sys/ino.h"
#include	"sys/inode.h"
#include	"sys/sysmacros.h"

char	*getblk();
char	*malloc();
daddr_t bmap();
char	*disk = "/dev/rrmxxx";
struct	stat	statb;
short	fs;

struct	bufs	{
	daddr_t	b_bno;
	char *b_addr;
} bufs[4];


main(argc, argv)
char **argv;
{
	register dev_t	d;
	register char	*s;

	s = argv[0];
	while(*s)s++;
	s--;
	if(*s == 'd')*s = 0;
	if(argc < 1){
usage:
		fprintf(stderr, "Usage: %s [file ...] [-i special inode#]\n",
			argv[0]);
		exit(1);
	}
	if(argc == 1){
		if(fstat(0, &statb) || 
		((statb.st_mode &= S_IFMT) != S_IFREG && statb.st_mode != S_IFDIR))
			goto usage;
		goto hidden;
	}
	while(--argc){
		if(**++argv != '-'){
			if(stat(*argv, &statb)){
				fprintf(stderr, "Can not find %s\n", *argv);
				continue;
			}
hidden:
			s = disk + 8;
			d = statb.st_dev & 077;
			if(d > 9)
				*s++ = '1';
			*s++ = (d % 10) + '0';
			*s = 0;
			catd(disk, statb.st_ino);
		} else	{
			if(argc < 3)
				goto usage;
			argv++;
			catd(*argv, atoi(argv[1]));
			argv++;
			argc -= 2;
		}
	}
	exit(0);
}


catd(filesys, ino)
char	*filesys;
ino_t	ino;
{
	register struct	dinode *dp;
	register char *savbp;
	register struct inode *ip;
	
	if((fs = open(filesys, 0)) == -1){
		fprintf(stderr, "Can not open disk: %s\n", filesys);
		return(-1);
	}

	savbp = getblk(itod(ino), NULL);
	dp = (struct dinode *)savbp;
	dp += itoo(ino);
	ip = (struct inode *)malloc(sizeof (struct inode));
	if(ip == NULL)
		panic("catd: Out of memory\n");
	ip->i_size = dp->di_size;
	cpdaddr(ip, dp);
	free(savbp);
	docat(ip);
	free(ip);
	close(fs);
	cleanbufs();
	return(0);
}

cpdaddr(ip, dp)
struct	inode *ip;
struct dinode *dp;
{
	register char *p1, *p2;
	register short i;

	ip->i_addr = malloc(40);
	p1 = (char *)ip->i_addr;
	p2 = (char *)dp->di_addr;
	for(i = 0; i < NADDR; i++){
#ifdef	BLK16
		if(*p2++)
			panic("cpdaddr: iaddr > 2^16\n");
		*p1++ = *p2++;
		*p1++ = *p2++;
#else
		*p1++ = *p2++;
		*p1++ = 0;
		*p1++ = *p2++;
		*p1++ = *p2++;
#endif
	}
}


panic(s)
char *s;
{
	fprintf(stderr, "Fatal error: %s\n", s);
	exit(2);
}

char *
getblk(bno, addr)
daddr_t bno;
char *addr;
{
	if(addr == NULL)
		if((addr = malloc(512)) == NULL)
			panic("getblk: Out of memory");
	lseek(fs, bno << 9, 0);
	if(read(fs, addr, 512) != 512)
		panic("getblk: Read error\n");
	return(addr);
}

docat(ip)
register struct inode *ip;
{
	register char *addr;
	daddr_t	nblk, blk;
	register unsigned	fluff;

	nblk = (ip->i_size + BMASK)/BSIZE;
	fluff = ip->i_size & BMASK;
	if((addr = malloc(512)) == NULL)
		panic("docat: Out of memory\n");
	for(blk = 1L; blk <= nblk; blk++){
		getblk(bmap(ip, blk), addr);
		fwrite(addr, 1, blk == nblk ? fluff : 512, stdout);
	}
	return(0);
}

daddr_t
bmap(ip, bno)
struct	inode *ip;
daddr_t bno;
{
	register unsigned i, j;
	daddr_t	*bp;
	daddr_t	nb;
	register unsigned sh;

	if(bno-- < 0)
		panic("bmap: block number too big\n");

	if(bno < NADDR-3){
		i = bno;
		bno = ip->i_addr[i];
		return(bno);
	}
	sh = 0;
	nb = 1;
	bno -= NADDR-3;
	for(j = 3; j > 0; j--){
		sh += NSHIFT;
		nb <<= NSHIFT;
		if(bno < nb)
			break;
		bno -= nb;
	}
	if(j == 0)
		panic("bmap: File too large\n");
	nb = ip->i_addr[NADDR-j];
	if(nb == 0)
		return(0L);
	for(; j <= 3; j++){
		bp = getiblk(nb, j);
		sh -= NSHIFT;
		i = (bno >> sh) & NMASK;
		nb = bp[i];
		if(nb == 0)
			return(0L);
	}
	return(nb);
}

daddr_t *
getiblk(bno, ib)
daddr_t bno;
register short ib;
{

	if(bufs[ib].b_bno == bno)
		return(bufs[ib].b_addr);
	bufs[ib].b_bno = bno;
	return(bufs[ib].b_addr = getblk(bno, bufs[ib].b_addr));
}

cleanbufs()
{
	register short j;

	for(j = 1; j <= 3; j++){
		bufs[j].b_bno = 0;
		if(bufs[j].b_addr){
			free(bufs[j].b_addr);
			bufs[j].b_addr = NULL;
		}
	}
}
