/*	@(#)fsdb.c	2.1		*/

/*  fsdb - file system debugger    */

#ifdef STANDALONE
#define FsTYPE	2
#endif

#include	<sys/param.h>
#include	<signal.h>
#include	<sys/types.h>
#include	<sys/sysmacros.h>
#include	<sys/inode.h>
#include	<sys/dir.h>
#include	<sys/ino.h>
#include	<stdio.h>
#include	<setjmp.h>
#include	<sys/filsys.h>

#if	u370 || u3b || m68k
#define loword(X)	(((ushort *)&X)[1])
#define lobyte(X)	(((char *)&X)[1])
#endif

#ifdef STANDALONE
#define ASSEM "Stand-Alone"
#else
#define ASSEM "UNIX T/S"
#endif

#define	NBUF	3
#define	MODE	0
#define	LINK	2
#define	UID	4
#define	GID	6
#define	SZ	8
#define	INOSZ	(BSIZE/64)
#define	ADRMSK	(BSIZE -1)
#define	A0	12
#define	MIN	12
#define	MAJ	13
#define	AT	52
#define	MT	56
#define	CT	60
#define	NUMB	10
#define	INODE	11
#define	BLOCK	12
#define	DIR	13

#define	HIBIT	0100000

#ifdef pdp11 || m68k
#define SLOC BSIZE + 2
#else
#define SLOC BSIZE + 4
#endif

struct buf {
	struct	buf  *fwd;
	struct	buf  *back;
	char	*blkaddr;
	short	valid;
	long	blkno;
} buf[NBUF], bhdr;
char buffers[NBUF][BSIZE];

struct dir {
	short	inumb;
	char nm[DIRSIZ];
} *dirp;

struct dinode *ip;

long	addr, value, temp, oldaddr, oldvalue, erraddr;
long	cur_ino = BSIZE * 2;
short	objsz =  2;
short	fd, c_count, i, j, k, oldobjsz, error, type;
short	count, pid, rpid, mode, char_flag, prt_flag;
int	retcode;
unsigned	isize;
long	fsize;
char	*p,
	*getblk();
short	override = 1;
jmp_buf env;

/*
 * main - continuously looping input scanner.  Lines are scanned
 * a character at a time and are processed as soon as
 * sufficient information has been collected. When an error
 * is detected, the remainder of the line is flushed.
 */

main(argc,argv)
	short	argc;
	char	**argv;
{

	register	char  *cptr;
	register	char c;
	register	short  *iptr;
#ifdef STANDALONE
	char devnam[9];
	int maj,min;
	long blk;
#endif
	struct	sblock {
		char	block0[512];
		struct	filsys fs;
	} sblock;
	extern	long get();
	extern	long bmap();
	extern	long getnumb();
	extern	err();
	register struct buf *bp;
	unsigned block;
	short offset;

/*  printf("version 1.4 - 12/15/80 %lu%\n\n");*/
#ifndef STANDALONE
	setbuf(stdin,NULL);
#endif

	if(argc!=2 && argc!=3) {
		printf("usage: fsdb /dev/rp??\n");
		exit(1);
	}

#if FsTYPE==2
#ifdef m68k
	printf("\nM68K %s assembly\n", ASSEM );
#else
#ifdef vax
	printf("\nVAX %s assembly\n",ASSEM);
#else
#ifdef pdp11
	printf("\n11/70 %s assembly\n",ASSEM);
#else
#ifdef u370
	printf("\nIBM370 %s assembly\n",ASSEM);
#else
#ifdef u3b
	printf("\n3B20S %s assembly\n",ASSEM);
#endif
#endif
#endif
#endif
#endif
#endif
	if((fd = open(argv[1],2)) < 0) {
#ifdef STANDALONE
		cptr = argv[1];
		for(maj = 0; maj <9; devnam[maj++] = *cptr++);
		if(devnam[8] == '\0'){
			maj = 0;
			min = devnam[7] - '0';
		}
		else {
			maj = devnam[7];
			min = devnam[8];
		}
		if(maj <0 | maj > 3 | min < 0 | min > 5){
			printf("invalid device name - %s\n",devnam);
			exit(1);
		}
		if(min == 0) blk = 0l;
		else blk = 1392l + ((long)(min-1) * 65626l);
printf("trying to make node %s %d %d %ld\n",devnam,maj,min,blk);
		if(MKNOD(devnam,maj,min,blk) < 0){
			printf("cannot create %s \n",devnam);
			exit(1);
		}
		if((fd = open(devnam,2)) < 0){
			printf("cannot open %s\n",argv[1]);
			exit(1);
		}
	}
#else
		/*printf("cannot open %s\n",argv[1]);*/
		perror(argv[0]);
		exit(1);
	}
