# include "basic.h"

static char *defshell = "/bin/sh";
static char *cmdflag = "-c";

int shell (int argc, char **argv) {
  
  int i, pid;
  int exit_status;
  int wait_status;
  int result, length;
  char **args, *shell;

  shell = getenv ("SHELL");
  if (shell == NULL) shell = defshell;

  // we are creating a command of the form /bin/sh -c argv[1] argv[2] etc, where
  // the argv[1], etc elements are concatenated into a single string
  ALLOCATE (args, char *, 4);
  args[0] = shell;
  args[1] = cmdflag;

  length = 0;
  for (i = 1; i < argc; i++) {
    length += strlen(argv[i]) + 1;
  }
  
  ALLOCATE (args[2], char, length);
  args[2][0] = 0;
  for (i = 1; i < argc; i++) {
    strcat (args[2], argv[i]);
    if (i < argc - 1) strcat (args[2], " ");
  }
  args[3] = NULL;

  // send the commands to the shell specified in the env variable SHELL, or else /bin/sh

  // use execvp to enable a timeout on the system call 
  pid = fork ();
  if (!pid) { /* must be child process */
    execvp (shell, args);
    exit (1);
  }
  free (args);
  
  // wait for process to finish or timeout
  for (i = 0; i < 200; i++) {
    result = waitpid (pid, &wait_status, WNOHANG);
    switch (result) {
      case -1:   // error on waitpid
	switch (errno) {
	  case ECHILD:
	    gprint (GP_ERR, "unknown PID, not a child process: %d\n", pid);
	    return (FALSE);
	  default:
	    gprint (GP_ERR, "unexpected response to waitpid: %d\n", result);
	    abort();
	}
	break;

      case 0:   // child not yet exited
	usleep (10000);
	continue;

      default:
	if (result != pid) {
	  gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
	  abort();
	}
	if (WIFEXITED(wait_status)) {
	  exit_status = WEXITSTATUS(wait_status);
	  if (exit_status) {
	    return FALSE;
	  } else {
	    return TRUE;
	  }
	}
	if (WIFSIGNALED(wait_status)) {
	  gprint (GP_ERR, "job %d exited on signal %d\n", pid, WTERMSIG(wait_status));
	  return (FALSE);
	}
	if (WIFSTOPPED(wait_status)) {
	  gprint (GP_ERR, "waitpid returns 'stopped' programming error\n");
	  abort();
	}
    }
  }

  gprint (GP_ERR, "timeout on %d\n", pid);
  return (FALSE);
}

