#
/* v6-- accesses version 6 filesystems and copies files to	*/
/*	version 7 filesystems					*/
/*		Mike Karels, Dept. of Molecular Biology		*/
/*		University of California, Berkeley		*/

#include "inode.h"
#include "filsys.h"
#include <signal.h>
#include <stdio.h>

#define	BLKSIZE	512
#define	MAXLINE 80		/* maximum input line length */
#define NARG	3	/* maximum number of arguments */
#define MAXARG	30	/* maximum size of input args */
#define	MAXSIZE	BLKSIZE/sizeof(struct direct)*8	/* max number of  
					* directory entries	*/
#define	DIRSIZE	14
#ifndef SUPOFFSET
#define SUPOFFSET	0	/* number of blocks before super block */
#endif

struct filsys sb;	/* superblock */
struct inode dinode, finode;	/* current dir, file inodes */
struct direct 
{
	short	d_ino;
	char	d_name[DIRSIZE];
} dir[MAXSIZE], cur_dir, root = { 1, "/" };
int	dir_sz, fd, fo, imax, rflag;
char	*cmd[] = { "ls", "cd", "cp", "cat", "?", "", "q", "cpdir",
		"c7d", "printi", "printblk", "cpblk", "readdir" };
#define NCMD	13	/* number of commands in cmd[] */




main(argc,argv)
int argc;
char *argv[];
{
	extern struct direct dir[], root, cur_dir;
	extern struct inode dinode;
	extern int fd;
	extern char *cmd[];
	char linein[MAXLINE], arg[NARG][MAXARG];
	int i, count, null();
	long lseek();

	if (argc < 2) {
		fprintf(stderr,"Usage: v6 file, where file is block special\n");
		exit(1);
	}
	if (strcmp(argv[1],"-r") == 0) {
		rflag = 1;
		argv++;
	}
	if ((fd=open(argv[1],0)) < 0) {
		fprintf(stderr,"can't open %s\n",argv[1]);
		exit(1);
	}
	if (rflag)
		imax = 32000;
	else {
		if ((lseek(fd, SUPOFFSET * (long)BLKSIZE, 0) < 0) ||
		   (read(fd,&sb,BLKSIZE) < BLKSIZE)) {
			fprintf(stderr,"can't read superblock\n");
			exit(1);
		   }
		printf("isize=%d\n", sb.isize);
		printf("fsize=%d\n", sb.fsize);
		imax = BLKSIZE / sizeof(struct inode) * (sb.isize-1);
		if (readdir(&root) < 0) exit(1);
		cur_dir.d_ino = root.d_ino;
		strcpy(cur_dir.d_name,root.d_name);
	}
	signal(SIGINT,SIG_IGN);

	while(1) {
		printf("* ");
		if (getline(linein) == EOF) exit(0);
		for (i=0; i<NARG; i++) arg[i][0] = '\0';
		sscanf(linein,"%s %s %s",arg[0],arg[1],arg[2]);
		for (i = 0; (i<NCMD) && (strcmp(arg[0],cmd[i])); i++)  ;
		switch(i) {
			case 0: ls(arg[1],arg[2]);
				break;
			case 1: cd(arg[1]);
				break;
			case 2: cp(arg[1],arg[2],1);
				break;
			case 3: cat(arg[1]);
				break;
			case 4: msg();
				break;
			case 5: break;
			case 6: exit(0);
			case 7: cpdir(arg[1]);
				break;
			case 8: if (chdir(arg[1]) < 0)
				perror("c7d");
				break;
			case 9: printi(atoi(arg[1]));
				break;
			case 10: printblk(atoi(arg[1]));
				break;
			case 11: cpblk(atoi(arg[1]),arg[2]);
				break;
			case 12: readroot(atoi(arg[1]));
				break;
			case NCMD: if (*arg[0] == '!') {
					signal(SIGINT,null);
					system(linein+1);
					signal(SIGINT,SIG_IGN);
				}
				else fprintf(stderr,"invalid command\n");
				break;
		}
	}
}

getline(linein)		/* reads input line into linein */
char linein[]; {
	int i, c;

	for (i=0; i<MAXLINE-1 && (c=getchar()) != '\n' && c != EOF;
		i++) linein[i] = c;
	if (c == '\n') {
		linein[i] = '\0';
		return(i);
	}
	else if (c == EOF) return(EOF);
	else {
		fprintf(stderr,"input line too long\n");
		fflush(stdin);
		linein[0] = '\0';
		return(0);
	}
}

