/*
 * runcommand.c
 * File Revision 1
 * Sub-command execution for smallsh.
 * (c) 1999 Addison Wesley Longman, Inc.
 * (c) 2000 Jacob Lundberg, jacob@chaos2.org
 */


/*
 * 2000.11.05	Acquire initial code
 *		Adjust style
 */


#include <signal.h>
#include <stdlib.h>
#include "smallsh.h"


int run_external(char **cline, sh_task_whence where, char *inp, char *oup) {
/*
 * run_external()
 * Execute an external command with an optional wait.
 */

   pid_t pid;
   int status;

   pid = fork();
   switch(pid) {
   case -1:
      /* fork error */
      perror("smallsh:runcommand:fork");
      return(-1);
   case 0:
      /* code for child */
      if(where == SH_TASK_BACK && inp == NULL)
         freopen("/dev/null", "r", stdin);
      if(inp != NULL && freopen(inp, "r", stdin) == NULL) {
         fprintf(stderr, "smallsh: unable to open input file\n");
         fflush(stderr);
         exit(1);
      }
      if(oup != NULL && freopen(oup, "w", stdout) == NULL) {
         fprintf(stderr, "smallsh: unable to open output file\n");
         fflush(stderr);
         exit(1);
      }
      /* Stop trapping signals. */
      signal(SIGINT, SIG_DFL);
      /* Execute the child. */
      execvp(*cline, cline);
      perror(*cline);
      exit(1);
   }

   /* code for parent */
   if(where == SH_TASK_BACK) {
      /* If background process, print pid and return. */
      printf("background pid is %lu\n", pid);
      fflush(stdout);
      return(0);
   }

   /* wait until process pid exits */
   if(waitpid(pid, &status, 0) == -1) 
      return(-1);
   else 
      return(status);

}


int run_exit(char **cline) {
/*
 * run_exit()
 * Exit smallsh.
 */

   /* Exit the shell. */
   exit(0);

   return(0);

}


int run_cd(char **cline) {
/*
 * run_cd()
 * Change the present working directory.
 */

   /* Default to the user's home directory. */
   if(cline[1] == NULL)
      cline[1] = getenv("HOME");

   /* Try and change the dir. */
   if(chdir(cline[1])) {
      fprintf(stderr, "smallsh: unable to cd to %s\n", cline[1]);
      fflush(stderr);
      return(1);
   } else
      return(0);

}


int run_pwd(char **cline, char *oup) {
/*
 * run_pwd()
 * List the present working directory.
 */

   FILE *newout;
   char pwd[1024] = "";

   /* Open the new output stream (if needed). */
   if(oup != NULL) {
      newout = fopen(oup, "w");
      if(newout == NULL) {
         fprintf(stderr, "smallsh: unable to open output file\n");
         fflush(stderr);
         exit(1);
      }
   }

   /* Output the pwd. */
   getcwd(pwd, 1024);
   pwd[1023] = '\0';
   fprintf(oup == NULL ? stdout : newout, "%s\n", pwd);

   /* Close the file stream. */
   if(oup != NULL)
      fclose(newout);

   return(0);

}


int run_status(char **cline, char *oup, int status) {
/*
 * run_status()
 * Show the exit conditions of the last foreground process.
 */

   FILE *newout;

   /* Open the new output stream (if needed). */
   if(oup != NULL) {
      newout = fopen(oup, "w");
      if(newout == NULL) {
         fprintf(stderr, "smallsh: unable to open output file\n");
         fflush(stderr);
         exit(1);
      }
   }

   if(status == -1)
      fprintf(oup == NULL ? stdout : newout, "no foreground processes yet\n");
   else if(WIFEXITED(status))
      fprintf(oup == NULL ? stdout : newout, "exit value %i\n", WEXITSTATUS(status));
   else if(WIFSIGNALED(status))
      fprintf(oup == NULL ? stdout : newout, "terminated by signal %i\n", WTERMSIG(status));
   else if(WIFSTOPPED(status))
      fprintf(oup == NULL ? stdout : newout, "stopped by signal %i\n", WSTOPSIG(status));

   /* Close the file stream. */
   if(oup != NULL)
      fclose(newout);

   return(0);

}


int runcommand(char **cline, sh_task_whence where, char *inp, char *oup) {
/*
 * runcommand()
 * Run a command.
 */

   /* Exit value info for foreground processes. */
   static int status = -1;

   /* Determine how to handle the command (internally / externally). */
   if(!strncmp(cline[0], "#", 1))
      return(0);
   else if(!strncmp(cline[0], "cd", 2))
      return(run_cd(cline));
   else if(!strncmp(cline[0], "exit", 4))
      return(run_exit(cline));
   else if(!strncmp(cline[0], "pwd", 3))
      return(run_pwd(cline, oup));
   else if(!strncmp(cline[0], "status", 6))
      return(run_status(cline, oup, status));
   else
      return(status = run_external(cline, where, inp, oup));

}

