#include <stdio.h>
#include <types.h>
#include <stat.h>
#include <dir.h>
#include <filesys.h>
#include <signal.h>

#define tty 1

char line[128];
int pid;
int exchild;
int chstat;
int bkround;
char cmd[30], arg1[30], arg2[30], arg3[30], arg4[30], arg5[30];

extern int errno;

char *envp[] = { "HOME=/", "PATH=:/bin:/usr/bin:/", 0};

_main(argc,argval)
int argc;
char *argval[];
{
    int rdev;
    int count;
    int interact;
    struct filesys fsys;
    int nread;
    int j;
    FILE *fp;
    char *argv[7];

    char *fgets();

    interact = 1;
    if (argc == 2)
    {
    	fp = fopen(argv[1],"r");
        if (fp)
 	{
	    interact = 0;
		pline("not interactive\n");
	}
	else
	    exit(98);
    }
    else
 	fp = stdin;
    
    if (interact)
	signal(SIGINT,SIG_IGN);

    for(;;)
    {
	if (interact)
	    pline("> ");

	if (fgets(line,sizeof(line),fp) == NULL)
	    exit(0);

	cmd[0] = '\0';
	*arg1 = '\0';
	arg2[0] = '\0';
	arg3[0] = '\0';
	arg4[0] = '\0';
	arg5[0] = '\0';
	count = sscanf(line,"%s %s %s %s %s %s",cmd, arg1, arg2, arg3);
	if (count == 0 || cmd[0] == '\n')
	    continue;

	sync();

	if (cmd[0] == '\0')
	    continue;

	if (strcmp(cmd,"ls") == 0)
	{
	    if (*arg1 )
		ls(arg1);
	    else
		ls(".");
	}
	else if (strcmp(cmd,"exit") == 0)
	{
	    _exit(0);
	}
	else if (strcmp(cmd,"cd") == 0)
	{
	    if (*arg1 )
	    {
		if (chdir(arg1) != 0)
		{
		    perror("cd");
		}
	    }
	}
	else if (strcmp(cmd,"mkdir") == 0)
	{
	    if (*arg1 )
		mkdir(arg1);
	}
	else if (strcmp(cmd,"mknod") == 0)
	{
	    if (*arg1  && *arg2 && *arg3 )
		_mknod(arg1,arg2, arg3);
	}
	else if (strcmp(cmd,"type") == 0)
	{
            if (*arg1 )
		type(arg1);
	}
	else if (strcmp(cmd,"dump") == 0)
	{
            if (*arg1 )
		fdump(arg1);
	}
	else if (strcmp(cmd,"rm") == 0)
	{
            if (*arg1 )
		_unlink(arg1);
	}
	else if (strcmp(cmd,"df") == 0)
	{
	    for (j=0; j < 32; ++j)  /* Arbirtary upper limit */
	    {
		if (getfsys(j,&fsys)==0)
		{
		    sprintf(line,"%d:   %u blks used %u blks free %u inodes used %u inodes free\n",
		    j,(fsys.s_fsize - fsys.s_isize)-fsys.s_tfree, fsys.s_tfree,
		    (8*(fsys.s_isize-2)-fsys.s_tinode), fsys.s_tinode);
		    pline(line);
		}
	    }
	}
	else if (strcmp(cmd,"rmdir") == 0)
	{
            if (*arg1 )
		rmdir(arg1);
	}
	else if (strcmp(cmd,"mount") == 0)
	{
	    if (*arg1 && *arg2)
		if (mount(arg1,arg2,0) != 0)
		{
		    perror("mount");
		}
	}
	else if (strcmp(cmd,"umount") == 0)
	{
	    if (*arg1)
		if (umount(arg1) != 0)
		{
		    perror("umount");
		}
	}
	else if (strcmp(cmd,"errno") == 0)
	{
		perror();
	}
	else if (strcmp(cmd,"setuid") == 0)
	{
            if (*arg1 )
		if (setuid(atoi(arg1)))
		    pline("Setuid error.\n");
	}
	else
	{
	    argv[0] = cmd;
	    argv[1] = *arg1 ? arg1 : NULL;
	    argv[2] = *arg2 ? arg2 : NULL;
	    argv[3] = *arg3 ? arg3 : NULL;
	    argv[4] = *arg4 ? arg4 : NULL;
	    argv[5] = *arg5 ? arg5 : NULL;
	    argv[6] = (char *)NULL;

	    bkround = 0;
            if (argv[count-1][0] == '&')
	    {
		bkround = 1;
		argv[count-1] = NULL;
	    }

	    fflush(stdout);
	    fflush(stderr);
	    pid = fork();
	    if (pid == -1)
		perror("sh");
	    else if (pid == 0) /* Child */
	    {
		    if (bkround==0)
		 	signal(SIGINT,SIG_DFL);
		    execve(cmd,argv, envp);
		    if (cmd[0] != '.' && cmd[0] != '/')
	   	    {
			sprintf(line,"/bin/%s",cmd);
			execve(line,argv, envp);
			sprintf(line,"/%s",cmd);
			execve(line,argv, envp);
	    	    }
	  	    perror(cmd);
		    _exit(99);
  	    }
	    else  /* Parent */
	    {
               if (bkround==0)
		    do ; while ((exchild = wait(&chstat)) != -1 && exchild != pid);
	    }

	}
    }
}