null() {}		/* trap here on interrupt during system() */

ls(arg1,arg2)	/* list directory 'name', current dir if no name */
char *arg1, *arg2; {		/* -i option gives inode numbers */
	int i, j, flag, iflag, col;
	char *name;
	extern struct direct dir[], cur_dir;
	extern int dir_sz;
	struct direct save_dir;

	if (strcmp(arg1,"-i") == 0) {
		iflag = 1;
		col = 3;
		name = arg2;
	} else {
		iflag = 0;
		col = 4;
		name = arg1;
	}
	flag = 0;
	if (name[0] != '\0') {
		flag = 1;
		save_dir.d_ino = cur_dir.d_ino;
		strcpy(save_dir.d_name,cur_dir.d_name);
		if (cd(name) < 0) return;
	}
	j=0;
	for (i=0; i<dir_sz; i++)
		if (dir[i].d_ino != 0 ) 
		   if (iflag)
			printf("%6d %-15.14s%c", dir[i].d_ino, dir[i].d_name,
				++j%col ? ' ':'\n');
		   else
			printf("%-15.14s%c", dir[i].d_name,
				++j%col ? ' ':'\n');
	if (j%col) putchar('\n');
	if (flag) {
		readdir(&save_dir);
		cur_dir.d_ino = save_dir.d_ino;
		strcpy(cur_dir.d_name,save_dir.d_name);
	}
}
cpdir(arg)	/* copy contents of current directory */
char *arg; {
	int i;
	extern struct direct dir[];
	extern int dir_sz;

	if (arg[0] != '\0') {
		fprintf(stderr,"no arguments allowed for cpdir\n");
		return(-1);
	}
	for (i=0; i<dir_sz; i++)
		if ((dir[i].d_ino != 0) && (dir[i].d_name[0] != '.')) 
			cp(dir[i].d_name,dir[i].d_name,1);
	return(0);
}

cd(name)		/* returns 0 if successful, else -1 */
char *name; {	/* returns to previous directory if unsuccesssful */
	extern struct inode dinode;
	extern struct direct dir[], cur_dir, root;
	extern int dir_sz;
	int i, ino;
	char *c, *rindex();
	struct direct *pdir, *find(), save_dir;

	if ((i=iindex(name,'/')) >= 0) {
		if (i==0) {
			readdir(&root);
			name++;
		}
		if (name[0]=='\0') {
			cur_dir.d_ino = root.d_ino;
			strcpy(cur_dir.d_name,root.d_name);
			return(0);
		}
		if (*((c=rindex(name,'/'))+1) == '\0') *c = '\0';
			/* removes trailing '/' if present */
		while ((i=iindex(name,'/')) != -1) {
			name[i] = '\0';
			if ((pdir=find(name)) == NULL) {
				fprintf(stderr,"can't find %s\n",name);
				readdir(&cur_dir);
				return(-1);
			}
			if (readdir(pdir) < 0) return(-1);
			name += i+1;
		}
	}
	if ((pdir=find(name))==NULL) {
		fprintf(stderr,"can't find %s\n",name);
		readdir(&cur_dir);
		return(-1);
	}
	ino = pdir->d_ino;
	if (readdir(pdir) >= 0) {
		cur_dir.d_ino = ino;
		strcpy(cur_dir.d_name,name);
		return(0);
	}
	else return(-1);
}

iindex(s,c)
char *s, c; {
	int i;

	for (i=0; ; i++) {
		if (s[i] == c) return(i);
		if (s[i] == NULL) return(-1);
	}
}

struct direct *find(name)	/* returns pointer to "name" entry */
char *name; {			/* in dir[], NULL if not found */
	extern struct direct dir[];
	int i;
	extern int dir_sz;

	for (i=0; i<dir_sz; i++)
		if ((strcmp(dir[i].d_name,name) == 0) &&
			(dir[i].d_ino != 0))
			break;
	if (i==dir_sz) return(NULL);
	return(&(dir[i]));
}

#define MODE 0644
#define STDOUT 1
char	buf[BLKSIZE];

