/* exec.c (emx+gcc) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <signal.h>
#include <time.h>
#include <sys/wait.h>
#include <errno.h>
#include <os2.h>

static int waitpid_flag;
static int gpid;

static void do_wait (void)
{
  int p, t;

  if (waitpid_flag)
    p = waitpid (gpid, &t, 0);
  else
    p = wait (&t);
  if (p == -1)
    perror ("wait");
  else
    {
      if (WIFEXITED (t))
        fprintf (stderr, "Process %d terminated normally, rc=%d\n",
                 p, WEXITSTATUS (t));
      else if (WIFSTOPPED (t))
        fprintf (stderr, "Process %d stopped by signal %d\n",
                 p, WSTOPSIG (t));
      else
        fprintf (stderr, "Process %d terminated by signal %d\n",
                 p, WTERMSIG (t));
    }
}


static void handler (int sig)
{
  if (sig == SIGCHLD)
    {
      fprintf (stderr, "SIGCHLD: "); fflush (stderr);
      do_wait ();
      fflush (stderr);
      signal (SIGCHLD, SIG_ACK);
    }
  else
    {
      printf ("Signal %d received. Process terminated.\n", sig);
      exit (1);
    }
}


static void cmd_help (void)
{
  puts ("?              Display help");
  puts ("help           Display help");
  puts ("quit           Quit this program");
  puts ("set v=n        Set environment variable V to value N");
  puts ("set +f         Set flag F");
  puts ("set -f         Clear flag F:");
  puts ("               waitpid         Use waitpid() instead of wait()");
  puts ("kill s p       Send signal S to process P");
  puts ("signal s a     Handle signal S, A=d: SIG_DFL, A=i: SIG_IGN");
  puts ("handles n      Set number of file handles (relatively if signed)");
  puts ("clock          Show result of clock()");
  puts ("sleep n        Sleep for N seconds");
  puts ("system c       Run command C using system()");
  puts ("run [<opt>] c  Run program C:");
  puts ("               -a  asynchronously (P_NOWAIT)");
  puts ("               -d  detached (P_DETACH)");
  puts ("               -D  for debugging (P_DEBUG)");
  puts ("               -o  as overlay (P_OVERLAY)");
  puts ("               -s  in a separate session (P_START):");
  puts ("                   -b  in background (P_BACKGROUND)");
  puts ("                   -c  don't close window (P_NOCLOSE)");
  puts ("                   -f  full-screen (P_FULLSCREEN)");
  puts ("                   -m  minimized (P_MINIMIZE)");
  puts ("                   -M  maximized (P_MAXIMIZE)");
  puts ("                   -p  as Presentation Manager program (P_PM)");
  puts ("                   -u  unrelated (P_UNRELATED)");
  puts ("                   -w  windowed (P_WINDOWED)");
  puts ("               -q  quote arguments (P_QUOTE)");
  puts ("               -t  use tilde in 3rd arg string (P_TILDE)");
  puts ("               -A  ask for arguments");
  puts ("               -E  empty environment");
}


static void cmd_set_flag (const char *p, int val)
{
  if (strcmp (p, "waitpid") == 0)
    waitpid_flag = val;
  else
    printf ("set: Unknown flag\n");
}


static void cmd_set (const char *p)
{
  while (*p == ' ')
    ++p;
  if (*p == '+')
    cmd_set_flag (p + 1, 1);
  else if (*p == '-')
    cmd_set_flag (p + 1, 0);
  else if (strchr (p, '=') == NULL)
    printf ("set: Invalid argument\n");
  else if (putenv (strdup (p)) != 0)
    perror ("putenv()");
}


static void cmd_kill (const char *p)
{
  int sig, pid;

  while (*p == ' ')
    ++p;
  if (sscanf (p, "%d %d", &sig, &pid) != 2)
    printf ("kill: Invalid arguments\n");
  else if (kill (pid, sig) < 0)
    perror ("kill()");
}


static void cmd_signal (const char *p)
{
  int sig;
  char act;
  struct sigaction sa;

  while (*p == ' ')
    ++p;
  if (*p == 0)
    {
      for (sig = 1; sig < NSIG; ++sig)
        if (sigaction (sig, NULL, &sa) == 0)
          {
            if (sa.sa_handler == SIG_IGN)
              printf ("%2d SIG_IGN\n", sig);
            else if (sa.sa_handler == SIG_DFL)
              printf ("%2d SIG_DFL\n", sig);
            else
              printf ("%2d %p\n", sig, (void *)sa.sa_handler);
          }
    }
  else if (sscanf (p, "%d %c", &sig, &act) != 2 || (act != 'd' && act != 'i'))
    printf ("signal: Invalid arguments\n");
  else if (signal (sig, (act == 'i' ? SIG_IGN : SIG_DFL)) == SIG_ERR)
    perror ("signal()");
}


static void cmd_run (char *p)
{
  int mode, i, j, ask, empty_env;
  char *q, *r;
  char *nargv[256];
  char *nenvp[1];
  char args[8192];

  while (*p == ' ')
    ++p;
  mode = P_WAIT; ask = 0; empty_env = 0;
  while (*p == '-')
    {
      ++p;
      while (*p != ' ' && *p != 0)
        switch (*p++)
          {
          case 'a':
            mode = P_NOWAIT;
            break;
          case 'd':
            mode = P_DETACH;
            break;
          case 'D':
            mode = P_DEBUG;
            break;
          case 'o':
            mode = P_OVERLAY;
            break;
          case 's':
            mode = P_SESSION;
            break;
          case 'b':
            mode |= P_BACKGROUND;
            break;
          case 'c':
            mode |= P_NOCLOSE;
            break;
          case 'f':
            mode |= P_FULLSCREEN;
            break;
          case 'm':
            mode |= P_MINIMIZE;
            break;
          case 'M':
            mode |= P_MAXIMIZE;
            break;
          case 'p':
            mode |= P_PM;
            break;
          case 'u':
            mode |= P_UNRELATED;
            break;
          case 'w':
            mode |= P_WINDOWED;
            break;
          case 'A':
            ask = 1;
            break;
          case 'E':
            empty_env = 1;
            break;
          case 'q':
            mode |= P_QUOTE;
            break;
          case 't':
            mode |= P_TILDE;
            break;
          default:
            printf ("run: Invalid option %c\n", p[-1]);
            return;
          }
      while (*p == ' ')
        ++p;
    }
  while (*p == ' ')
    ++p;

  i = 0;
  q = p;
  while ((nargv[i++] = strtok (q, " \t")) != NULL)
    q = NULL;
  if (ask)
    {
      printf ("Enter arguments, one per line (. to end):\n");
      q = args; --i;
      for (;;)
        {
          r = fgets (q, sizeof (args) - (q - args), stdin);
          if (r == NULL)
            return;
          r = strchr (q, '\n');
          if (r != NULL)
            *r = 0;
          else
            r = strchr (q, 0);
          if (strcmp (q, ".") == 0)
            break;
          nargv[i++] = q;
          q = r + 1;
        }
      nargv[i] = NULL;
    }
  nenvp[0] = NULL;
  gpid = spawnvpe (mode, p, nargv, (empty_env ? nenvp : environ));
  if (gpid < 0)
    {
      j = _syserrno ();
      perror ("spawnve");
      printf ("syserrno=%d\n", j);
    }
  else
    printf ("rc=%d\n", gpid);
}


static void cmd_handles (const char *p)
{
  LONG req_count;
  ULONG handle_count;
  int i;

  while (*p == ' ')
    ++p;
  if (sscanf (p, "%d", &i) != 1)
    printf ("handles: Invalid argument\n");
  else if (_osmode == OS2_MODE)
    {
      if (p[1] == '+' || p[1] == '-')
        req_count = i;
      else
        {
          if (DosSetMaxFH (i) != 0)
            fprintf (stderr, "DosSetMaxFH failed\n");
          req_count = 0;
        }
      if (DosSetRelMaxFH (&req_count, &handle_count) != 0)
        fprintf (stderr, "DosSetRelMaxFH failed\n");
          printf ("Number of handles: %lu\n", handle_count);
    }
}


static void cmd_sleep (const char *p)
{
  int i;

  while (*p == ' ')
    ++p;
  if (sscanf (p, "%d", &i) != 1)
    printf ("sleep: Invalid argument\n");
  sleep (i);
}


static void cmd_system (const char *p)
{
  int i;

  while (*p == ' ')
    ++p;
  i = system (p);
  if (i == -1)
    perror ("system()");
  else
    printf ("rc=%d\n", i);
}


int main (int argc, char *argv[])
{
  char buf[4096], *p;
  int i;
  
  signal (SIGINT, handler);
  signal (SIGCHLD, handler);
  if (argc >= 2)
    {
      i = spawnvp (P_WAIT, argv[1], &argv[1]);
      if (i < 0)
        perror ("spawnve");
      else
        printf ("rc=%d\n", i);
      return 0;
    }
  waitpid_flag = 0;
  for (;;)
    {
      printf ("exec[%d]: ", (int)getpid ());
      fflush (stdout);
      while (fgets (buf, sizeof (buf), stdin) == NULL)
        {
          if (ferror (stdin))
            {
              int e = errno;
              perror ("fgets");
              if (e != EINTR)
                return 1;
              clearerr (stdin);
            }
          else
            {
              printf ("done\n");
              return 0;
            }
        }
      p = strchr (buf, '\n');
      if (p != NULL) *p = 0;
      p = buf;
      while (*p == ' ')
        ++p;
      if (strcmp (p, "help") == 0 || *p == '?')
        cmd_help ();
      else if (strncmp (p, "set", 3) == 0)
        cmd_set (p + 3);
      else if (strncmp (p, "kill", 4) == 0)
        cmd_kill (p + 4);
      else if (strncmp (p, "run", 3) == 0)
        cmd_run (p + 3);
      else if (strncmp (p, "signal", 6) == 0)
        cmd_signal (p + 6);
      else if (strncmp (p, "system", 6) == 0)
        cmd_system (p + 6);
      else if (strcmp (p, "quit") == 0 || strcmp (p, "q") == 0)
        {
          puts ("Done");
          return 0;
        }
      else if (strncmp (p, "handles", 7) == 0)
        cmd_handles (p + 7);
      else if (strncmp (p, "sleep", 5) == 0)
        cmd_sleep (p + 5);
      else if (strcmp (p, "clock") == 0)
        printf ("clock(): %lu\n", (unsigned long)clock ());
      else if (*p != 0)
        puts ("Unknown command.  Type `?' for help.");
    }
  return 1;
}