#if FsTYPE==2
	/* check to see if this is a 1kb or 512b file system */
	if (read(fd, &sblock, sizeof sblock) != sizeof sblock) {
		printf("%s: cannot read superblock\n", argv[0]);
		exit(1);
	}
#define S	sblock.fs
	if (S.s_magic != FsMAGIC)
		S.s_type = Fs1b; /* old style fs--default to 512 block */
	switch (S.s_type) {
	case Fs1b:
		printf("%s(%s): 512 byte Block File System\n", argv[1], S.s_fname);
		close(fd);
		if (execvp("/etc/fsdb1b", argv) < 0) {
			/*printf("%s: cannot exec fsdb1\n", argv[0]);*/
			perror(argv[0]);
			exit(1);
		}
		break;
	case Fs2b:
		printf("%s(%s): 1K byte Block File System\n", argv[1], S.s_fname);
		break;
	default:
		printf("%s: Invalid File System Type\n", S.s_fname);
		exit(1);
	}
#endif
#endif

	bhdr.fwd = bhdr.back = &bhdr;
	for(i=0; i<NBUF; i++) {
		bp = &buf[i];
		bp->blkaddr = buffers[i];
		bp->valid = 0;
		insert(bp);
	}

	reload();
	if(argc == 3) printf("error checking off\n");
	else override = 0;

#ifndef STANDALONE
	signal(2,err);
	setjmp(env);
