static char rcsid[] = "$Header:copyout.c 12.0$";
#include <stdio.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/vm.h>
#include <sys/proc.h>
#include <sys/pte.h>
#include <sys/cmap.h>
#include <sys/inode.h>
#include <sys/buf.h>
#include <sys/text.h>
#include <sys/ino.h>

struct	buf *bread();
struct	buf b;
char	bc[BSIZE];
int	bflag;

main(argc, argv)
	char *argv[];
{
	struct dinode di;
	struct inode i;
	struct proc p;
	struct user u;
	struct text x;
	struct pte *pte;
	int j,k,l;

	if (argc > 1 && !strcmp(argv[1], "-b"))
		argc--, argv++, bflag++;
	if (argc != 3) {
		fprintf(stderr, "usage: copyout [ -b ] inum disk\n");
		exit(1);
	}
	close(0);
	if (open(argv[2], 0) != 0) {
		perror(argv[2]);
		exit(1);
	}
	lseek(0, itod(atoi(argv[1])) * BSIZE + itoo(atoi(argv[1])) * sizeof (di), 0);
	if (read(0, &di, sizeof (di)) != sizeof (di)) {
		fprintf(stderr, "error reading inode ");
		perror(argv[1]);
		exit(1);
	}
	i.i_dev = 0;
	i.i_number = atoi(argv[1]);
	i.i_flag = ILOCK;
	i.i_count = 1;
	i.i_un.i_lastr = 0;
	i.i_mode = di.di_mode;
	i.i_nlink = di.di_nlink;
	i.i_uid = di.di_uid;
	i.i_size = di.di_size;
	l3tol(i.i_un.i_addr, di.di_addr, NADDR);
	p.p_textp = &x;
	x.x_iptr = &i;
	b.b_un.b_addr = bc;
	pte = (struct pte *)calloc(btoc(i.i_size), sizeof (struct pte));
	if (pte == NULL) {
		fprintf(stderr, "Not enough core for block pointers\n");
		exit(1);
	}
	vinizfod(&p, pte, 0, (btoc(i.i_size)+(CLSIZE-1)) / CLSIZE);
	l = i.i_size;
	for (j = 0; j < (btoc(i.i_size)+(CLSIZE-1)) / CLSIZE; j++)
		if (bflag)
			printf("#%d: block %d\n", j, pte[j].pg_pfnum);
		else {
			k = imin(l, BSIZE);
			write(1, bread(0, pte[j].pg_pfnum)->b_un.b_words, k);
			brelse(&b);
			l -= BSIZE;
		}
	exit(0);
}

int	bused;

struct buf *
bread(dev, blk)
{

	if (bused)
		abort();
	bused = 1;
	printf("getblk %x\n", blk);
	lseek(0, blk * BSIZE, 0);
	if (read(0, b.b_un.b_addr, BSIZE) != BSIZE) {
		printf("block %d: ", blk);
		perror("bread");
	}
	return (&b);
}

brelse(bp)
struct buf *bp;
{

	if (bp != &b)
		abort();
	bused = 0;
}

struct	buf *vbmap();
/*
 * Initialize the page tables for paging from an inode,
 * by scouring up the indirect blocks in order.
 */
vinizfod(p, pte, bstart, count)
	struct proc *p;
	register struct pte *pte;
	daddr_t bstart;
	int count;
{
	register struct inode *ip = p->p_textp->x_iptr;
	register int i;
	struct buf *bp;
	int indx;
	register daddr_t *pp;

	while (count > 0) {
		if (bstart < NADDR - 3) {
			pte++->pg_pfnum = ip->i_un.i_addr[bstart];
			bstart++;
			count--;
		} else {
			bp = vbmap(ip, bstart);
			indx = (bstart - (NADDR - 3)) % NINDIR;
			i = imin(NINDIR - indx, count);
			bstart += i;
			count -= i;
			if (bp) {
				pp = &bp->b_un.b_daddr[indx];
				do
					pte++->pg_pfnum = *pp++;
				while (--i > 0);
				brelse(bp);
			} else
				pte += i;
		}
	}
}

/*
 * Vbmap returns a block full of indirect pointers for a given block offset
 * in a file.  It returns 0 if a missing address block was encountered,
 * in which case the pages can be normal zfod pages.
 */
struct buf *
vbmap(ip, bn)
register struct inode *ip;
daddr_t bn;
{
	register i;
	struct buf *bp;
	int j, sh;
	daddr_t nb;
	dev_t dev = ip->i_dev;

	if (bn < NADDR-3)
		panic("vbmap");

	/*
	 * addresses NADDR-3, NADDR-2, and NADDR-1
	 * have single, double, triple indirect blocks.
	 * the first step is to determine
	 * how many levels of indirection.
	 */
	sh = 0;
	nb = 1;
	bn -= NADDR-3;
	for (j = 3; j > 0; j--) {
		sh += NSHIFT;
		nb <<= NSHIFT;
		if(bn < nb)
			break;
		bn -= nb;
	}
	if (j == 0)
		goto noblk;

	/*
	 * fetch the address from the inode
	 */
	nb = ip->i_un.i_addr[NADDR-j];

	/*
	 * fetch through the indirect blocks
	 */
	for (;;) {
		if (nb == 0)
			return ((daddr_t)0);
		bp = bread(dev, nb);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			goto noblk;
		}
		if (j == 3)
			break;
		j++;
		sh -= NSHIFT;
		i = (bn>>sh) & NMASK;
		nb = bp->b_un.b_daddr[i];
		brelse(bp);
		if (nb == 0)
			goto noblk;
	}
	return (bp);

noblk:
	return ((struct buf *)0);
}

imin(a,b)
{
	return (a<b?a:b);
}

panic(cp)
{
	printf(cp);
	abort();
}

char vmmap[1];
char version[1];