cp(ifile,ofile,mode)	/* copies v6 ifile to v7 ofile if mode 1 */
char *ifile, *ofile;	/* cats ifile if mode 0 */
int mode;
{
	extern struct inode finode;
	extern struct direct dir[], cur_dir;
	extern int fd,fo;
	int n, i, j, blk, ino, flag;
	unsigned short indir[BLKSIZE/2];
	long size;
	char *tname;
	struct direct *pdir, save_dir;

	flag = 0;
	if ((ino = atoi(ifile)) > 0) {
		if (readi(ino,&finode) < 0) return;
	} else {
		if (iindex(ifile,'/') != -1) {
			flag = 1;
			save_dir.d_ino = cur_dir.d_ino;
			strcpy(save_dir.d_name,cur_dir.d_name);
			tname = rindex(ifile,'/');
			if (tname == ifile)	/* file in root directory */
				ifile = root.d_name;
			*tname = '\0';
			if (cd(ifile) < 0) return;
			ifile = tname + 1;
		}
		if ((pdir = find(ifile)) == NULL) {
			fprintf(stderr,"can't find %s\n",ifile);
			goto quit;
		}
		if (readi(pdir->d_ino,&finode) < 0) goto quit;
	}
	if (ofile[0] == '\0' && mode == 1)
		ofile = ifile;
	if ((mode==1) && ((fo=open(ofile,0) != -1))) {
		fprintf(stderr,"%s already exists\n",ofile);
		goto quit;
	}
	if (mode == 1)  {
		if ((fo= creat(ofile,MODE)) < 0) {
			fprintf(stderr,"can't create %s\n",ofile);
			goto quit;
		}
	}
	else fo = dup(STDOUT);
	if (finode.flags & (IFCHR | IFBLK)) {	
		/* is special file or directory */
		fprintf(stderr,"cp: %s not a regular file\n",ifile);
		goto quit;
	}
	size = finode.size1 + ((long)(finode.size0 & 0377) << 16);
	if (finode.flags & IFLRG)	/* file is large */
	   for (blk=0; size>0; blk++) {
		if (blk == 7) {
			fprintf(stderr,"%s is huge file, cp incomplete\n",
				ifile);
			goto quit;
		}
		if (readblk(finode.addr[blk],indir) < BLKSIZE)
			goto quit;
		for (i=0; ((i<BLKSIZE/2) && (size > 0)); i++) {
			if (indir[i]==0)
				for (j=0; j<BLKSIZE/2; j++)
				buf[j] = 0;
			else if (readblk(indir[i],buf) < BLKSIZE)
				goto quit;
			n = (size < BLKSIZE) ? size : BLKSIZE;
			if (write(fo,buf,n) != n) {
				if (mode == 1)
					fprintf(stderr,"write error\n");
				else putchar('\n');
				goto quit;
			}
			size -= BLKSIZE;
		}
	   }
	else for (blk=0; size>0 && blk<8; blk++) {
		if (finode.addr[blk]==0)
			for (j=0; j<BLKSIZE/2; j++)
			buf[j] = 0;
		else if (readblk(finode.addr[blk],buf) < BLKSIZE)
			goto quit;
		n = (size<BLKSIZE) ? size : BLKSIZE;
		if (write(fo,buf,n) != n) {
			if (mode == 1) fprintf(stderr,"write error\n");
			else putchar('\n');
			goto quit;
		}
	size -= BLKSIZE;
	}
quit:	if (fo != STDOUT) close(fo);
	if (flag) {
		readdir(&save_dir);
		cur_dir.d_ino = save_dir.d_ino;
		strcpy(cur_dir.d_name,save_dir.d_name);
	}
}

readdir(pdir)		/* reads pdir->d_inode into dinode, then */
struct direct *pdir;	/* reads corresponding directory into dir */
{			/* reads cur_dir on error, then returns -1 */
	extern struct inode dinode;
	extern struct direct dir[], cur_dir;
	extern int dir_sz, fd;
	char buf[BLKSIZE];
	int blk, sz, i, n;
	long size;