#endif

	for(;;) {
		if(error) {
			if(c != '\n') while (getc(stdin) != '\n');
			c_count = 0;
			prt_flag = 0;
			char_flag = 0;
			error = 0;
			addr = erraddr;
			printf("?\n");
			/* type = -1; allows "d31 + +" to work */
		}
		c_count++;

		switch(c = getc(stdin)) {

		case '\n': /* command end */
			erraddr = addr;
			if(c_count == 1) addr = addr + objsz;
			c_count = 0;
			if(prt_flag) {
				prt_flag = 0;
				continue;
			}
			temp = get(objsz);
			/* if an error has been flagged, it is probably
			 * due to allignment.  This will have set objsz
			 * to 1 hence the next get should work.
			 */
			if(error) temp = get(objsz);
			switch(objsz) {
			case 1: cptr = ".B"; break;
			case 2: cptr = ""; break;
			case 3: cptr = ".A"; break;
			case 4: cptr = ".D"; break;
			case DIRSIZ:
			case 16:
				if(bcomp(addr,erraddr)) continue;
				fprnt('d', 1);
				prt_flag = 0;
				continue;
			case 64: fprnt('i',1);
				cur_ino = addr;
				prt_flag = 0;
				continue;
			}
			printf("%6.6lo%s: %6.6lo (%ld)\n",addr,cptr,temp,temp);
			continue;

		default: /* catch absolute addresses, b and i#'s */
			if(c<='9' && c>='0') {
				ungetc(c,stdin);
				addr = getnumb();
				objsz = 2;
				value = addr;
				type = NUMB;
				continue;
			}
			if(feof(stdin)) exit(0);
			error++;
			continue;

		case 'i': /* i# to inode conversion */
			if(c_count == 1) {
				addr = cur_ino;
				value = get(64);
				type = INODE;
				continue;
			}
			if(type==NUMB)value = addr;
			addr = ((value - 1) * sizeof(struct dinode)) + (BSIZE * 2);
			if(icheck(addr)) continue;
			cur_ino = addr;
			value = get(64);
			type = INODE;
			continue;

		case 'b': /* block conversion */
			if(type == NUMB)value = addr;
			addr = value << BSHIFT;
			value = get(2);
			type = BLOCK;
			continue;

		case 'd': /* directory offsets */
			value = getnumb();
			if(error||(value > (BSIZE / sizeof(struct direct)) - 1)) {
				error++;
				continue;
			}
			if(value != 0) if(dircheck()) continue;
			addr = (addr & ~ADRMSK) + (value * sizeof(struct direct));
			value = get(16); /* i-number */
			type = DIR;
			continue;

		case '\t':
		case ' ':
		case '.': continue;

		case '+': /* address addition */
			c = getc(stdin);
			ungetc(c,stdin);
			if(c > '9' || c < '0') temp = objsz;
			else temp = getnumb() * objsz;
			if(error) continue;
			if(objsz == DIRSIZ || objsz == 16)
				if(bcomp(addr,addr+temp)) {
					c = '+';
					continue;
				}
			addr = addr + temp;
			value = get(objsz);
			continue;

		case '-': /* address subtraction */
			c = getc(stdin);
			ungetc(c,stdin);
			if(c > '9' || c < '0') temp = objsz;
			else temp = getnumb() * objsz;
			if(error) continue;
			if(objsz == DIRSIZ || objsz == 16)
				if(bcomp(addr,addr-temp)) {
					c = '-';
					continue;
				}
			addr = addr - temp;
			value = get(objsz);
			continue;

		case '*': temp = getnumb();
			if(error) continue;
			addr = addr * temp;
			value = get(objsz);
			continue;

		case '/': temp = getnumb();
			if(error) continue;
			addr = addr / temp;
			value = get(objsz);
			continue;

		case 'q': /* quit */
			if(c_count != 1 || (c = getc(stdin)) != '\n') {
				error++;
				continue;
			}
			exit(0);

		case '>': /* save current address */
			oldaddr = addr;
			oldvalue = value;
			oldobjsz = objsz;
			continue;

		case '<': /* restore saved address */
			addr = oldaddr;
			value = oldvalue;
			objsz = oldobjsz;
			continue;

		case 'a': /* access time */
			if((c = getc(stdin)) == 't') {
				addr = cur_ino + AT;
				type = AT;
				value = get(4);
				continue;
			}

			/* data block addresses */
			ungetc(c,stdin);
			value = getnumb();
			if(error||(value > 12)) {
				error++;
				continue;
			}
			addr = cur_ino + A0 + (value * 3);
			value = get(3);
			type = A0;
			continue;

		case 'm': /* mt, md, maj, min */
			addr = cur_ino;
			mode = get(2);
			if(error) continue;
			switch(c = getc(stdin)) {
			case 't': /* modification time */
				addr = addr + MT;
				type = MT;
				value = get(4);
				continue;
			case 'd': /* mode */
				addr = addr + MODE;
				type = MODE;
				value = get(2);
				continue;
			case 'a': /* major device number */
				if((c = getc(stdin)) != 'j') {
					error++;
					continue;
				}
				if(devcheck(mode)) continue;
				addr = addr + MAJ;
				value = get(1);
				type = MAJ;
				continue;
			case 'i': /* minor device number */
				if((c = getc(stdin)) != 'n') {
					error++;
					continue;
				}
				if(devcheck(mode)) continue;
				addr = addr + MIN;
				value = get(1);
				type = MIN;
				continue;
			}
			error++;
			continue;

		case 's': /* file size */
			if((c = getc(stdin)) != 'z') {
				error++;
				continue;
			}
			addr = cur_ino + SZ;
			value = get(4);
			type = SZ;
			continue;

		case 'l': /* link count */
			if((c = getc(stdin)) != 'n') {
				error++;
				continue;
			}
			addr = cur_ino + LINK;
			value = get(2);
			type = LINK;
			continue;

		case 'g': /* group id */
			if((c=getc(stdin))!= 'i' || (c=getc(stdin)) != 'd') {
				error++;
				continue;
			}
			addr = cur_ino + GID;
			value = get(2);
			type = GID;
			continue;

		case 'u': /* user id */
			if((c=getc(stdin))!= 'i' || (c=getc(stdin)) != 'd') {
				error++;
				continue;
			}
			addr = cur_ino + UID;
			value = get(1);
			type = UID;
			continue;

		case 'n': /* directory name */
			if((c = getc(stdin)) != 'm') {
				error++;
				continue;
			}
			if(dircheck()) continue;
			type = DIR;
			objsz = DIRSIZ;
			addr = (addr & ~017) + 2;
			continue;

		case '=': /* assignment operation	*/
			switch(c = getc(stdin)) {
			case '"': /* character string */
				puta();
				continue;
			case '+': /* =+ operator */
				temp = getnumb();
				value = get(objsz);
				if(!error) put(value+temp,objsz);
				continue;
			case '-': /* =- operator */
				temp = getnumb();
				value = get(objsz);
				if(!error) put(value-temp,objsz);
				continue;
			default: /* nm and regular assignment */
				ungetc(c,stdin);
				if((type == DIR) && (c > '9' || c < '0')) {
					addr = (addr & ~017) + 2;
					puta();
					continue;
				}
				value = getnumb();
				if(!error) put(value,objsz);
				continue;
			}

		case '!': /* shell command */
#ifdef STANDALONE
			printf("Shell not available stand-alone");
			continue;
#else
			if(c_count != 1) {
				error++;
				continue;
			}
			if((pid = fork()) == 0) {
				execl("/bin/sh", "sh", "-t", 0);
				error++;
				continue;
			}
			while((rpid = wait(&retcode)) != pid && rpid != -1);
			printf("!\n");
			c_count = 0;
			continue;
#endif

		case 'F': /* buffer status */
			for(bp=bhdr.fwd; bp!= &bhdr; bp=bp->fwd)
				printf("%6lu %d\n",bp->blkno,bp->valid);
			continue;

		case 'f': /* file print facility */
			if((c=getc(stdin)) >= '0' && c <= '9') {
				ungetc(c,stdin);
				temp = getnumb();
				if (error) continue;
				c = getc(stdin);
			}
			else temp = 0;
			count = 0;
			addr = cur_ino;
			mode = get(2);
			if(!override) {
				if((mode & IFMT)==0)
					printf("warning: inode not allocated\n");
			}

			if(mode & IFCHR) {
				printf("special device\n");
				error++;
				continue;
			}

			if((addr = (bmap(temp)) << BSHIFT) == 0)
				continue;
			fprnt(c,0);
			continue;

		case 'O': /* override flip flop */
			if(override = !override)
				printf("error checking off\n");
			else {
				printf("error checking on\n");
				reload();
			}
			prt_flag++;
			continue;

		case 'B': /* byte offsets */
			objsz = 1;
			continue;

		case 'W': /* word offsets */
			objsz = 2;
			addr = addr & ~01;
			continue;

		case 'D': /* double word offsets */
			objsz = 4;
			addr = addr & ~01;
			continue;
#ifndef STANDALONE
		case 'A': abort();
#endif

		case ',': /* general print facilities */
		case 'p':
			if(( c = getc(stdin)) >= '0' && c <= '9') {
				ungetc(c,stdin);
				count = getnumb();
				if(error) continue;
				if((count < 0) || (count > BSIZE))
					count = 1;
				c = getc(stdin);
			}
			else if(c == '*'){
				count = 0;	/* print to end of block */
				c = getc(stdin);
			}
			else count = 1;
			fprnt(c,count);
		}
	}
}