pline(line)
char *line;
{
    write(tty,line,(uint16)strlen(line));
}



ls(path)
char *path;
{
    struct direct buf;
    struct stat statbuf;
    char dname[128];
    int d;

    char *ctime();
    
    d = open(path,0);
    if (d < 0)
    {
	perror(path);
	return;
    }

    fstat(d,&statbuf);
    if ((statbuf.st_mode & F_MASK) != F_DIR)
    {
	pline("Not a directory.\n");
	return;
    }

    while (read(d,(char *)&buf,16) == 16)
    {
	if (buf.d_name[0] == '\0')
	    continue;

	if (path[0] != '.' || path[1] )
	{
	    strcpy(dname,path);
	    strcat(dname,"/");
	}
	else
	    dname[0] = '\0';
	strcat(dname,buf.d_name);

	if (stat(dname,&statbuf) != 0)
	{
	    sprintf(line,"ls: can't stat %s",dname);
	    perror(line);
	    continue;
	}

	if ((statbuf.st_mode & F_MASK) == F_DIR)
	    strcat(dname,"/");

	sprintf(line,"%-6d %-15s",
		(statbuf.st_mode & F_CDEV) ?
		    statbuf.st_rdev :
		    512*statbuf.st_size.o_blkno + statbuf.st_size.o_offset,
		dname);
	pline(line);

	sprintf(line,"  0%-6o %-2d %-5d %s\n", statbuf.st_mode, statbuf.st_nlink,
		statbuf.st_ino,ctime(&statbuf.st_mtime));
	pline(line);
    }
    close(d);
}


_mknod(path,modes, devs)
char *path;
char *modes;
char *devs;
{
    int mode;
    int dev;

    mode = -1;
    sscanf(modes,"%o",&mode);
    if (mode == -1 )
    {
	pline("mknod: bad mode\n");
	return(-1);
    }
    
    if ((mode & F_MASK) != F_BDEV && (mode & F_MASK) != F_CDEV)
    {
	pline("mknod: mode is not device\n");
	return(-1);
    }

    dev = -1;
    sscanf(devs,"%d",&dev);
    if (dev == -1 )
    {
	pline("mknod: bad device\n");
	return(-1);
    }

    if (mknod(path,mode,dev) != 0)
    {
	sprintf(line,"mknod: error %d\n",errno);
	pline(line);
	return(-1);
    }

    return(0);
}



