//*****************************************************************************
//
// Program:     Shell Project -- part 4
//
// Author:      Travis Dillon - I only wrote the doline & run_server function
//                              I modified some code from SCO
// Class:       CS 442
// Email:       tdillon@ace.cs.ohiou.edu
//
// Description: this is a simple shell that implements pipelines
//
// Date:        November 01, 2004
//
//*****************************************************************************


#include <string.h>
#include <strings.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 <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include "parser.h"
#include "bash.h"
#define MAX_DIR 1000
#define MAX_STRING 300
#define NUM_CLIENT 5
#define STDIN 0
#define STDOUT 1
#define STDERR 2
#define PORTNUM 2525

void run_server();

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

   int server_pid;
   server_pid = fork();
   if(server_pid == -1) perror("");

   if(server_pid)
   {//parent
   }
   else
   {//child
      run_server();
      exit(EXIT_SUCCESS);
   }

   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;
   }

   if(strcmp(pcmd -> command , "chat") == 0)
   {  //this is the client for the chat
      int s;  //server socket
      struct sockaddr_in sin;
      struct hostent *hp;
      char buf[MAX_STRING];

      if(pcmd -> argc != 3)
      {
         printf("format -> chat 'chat message' computer_name ");
         exit(1);
      }

      if((hp = gethostbyname(pcmd -> argv[2])) == 0)
      {
         perror("gethostbyname: ");
         exit(1);
      }

      bzero(&sin, sizeof(sin));
      bcopy(hp -> h_addr, &sin.sin_addr, hp -> h_length);
      sin.sin_family = hp -> h_addrtype;
      sin.sin_port = htons(PORTNUM);  //portability

      if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
      {  //create socket
         perror("socket: ");
         exit(1);
      }

      if(connect(s, (struct sockaddr *) &sin, sizeof(sin)) == -1)
      {  //make a connection
         perror("connect: ");
         exit(1);
      }

      struct passwd* user_pw = getpwuid(getuid());   //get this users real name
      char out_line[MAX_STRING];  //send this to server
      size_t j, k, l;  //counter variables
      char h_name[MAX_STRING];  //name of this computer
      gethostname(h_name, MAX_STRING);

      for(j = 0; pcmd -> argv[1][j] != '\0'; ++j)
      {  //the message
         out_line[j] = pcmd -> argv[1][j];
      }
      out_line[j++] = ' ';
      out_line[j++] = 'f';
      out_line[j++] = 'r';
      out_line[j++] = 'o';
      out_line[j++] = 'm';
      out_line[j++] = ' ';
      for(l = 0; user_pw -> pw_gecos[l] != '\0'; ++l)
      {  //the user name
         out_line[j++] = user_pw -> pw_gecos[l];
      }
      out_line[j++] = ' ';
      out_line[j++] = 'a';
      out_line[j++] = 't';
      out_line[j++] = ' ';
      for(k = 0; h_name[k] != '\0'; ++k)
      {  //the computer name
         out_line[j++] = h_name[k];
      }
      out_line[j++] = '\n';
      out_line[j++] = '\0';

      sprintf(buf, out_line);
      if(write(s, buf, sizeof(buf)) == -1)  //send text to server
      {
         perror("write: ");
         exit(1);
      }
      close(s);
      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(..) */


//****************************************************************************
//
// Function:   run_server
//
// Purpose:    to receive a message from a different computer running the
//             same shell
//
// Parameters: none
//
//****************************************************************************


void run_server()
{
   int s, ns, pid;  //s = orig server socket, ns = new socket
   struct sockaddr_in sin;  //client connecting to server
   char buf[MAX_STRING];
   if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
   {  //create socket
      perror("socket: ");
      exit(EXIT_FAILURE);
   }

   bzero(&sin, sizeof(sin));

   sin.sin_family = AF_INET;  //internet domain
   sin.sin_port = htons(PORTNUM);  //portability
   sin.sin_addr.s_addr = htonl(INADDR_ANY);  //portability

   if(bind(s, (struct sockaddr *) &sin, sizeof(sin)) == -1)
   {  //hook up the socket
      perror("bind: ");
      exit(EXIT_FAILURE);
   }
   if(listen(s, NUM_CLIENT) == -1)
   {  //listen for clients
      perror("listen: ");
      exit(EXIT_FAILURE);
   }
   while(1)
   {
      if ((ns = accept(s, 0, 0)) == -1)
      {  //accept the clients
         perror("accept: ");
         exit(EXIT_FAILURE);
      }

      if ((pid = fork()) == -1)
      {
         perror("fork: ");
         exit(1);
      }
      if (pid == 0)
      {
         while(1)
         {
            if ((pid = read(ns, buf, sizeof(buf))) == -1)
            {  //open read
               perror("read: ");
               exit(1);
            }
            if (pid < sizeof(buf)) break;
            printf("\nMessage: %s", buf);  //ouput message
         }
         close(ns);
         printf("ߪsh%% ");
         exit(EXIT_SUCCESS);
      }
      close(ns);
   }
}  //end of run_server