/*
 * getnumb - read a number from the input stream.  A leading
 * zero signifies octal interpretation. If the first character
 * is not numeric this is an error, otherwise continue
 * until the first non-numeric.
 */

long
getnumb()
{

	extern	short  error;
	long	number, base;
	register	char  c;

	if(((c = getc(stdin)) < '0')||(c > '9')) {
		error++;
		ungetc(c,stdin);
		return(-1);
	}
	if(c == '0') base = 8;
	else base = 10;
	number = c - '0';
	while(((c = getc(stdin)) >= '0' )&&( c <= '9')) {
		if((base == 8) && ((c =='8')||(c == '9'))) {
			error++;
			return(-1);
		}
		number = number * base + c - '0';
	}
	ungetc(c,stdin);
	return(number);
}

/*
 * get - read a byte, word or double word from the file system.
 * The entire block containing the desired item is read
 * and the appropriate data is extracted and returned. 
 * Inode and directory size requests result in word
 * fetches. Directory names (objsz == DIRSIZ) result in byte
 * fetches.
 */

long
get(lngth)
	short	lngth;
{

	long	vtemp;
	char *bptr;
	short offset;

	objsz = lngth;
	if(allign(objsz)) return(-1);
	if((bptr = getblk(addr)) == 0) return(-1);
	vtemp = 0;
	offset = addr & ADRMSK;
	bptr = bptr + offset;
	if(offset + lngth > BSIZE) {
		error++;
printf ("get(lngth) - %d too long\n",offset + lngth);
		return(-1);
	}
	switch(objsz) {
	case 4:	vtemp = *(long *)bptr;
		return(vtemp);
	case 64:
	case 16:
	case 2: loword(vtemp) = *(short *)bptr;
		return(vtemp);
	case DIRSIZ:
	case 1: lobyte(loword(vtemp)) = *bptr;
		return(vtemp);
	case 3:
		l3tol(&vtemp,bptr,1);
		return(vtemp);
	}
	error++;
printf ("get(%d) - invalid length\n",lngth);
	return(0);
}

