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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <getopt.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <sys/wait.h>

static int v = 1;


static void usage (void)
{
  puts ("Usage: fork [-r#] [-z#] [-cnpqsvw]");
  puts ("Options:");
  puts ("  -r#  Set number of recursion levels");
  puts ("  -z#  Sleep for # seconds before forking");
  puts ("  -c   Display clock() value");
  puts ("  -m   Call malloc() after fork");
  puts ("  -n   Don't use heap");
  puts ("  -p   Make stdout private");
  puts ("  -q   Be quiet");
  puts ("  -s   Set SIGCHLD handler");
  puts ("  -v   Be verbose");
  puts ("  -w   Wait for children");
  exit (1);
}


static void handler (int sig)
{
  int t;

  switch (sig)
    {
    case SIGCHLD:
      printf ("SIGCHLD\n");
      wait (&t);
      break;
    default:
      printf ("Signal %d\n", sig);
      break;
    }
  fflush (stdout);
  signal (SIGCHLD, SIG_ACK);
}


int main (int argc, char *argv[])
{
  int c, fc, i, n, p, t, v0, rep, opt_c, opt_m, opt_n, opt_p, opt_s, opt_w;
  int opt_z, verb;
  char buf[32], *fork_name;
  
  opt_c = 0; opt_m = 0; opt_n = 0; opt_p = 0; opt_s = 0; opt_w = 0; opt_z = 0;
  rep = 0; verb = 1;
  while ((c = getopt (argc, argv, "cr:mnpqsvwz:")) != -1)
    switch (c)
      {
      case 'c':
        opt_c = 1;
        break;
      case 'm':
        opt_m = 1;
        break;
      case 'n':
        opt_n = 1;
        break;
      case 'p':
        opt_p = 1;
        break;
      case 'q':
        verb = 0;
        break;
      case 'r':
        rep = atoi (optarg);
        break;
      case 's':
        opt_s = 1;
        break;
      case 'v':
        verb = 2;
        break;
      case 'w':
        opt_w = 1;
        break;
      case 'z':
        opt_z = atoi (optarg);
        break;
      default:
        usage ();
      }
  if (optind < argc)
    usage ();
  if (rep > 6)
    {
      printf ("*** That's too dangerous ***\n");
      return 1;
    }
  if (opt_s)
    signal (SIGCHLD, handler);
  if (opt_p)
    fcntl (1, F_SETFD, 1);
  v0 = v;
  if (opt_n)
    fork_name = "fork";
  else
    {
      printf ("Here's fork%d (pid=%d, ppid=%d)\n", v0,
              getpid (), getppid ());
      fork_name = strdup ("fork"); /* test heap */
    }
  n = 0;
  do
    {
      if (opt_z != 0)
        sleep (opt_z);
      ++v;
      i = fork ();
      if (i < 0)
        {
          sprintf (buf, "fork%d: fork", v0);
          perror (buf);
          return 1;
        }
      else if (i == 0)
        {
          v0 = v; n = 0;
          printf ("Here's forked %s%d (pid=%d, ppid=%d)\n",
                  fork_name, v0, getpid (), getppid ());
          if (opt_s && signal (SIGCHLD, handler) != handler)
            printf ("Signal handler not inherited!\n");
          if (opt_m)
            printf ("malloc %s\n", malloc (1) ? "succeeded" : "failed");
        }
      else
        {
          ++n;
          if (verb >= 1)
            printf ("fork%d: forked fork has pid %d\n", v0, i);
        }
      if (opt_p)
        {
          /* Check in both the parent and the child processes! */

          fc = fcntl (1, F_GETFD, 0);
          if (fc == -1)
            perror ("fcntl()");
          else if (!(fc & FD_CLOEXEC))
            fprintf (stderr, "FD_CLOEXEC not inherited!\n");
        }
    } while (v <= rep);
  if (opt_w && n > 0)
    {
      if (verb >= 2)
        printf ("fork%d: waiting for %d child%s\n",
                v0, n, (n == 1 ? "" : "ren"));
      while (n > 0)
        {
          p = wait (&t);
          if (p == -1)
            {
              sprintf (buf, "fork%d: wait", v0);
              perror (buf);
              return 1;
            }
          else
            {
              --n;
              if (WIFEXITED (t))
                printf ("fork%d: process %d terminated normally, rc=%d\n",
                        v0, p, WEXITSTATUS (t));
              else if (WIFSTOPPED (t))
                printf ("fork%d: process %d stopped by signal %d\n",
                        v0, p, WSTOPSIG (t));
              else
                printf ("fork%d: process %d terminated by signal %d\n",
                        v0, p, WTERMSIG (t));
            }
        }
    }
  if (opt_c)
    printf ("fork%d: clock=%ld\n", v0, (long)clock ());
  if (verb >= 2)
    printf ("fork%d: end\n", v0);
  return 0;
}
