//*****************************************************************************
//
// Program:     Shell Project -- part 3
//
// Author:      Travis Dillon - I only wrote the doline function
// Class:       CS 442
// Email:       tdillon@ace.cs.ohiou.edu
//
// Description: this is a simple shell that implements pipelines
//
// Date:        October 14, 2004
//
//*****************************************************************************


#include <string.h> 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "parser.h"
#include "bash.h"
#define MAX_DIR 1000
#define STDIN 0
#define STDOUT 1
#define STDERR 2
//static int line_num = 1;

int main(int argc, char *argv[])
{
   yydebug = 1;  /* turn on ugly YACC debugging */
   yydebug = 0;  /* turn off ugly YACC debugging */

   printf("\nߪsh%% ");  

   /* parse the input file */
   yyparse();

   exit(0);
}



//*****************************************************************************
//
// Function:   doline
//
// Purpose:    Simple but useful shell.  exit, pwd, cd, and execution of cmds
//
// Parameters: *pcmd - link lists of command structs
//
//
//*****************************************************************************



void doline(struct command *pcmd)
{
   char *pwd = getenv("PWD");
   char *home = getenv("HOME");
   static char temp[MAX_DIR],temp2[MAX_DIR];

   if(strcmp(pcmd -> command , "exit") == 0)
   {  //exit from the bash shell
      printf("exit\n");
      exit(EXIT_SUCCESS);
   }

   if(strcmp(pcmd -> command , "cd") == 0)
   {  //we are going to change the current directory
      if(pcmd -> argc == 1)
      {  //change to home directory
         if(chdir(home)) perror("");
         else
         {
            strcpy(temp2,"PWD=");
            strncat(temp2,home,MAX_DIR);
            if(!(putenv(temp2)));
            else perror("");
         }     
         goto DONE;
      }
      else
      {  //change to a specified directory
         if(strcmp(pcmd -> argv[1], "..") == 0)
         {  //go to a parent directory
            char* x = strdup(pwd);
            strcpy(temp2,x);
            x = strrchr(temp2,47);
            *x = '\0';
            if(!chdir(temp2))  //make sure there is a parent directory
            {
               strcpy(temp, "PWD=");
               strncat(temp,temp2,MAX_DIR);
               if(!(putenv(temp)));
               else perror("");
            }
            else
            {
               perror("");
            }
            goto DONE;
         }
         //go to child directory specified by argv[1]
         strcpy(temp,pwd);
         strncat(temp,"/",MAX_DIR);
         strncat(temp,pcmd -> argv[1], MAX_DIR);
         if(!chdir(temp))  //check for a valid directory
         {  strcpy(temp2,"PWD=");
            strncat(temp2,temp,MAX_DIR);
            if(!(putenv(temp2)));
            else perror("");
         }
         else
         {  //no such directory
            perror("");
         }
         goto DONE;
      }
   }

   if(strcmp(pcmd -> command , "pwd") == 0)
   {  //output the current working directory
      printf(pwd);
      printf("\n");
      goto DONE;
   }

   int status = 0, pid, fd0 = -1, fd1 = -1, fd2 = -1, temp_in = dup(STDIN),
       temp_out = dup(STDOUT), temp_error = dup(STDERR);
   
   if(pcmd -> infile != NULL)
   {  //stdin is directed
      fd0 = open(pcmd -> infile, O_RDWR, 0644);
      if(fd0 < 0) perror("");
   }
   else
   {  //input from standard in
      fd0 = dup(STDIN);
      if(!(fd0 >= 0)) perror("");
   }

   if(pcmd -> errfile != NULL)
   {  //stderr is directed      
      if(pcmd -> error_append)
      {
         fd2 = open(pcmd -> errfile, O_APPEND | O_CREAT | O_WRONLY, 0644);
      }
      else
      {
         fd2 = open(pcmd -> errfile, O_WRONLY | O_CREAT, 0644);
      }
   }

   while(pcmd != NULL)
   {  //for all simple commands
      dup2(fd0, STDIN);
      if(close(fd0) == -1) perror("");
      if(pcmd -> nextpipe == NULL)
      {  //last command check for output redirection
         if(pcmd -> outfile != NULL)
         {  //output redirection
            if(pcmd -> output_append)
            {
               fd1 = open(pcmd -> outfile, O_APPEND | O_WRONLY | O_CREAT, 0644);
            }
            else
            {
               fd1 = open(pcmd -> outfile, O_WRONLY | O_CREAT, 0644);
            }
         }
         else
         {  //no redirection, output to standard out
            fd1 = dup(temp_out);
            if(!(fd1 >= 0)) perror("");
         }
      }
      else
      {//not last simple command
         int fdpipe[2];
         if(pipe(fdpipe) == -1) perror("");
         fd0 = fdpipe[STDIN];  //hook up input side of pipe
         fd1 = fdpipe[STDOUT];  //hook up output side of pipe
      }
      if(!(dup2(fd1,STDOUT) >= 0)) perror("");  //standard out to out of pipe
      if(close(fd1) == -1) perror("");
      pid = fork();
      if(pid == -1) perror("");

      if(pid)
      {//parent
         pid = wait(&status);
      }
      else
      {//child         
         int pid2 = fork();  //fork in child to eliminate zombies
         if(pid2 == -1) perror("");
         if(pid2)
         {
            if(pcmd -> is_back)
            {  //immediately exit if process runs in back ground
               exit(status);
            }
            else
            {  //wait for not back ground processes
               pid = wait(&status);
               exit(status);
            }
         }
         else
         {
            if(execvp(pcmd -> command,pcmd -> argv) == -1)  //execute process
            {  //check for valid file
               perror("");
            }
            exit(status);
         }
      }
      pcmd = pcmd -> nextpipe;
   }  //end while done with all pipes   
   if(!(dup2(temp_in, STDIN) >= 0)) perror("");  //reestablish standard in
   if(!(dup2(temp_out, STDOUT) >= 0)) perror("");  //reestablish standard out
   if(!(dup2(temp_error, STDERR) >= 0)) perror("");  //reestablish standard err
   DONE:;
   printf("ߪsh%% ");
} /* end of doline(..) */