/*
 * icheck - check if the current address is within the I-list.
 * The I-list extends for isize blocks beginning at the 
 * super block + 1, i.e., from block 2 to block isize.
 */

icheck(address)
	long	address;
{
	unsigned blk;

	if(override) return(0);
	blk = address >> BSHIFT;
	if((blk >= 2) && (blk < isize))
		return(0);
	printf("inode out of range\n");
	error++;
	return(1);
}

/*
 * putf - print a byte as an ascii character if possible.
 * The exceptions are tabs, newlines, backslashes
 * and nulls which are printed as the standard c
 * language escapes. Characters which are not
 * recognized are printed as \?.
 */

putf(c)
	register	char  c;
{

	if(c<=037 || c>=0177 || c=='\\') {
		putc('\\',stdout);
		switch(c) {
		case '\\': putc('\\',stdout);
			break;
		case '\t': putc('t',stdout);
			break;
		case '\n': putc('n',stdout);
			break;
		case '\0': putc('0',stdout);
			break;
		default: putc('?',stdout);
		}
	}
	else {
		putc(' ',stdout);
		putc(c,stdout);
	}
	putc(' ',stdout);
}

/*
 * put - write an item into the buffer for the current address
 * block.  The value is checked to make sure that it will
 * fit in the size given without truncation.  If successful,
 * the entire block is written back to the file system.
 */

put(item,lngth)
	long item;
	short lngth;
{

	register char *bptr, *sbptr;
	register long *vptr;
	short offset;
	long	s_err,nbytes;

	objsz = lngth;
	if(allign(objsz)) return(-1);
	if((sbptr = getblk(addr)) == 0) return;
	offset = addr & ADRMSK;
	if(offset + lngth > BSIZE) {
		error++;
		printf("block overflow\n");
		return;
	}
	bptr = sbptr + offset;
	switch(objsz) {
	case 3: vptr = &item;
		if ( ((long)vptr & 1) != 0 ) break;
		ltol3(bptr,vptr,1);
		goto rite;
	case 4: *(long *)bptr = item;
		goto rite;
	case 64:
	case 16:
	case 2: if(item & ~0177777L) break;
		*(short *)bptr = item;
		goto rite;
	case DIRSIZ:
	case 1: if(item & ~0377) break;
		*bptr = lobyte(loword(item));
rite:	if((s_err = lseek(fd,addr & ~(long)ADRMSK,0)) == -1) {
		error++;
		printf("seek error : %lo\n",addr);
		return(0);
	}
	if((nbytes = write(fd,sbptr,BSIZE)) != BSIZE) {
		error++;
		printf("write error : addr   = %lo\n",addr);
		printf("           : s_err  = %lo\n",s_err);
		printf("           : nbytes = %lo\n",nbytes);
		return(0);
	}
		return;
	default: error++;
		return;
	}
	printf("truncation error\n");
	error++;
}

/*
 * getblk - check if the desired block is in the file system.
 * Search the incore buffers to see if the block is already
 * available. If successful, unlink the buffer control block
 * from its position in the buffer list and re-insert it at
 * the head of the list.  If failure, use the last buffer
 * in the list for the desired block. Again, this control
 * block is placed at the head of the list. This process
 * will leave commonly requested blocks in the in-core buffers.
 * Finally, a pointer to the buffer is returned.
 */

char *getblk(address)
	long	address;
{

	register struct buf *bp;
	long	block;
	long	s_err,nbytes;

	block = addr >> BSHIFT;
	if(!override)
		if(block >= fsize) {
			printf("block out of range\n");
			error++;
			return(0);
		}
	for(bp=bhdr.fwd; bp!= &bhdr; bp=bp->fwd)
		if(bp->blkno==block && bp->valid) goto xit;
	bp = bhdr.back;
	bp->blkno = block;
	bp->valid = 0;

	if((s_err = lseek(fd,addr & ~(long)ADRMSK,0)) == -1) {
		error++;
		printf("seek error : %lo\n",addr);
		return(0);
	}
	if((nbytes = read(fd,bp->blkaddr,BSIZE)) != BSIZE) {
		error++;
		printf("read error : addr   = %lo\n",addr);
		printf("           : s_err  = %lo\n",s_err);
		printf("           : nbytes = %lo\n",nbytes);
		return(0);
	}
	bp->valid++;
xit:	bp->back->fwd = bp->fwd;
	bp->fwd->back = bp->back;
	insert(bp);
	return(bp->blkaddr);
}