	if (pdir->d_ino == 0) return(-1);
	if (readi(pdir->d_ino,&dinode) < 0) return(-1);
	if (!(dinode.flags & IFDIR)) {	/* not a directory */
		fprintf(stderr,"%s not a directory\n",pdir->d_name);
		readdir(&cur_dir);
		return(-1);
	}
	if (dinode.flags & IFLRG) {	/* file is large */
		fprintf(stderr,"Ouch, %s is large\n",pdir->d_name);
		readdir(&cur_dir);
		return(-1);
	}
	size = dinode.size1 + (long)(dinode.size0 & 0377) << 8;
	sz = 0;
	n = BLKSIZE/sizeof(struct direct);
	for (blk=0; size>0 && blk<8; blk++)
		if (dinode.addr[blk] > 0) {
			if (readblk(dinode.addr[blk],&dir[sz])<BLKSIZE)
				return(-1);
			sz += (size<BLKSIZE ? size : BLKSIZE)
				/sizeof(struct direct);
			size -= BLKSIZE;
		}
	dir_sz = sz;
	return(dir_sz);
}

readi(inum,pinode)	/* reads inode inum into *pinode */
int inum;
struct inode *pinode; {
	extern int fd, imax;
	long lseek();

	if (inum < 1 || inum > imax) {
		fprintf(stderr,"bad inode number, %d\n",inum);
		return(-1);
	}
	if ((lseek(fd,((long)((1 + SUPOFFSET)*BLKSIZE) +
		(long)(inum-1)*(sizeof(struct inode))),0) < 0) ||
	(read(fd,pinode,sizeof(struct inode)) < 0)) {
		fprintf(stderr,"can't read inode %d\n",inum);
		return(-1);
	}
	return(0);
}

readblk(blk,buf)	/* reads block blk into buf */
int blk;
char *buf; {
	extern struct filsys sb;
	extern int fd;
	long lseek();
	int n;

	blk += SUPOFFSET - 1;
	if (!rflag && ((blk < sb.isize-1) || (blk >= sb.fsize))) {
		fprintf(stderr,"bad block number, %d\n",blk);
		return(-1);
	}
	if (lseek(fd,(long)(BLKSIZE)*(long)(blk),0) < 0) {
		fprintf(stderr,"seek error, block %d\n",blk);
		return(-1);
	}
	if ((n=read(fd,buf,BLKSIZE)) != BLKSIZE)
		fprintf(stderr,"read error, block %d\n",blk);
	return(n);
}

cat(ifile)		/* prints ifile on terminal, using cp */
char *ifile; {

	int flush();
	signal(SIGINT,flush);
	cp(ifile,NULL,0);	/* mode 0 signals print on tty */
	signal(SIGINT,SIG_IGN);
}

flush() {		/* closes fo to terminate a cat() */
	extern int fo;
	close(fo);
	return;
}

msg() {
	printf("commands:\n");
	printf("\t ls [-i] [dir]: list directory contents, current dir default\n");
	printf("\t cd name: change to (v6) directory 'name'\n");
	printf("\t cat name: print (v6) file 'name' on terminal\n");
	printf("\t cp name1 [name2]: copy (v6) 'name1' to (v7) file 'name2'\n");
	printf("\t\tname2 defaults to name1.");
	printf("\t cpdir: copy all files in current v6 directory\n");
	printf("\t\tto current v7 directory\n");
	printf("\t q or ^d: quit\n");
}

printi(inum)
{
	int i;
	if (readi(inum,&finode) < 0) return;
	printf("flags: %o\tnlinks: %d\tuid: %d\tgid: %d\n",
		finode.flags,finode.nlinks,finode.uid,finode.gid);
	printf("size: %ld\n",
		finode.size1 + ((long)(finode.size0 & 0377) << 16));
	printf("blocks:");
	for (i=0; i<8; i++)
		printf(" %u",finode.addr[i]);
	putchar('\n');
}

printblk(blk)
{
	int count;
	if ((count = readblk(blk,buf)) > 0)
		write(STDOUT,buf,count);
}

cpblk(blk,file)
char *file;	/* copy block 'blk' to end of v7 'file' */
{
	int count;
	if ((count = readblk(blk,buf)) < 0)
		return;
	if ((fo = open(file,1)) < 0)
		if ((fo = creat(file,0644)) < 0) {
			fprintf(stderr,"can't create %s\n",file);
			return;
		}
	lseek(fo,0L,2);
	write(fo,buf,count);
	close(fo);
}

readroot(ino)	/* make root directory that specified by ino */
{
	root.d_ino = ino;
	readdir(&root);
}