mkdir(path)
char *path;
{

    char dot[100];

    if ( mknod(path, 040000 | 0777, 0) != 0)
    {
	sprintf(line,"mkdir: mknod error %d\n",errno);
	pline(line);
	return(-1);
    }

    strcpy(dot,path);
    strcat(dot,"/.");
    if ( link(path,dot) != 0)
    {
	sprintf(line,"mkdir: link dot error %d\n",errno);
	pline(line);
	return(-1);
    }

    strcpy(dot,path);
    strcat(dot,"/..");
    if ( link(".",dot) != 0)
    {
	sprintf(line,"mkdir: link dotdot error %d\n",errno);
	pline(line);
	return(-1);
    }

    return(0);
}


type(arg)
char *arg;
{
    int d;
    char cbuf[512];
    int nread;

    d = open(arg,0);
    if (d < 0)
    {
	sprintf(line,"Cant open file error %d\n",errno);
	pline(line);
   	return(-1);
    }

    for (;;)
    {
	if( (nread = read(d,cbuf,512)) == 0)
	    break;

	write(tty,cbuf,nread);
    }
    close(d);
    return(0);
}


fdump(arg)
char *arg;
{
    int d;
    char cbuf[512];
    int nread;

    pline("Dump starting.\n");
    d = open(arg,0);
    if (d < 0)
    {
	sprintf(line,"Cant open unix file error %d\n",errno);
	pline(line);
   	return(-1);
    }

    for (;;)
    {
	if( (nread = read(d,cbuf,512)) == 0)
	    break;
    }
    close(d);
    sprintf(line,"Dump done.\n");
    pline(line);
    return(0);
}




_unlink(path)
char *path;
{
    struct stat statbuf;

    if (stat(path,&statbuf) != 0)
    {
        sprintf(line,"unlink: can't stat %s",path);
	perror(line);
	return(-1);
    }

    if ((statbuf.st_mode & F_MASK) == F_DIR)
    {
	sprintf(line,"unlink: %s directory\n",path);
	pline(line);
	return(-1);
    }

    if (unlink(path) != 0)
    {
	sprintf(line,"_unlink: unlink error %d\n",errno);
	pline(line);
	return(-1);
    }

    return(0);
}


rmdir(path)
char *path;
{
    struct stat statbuf;
    char newpath[100];
    struct direct dir;
    int fd;

    if (stat(path,&statbuf) != 0)
    {
        sprintf(line,"rmdir: can't stat %s",path);
	perror(line);
	return(-1);
    }

    if (statbuf.st_mode & F_DIR == 0)
    {
	sprintf(line,"rmdir: %s not directory\n",path);
	pline(line);
	return(-1);
    }

    if((fd = open(path,0)) < 0) {
	    sprintf(line,"rmdir: %s unreadable\n", path);
	    pline(line);
	    return(-1);
    }
    while(read(fd, (char *)&dir, sizeof (dir)) == sizeof (dir)) {
	    if(dir.d_ino == 0) continue;
	    if(!strcmp(dir.d_name, ".") || !strcmp(dir.d_name, ".."))
		    continue;
	    sprintf(line,"rmdir: %s not empty\n", path);
	    pline(line);
	    close(fd);
	    return(-1);
    }
    close(fd);

    strcpy(newpath,path);
    strcat(newpath,"/.");
    if (unlink(newpath) != 0)
    {
	sprintf(line,"rmdir: can't unlink \".\"  error %d\n",errno);
	pline(line);
	return(-1);
    }

    strcat(newpath,".");
    if (unlink(newpath) != 0)
    {
	sprintf(line,"rmdir: can't unlink \"..\"  error %d\n",errno);
	pline(line);
	return(-1);
    }

    if (unlink(path) != 0)
    {
	sprintf(line,"rmdir: unlink error %d\n",errno);
	pline(line);
	return(-1);
    }

    return(0);
}

/* Get string from buffered file */
char *fgets(s, maxsize, fp)
char *s;
FILE *fp;
	{
	register int c, size;
	register char *ps, *q;

	ps = s;
	while (--maxsize > 0 && (c = getc(fp)) != EOF)
		if ((*ps++ = c) == '\n')
			break;
	*ps = '\0';
	return ((c == EOF && ps == s) ? NULL : s);
	}