/*
 * insert - place the designated buffer control block
 * at the head of the linked list of buffers.
 */

insert(bp)
	register struct buf *bp;
{

	bp->back = &bhdr;
	bp->fwd = bhdr.fwd;
	bhdr.fwd->back = bp;
	bhdr.fwd = bp;
}

/*
 * allign - before a get or put operation check the 
 * current address for a boundary corresponding to the 
 * size of the object.
 */

allign(ment)
	short ment;
{

	switch(ment) {
	case 4: if(addr & 01L) break;
		return(0);
	case DIRSIZ: if((addr & 017) != 02) break;
		return(0);
	case 16:
	case 64:
	case 2: if(addr & 01L) break;
	case 3:
	case 1: return(0);
	}
	error++;
	objsz = 1;
	printf("allignment\n");
	return(1);
}

/*
 * err - called on interrupts.  Set the current address
 * back to the last address stored in erraddr. Reset all
 * appropriate flags.  If the prt_flag is set, the interrupt
 * occured while transferring characters to a buffer. 
 * These are "erased" by invalidating the buffer, causing
 * the entire block to be re-read upon the next reference.
 * A reset call is made to return to the main loop;
 */

err()
{
#ifndef STANDALONE
	signal(2,err);
	addr = erraddr;
	error = 0;
	c_count = 0;
	if(char_flag) {
		bhdr.fwd->valid = 0;
		char_flag = 0;
	}
	prt_flag = 0;
	printf("\n?\n");
	fseek(stdin, 0L, 2);
	longjmp(env,0);
#endif
}

/*
 * devcheck - check that the given mode represents a 
 * special device. The IFCHR bit is on for both
 * character and block devices.
 */

devcheck(md)
	register short md;
{
	if(override) return(0);
	if(md & IFCHR) return(0);
	printf("not char or block device\n");
	error++;
	return(1);
}

/*
 * nullblk - return error if address is zero.  This is done
 * to prevent block 0 from being used as an indirect block
 * for a large file or as a data block for a small file.
 */

nullblk(bn)
	long	bn;
{
	if(bn != 0) return(0);
	printf("non existent block\n");
	error++;
	return(1);
}

/*
 * dircheck - check if the current address can be in a directory.
 * This means it is not in the I-list, block 0 or the super block.
 */

dircheck()
{
	unsigned block;

	if(override) return (0);
	if((block = (addr >> BSHIFT)) >= isize) return(0);
	error++;
	printf("block in I-list\n");
	return(1);
}

/*
 * puta - put ascii characters into a buffer.  The string
 * terminates with a quote or newline.  The leading quote,
 * which is optional for directory names, was stripped off
 * by the assignment case in the main loop.  If the type
 * indicates a directory name, the entry is null padded to
 * DIRSIZ bytes.  If more than 14 characters have been given
 * with this type or, in any case, if a block overflow
 * occurs, the current block is made invalid. See the 
 * description for err.
 */

puta()
{
	register char *bptr, c;
	register offset;
	char *sbptr;
	unsigned block;
	long	s_err,nbytes;

	if((sbptr = getblk(addr)) == 0) return;
	char_flag++;
	offset = addr & ADRMSK;
	bptr = sbptr + offset;

	while((c = getc(stdin)) != '"') {
		if(offset++ == BSIZE) {
			bhdr.fwd->valid = 0;
			error++;
			char_flag = 0;
			printf("block overflow\n");
			return;
		}
		if(c == '\n') {
			ungetc(c,stdin);
			break;
		}
		if(c == '\\') {
			switch(c = getc(stdin)) {
			case 't': *bptr++ = '\t'; break;
			case 'n': *bptr++ = '\n'; break;
			case '0': *bptr++ = '\0'; break;
			default: *bptr++ = c; break;
			}
		}
		else *bptr++ = c;
	}
	char_flag = 0;
	if(type == DIR) {
		c = offset - (addr & ADRMSK);
		if(c > DIRSIZ) {
			bhdr.fwd->valid = 0;
			error++;
			char_flag = 0;
			printf("name too long\n");
			return;
		}
		while(c++ < DIRSIZ) *bptr++ = '\0';
	}
	if((s_err = lseek(fd,addr & ~(long)ADRMSK,0)) == -1) {
		error++;
		printf("seek error : %lo\n",addr);
		return(0);
	}
	if((nbytes = write(fd,sbptr,BSIZE)) != BSIZE) {
		error++;
		printf("write error : addr   = %lo\n",addr);
		printf("           : s_err  = %lo\n",s_err);
		printf("           : nbytes = %lo\n",nbytes);
		return(0);
	}
}

/*
 * fprnt - print data as characters, octal or decimal words, octal
 * bytes, directories or inodes	A total of "count" entries
 * are printed. A zero count will print all entries to the 
 * end of the current block.  If the printing would cross a
 * block boundary, the attempt is aborted and an error returned.
 * This is done since logically sequential blocks are generally
 * not physically sequential.  The error address is set
 * after each entry is printed.  Upon completion, the current
 * address is set to that of the last entry printed.
 */

fprnt(style,count)
	register char style;
	register count;
{
	short offset;
	unsigned block;
	char *cptr;
	short *iptr;
	short *tptr;
	long tmp;

	prt_flag++;
	block = addr >> BSHIFT;
	offset = addr & ADRMSK;
	if((cptr = getblk(addr)) == 0){
		ip = 0;
		iptr = 0;
		dirp = 0;
		return;
	}
	ip = ((struct dinode *)cptr);
	erraddr = addr;

	switch (style) {

	case 'c': /* print as characters */
	case 'b': /* or octal bytes */
		if(count == 0) count = BSIZE - offset;
		if(offset + count > BSIZE) break;
		objsz = 1;
		cptr = cptr + offset;
		for(i=0; count--; i++) {
			if(i % 16 == 0)
				printf("\n%6.6lo: ",addr);
			if(style == 'c') putf(*cptr++);
			else printf("%4.4o",*cptr++ & 0377);
			erraddr = addr;
			addr++;
		}
		addr--;
		putc('\n',stdout);
		return;

	case 'd': /* print as directories */
		if(dircheck()) return;
		addr = addr & ~017;
		offset = offset / sizeof(struct direct);
		if(count == 0) count = (BSIZE / sizeof(struct direct)) - offset;
		if(count + offset > (BSIZE / sizeof(struct direct))) break;
		type = DIR; 
		objsz = 16;
		for(dirp = ((struct dir *)cptr) + offset; count--; dirp++) {
			printf("d%d: %4d  ",offset++,dirp->inumb);
			cptr = dirp->nm;
			for(j=0; j<DIRSIZ; j++) {
				if(*cptr == '\0') break;
				putf(*cptr++);
			}
			putc('\n',stdout);
			erraddr = addr;
			addr = addr + sizeof(struct direct);
		}
		addr = erraddr;
		return;

	case 'o': /* print as octal words */
	case 'e': /* print as decimal words */
		addr = addr & ~01;
		offset = offset >> 1;
		iptr = ((short *)cptr) + offset;
		if(count == 0) count = BSIZE / 2 - offset;
		if(offset + count > BSIZE / 2) break;
		objsz = 2;
		for(i=0; count--; i++) {
			if(i % 8 == 0) {

				/*  this code deletes lines of zeros  */
				tptr = iptr;
				k = count -1;	/* always print last line */
				for(j = i; k--; j++)
				if(*tptr++ != 0) break;
				if(j > (i + 7)) {
					j = (j - i) >> 3;
					while(j-- > 0){
						iptr = iptr + 8;
						count = count - 8;
						i = i + 8;
						addr = addr + 16; 
					}
					printf("\n*");
				}

				printf("\n%6.7lo:",addr);
			}
			if(style == 'o')printf("  %6.6o",*iptr++ & 0177777);
			else printf("  %6d",*iptr++);
			erraddr = addr;
			addr = addr + 2;
		}
		addr = erraddr;
		putc('\n',stdout);
		return;

	case 'i': /* print as inodes */
		if(icheck(addr)) return;
		addr = addr & ~077;
		offset = offset / sizeof(struct dinode);
		if(count == 0) count = INOSZ - offset;
		if(count + offset > INOSZ) break;
		type = INODE;
		objsz = 64;
		ip = ip + offset;
		temp = (addr - (BSIZE * 2)) / sizeof(struct dinode) + 1;
		for(i=0; count--; ip++) {
			printf("i#:%6ld  md: ",temp++);
			p = " ugtrwxrwxrwx";
			mode = ip->di_mode;
			switch(mode & IFMT) {
			case IFDIR: putc('d',stdout); break;
			case IFCHR: putc('c',stdout); break;
			case IFBLK: putc('b',stdout); break;
			case IFREG: putc('f',stdout); break;
			case IFIFO: putc('p',stdout); break;
			default: putc('-',stdout); break;
			}
			for(mode = mode << 4; *++p; mode = mode << 1) {
				if(mode & HIBIT) putc(*p,stdout);
				else putc('-',stdout);
			}
			printf("  ln:%5d  uid:%5d  gid:%5d",
				ip->di_nlink,ip->di_uid,ip->di_gid);
			printf("  sz: %8lu\n", ip->di_size);
			if(ip->di_mode & IFCHR) 
				printf("maj:%3.3o  min:%3.3o  ",
					ip->di_addr[1] & 0377, ip->di_addr[0] & 0377);
			else {
				for(i = 0; i < NADDR; i++) {
					l3tol(&tmp,&ip->di_addr[3*i],1);
					printf("a%d:%6lu  ",i,tmp);
					if(i == 6) putc('\n',stdout);
				}
				putc('\n',stdout);
			}
			printf("at: %s",ctime(&ip->di_atime));
			printf("mt: %s",ctime(&ip->di_mtime));
			printf("ct: %s",ctime(&ip->di_ctime));
			if(count) putc('\n',stdout);
			cur_ino = erraddr = addr;
			addr = addr + sizeof(struct dinode);
		}
		addr = erraddr;
		return;


	default: error++;
		printf("no such print option\n");
		return;
	}
	error++;
	printf("block overflow\n");
}

/*
 * reload - read new values for isize and fsize. These are
 * the basis for most of the error checking procedures.
 */

reload()
{
	long saddr;
	short	sv_objsz;

	saddr = addr;
	sv_objsz = objsz;
#if FsTYPE==2
	addr = BSIZE / 2;
#else
	addr = BSIZE;
#endif
	isize = get(2);
#if FsTYPE==2
#ifdef m68k
	addr = BSIZE / 2 + 2;
#else
	addr = BSIZE / 2 + 4;
#endif
#else
	addr = SLOC;
#endif
	fsize = get(4);
	addr = saddr;
	objsz = sv_objsz;
	if(error) printf("cannot read superblock\n");
	else printf("FSIZE = %ld, ISIZE = %u\n", fsize, (isize - 2) * INOSZ);
}

/*
 * bcomp - compare the block numbers of two long addresses.
 * Used to check for block over/under flows when stepping through
 * a file system.
 */

bcomp(addr1,addr2)
	long addr1;
	long addr2;
{
	if(override) return(0);
	if((addr1 & ~(long)ADRMSK) == (addr2 & ~(long)ADRMSK)) return(0);
	error++;
	printf("block overflow\n");
	return(1);
}

/*
 * bmap - maps the logical block number of a file into
 * the corresponding physical block on the file
 * system.  Algorithm is that of the version 7 bmap routine.
 */

long
bmap(bn)
	long	bn;
{

	long	nb;
	int	j, sh, i;

	addr = cur_ino;
	if(bn < NADDR - 3) {
		addr += A0 + bn * 3;
		return(nullblk(nb=get(3)) ? 0L : nb);
	}

	sh = 0;
	nb = 1;
	bn -= NADDR - 3;
	for(j=3; j>0; j--) {
		sh += NSHIFT;
		nb = nb << NSHIFT;
		if(bn < nb)
			break;
		bn -= nb;
	}
	if(j==0) {
		error++;
		printf("file too big\n");
		return(0L);
	}
	addr += A0 + (NADDR - j) * 3;
	if(nullblk(nb=get(3)))
		return(0L);
	for(; j<=3; j++) {
		sh -= NSHIFT;
		addr = (nb << BSHIFT) + 4 * ((bn >> sh) & NMASK);
		if(nullblk(nb=get(4)))
			return(0L);
	}
	return(nb);
}
