/* 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>

static int v = 1;


static void usage (void)
{
  puts ("Usage: fork [-r#] [-z#] [-cnqsvw]");
  puts ("Options:");
  puts ("  -r#  Set number of recursion levels");
  puts ("  -z#  Sleep for # seconds before forking");
  puts ("  -c   Display clock() value");
  puts ("  -n   Don't use heap");
  puts ("  -q   Be quiet");
  puts ("  -s   Set SIGCLD handler");
  puts ("  -v   Be verbose");
  puts ("  -w   Wait for children");
  exit (1);
}


static void handler (int sig)
{
  switch (sig)
    {
    case SIGCLD:
      printf ("SIGCLD\n");
      break;
    default:
      printf ("Signal %d\n", sig);
      break;
    }
  fflush (stdout);
  signal (SIGCLD, SIG_ACK);
}


int main (int argc, char *argv[])
{
  int c, i, n, p, t, v0, rep, opt_c, opt_n, opt_s, opt_w, opt_z, verb;
  char buf[32], *fork_name;
  
  rep = 0; opt_c = 0; opt_n = 0; opt_s = 0; opt_w = 0; opt_z = 0; verb = 1;
  while ((c = getopt (argc, argv, "cr:nqsvwz:")) != EOF)
    switch (c)
      {
      case 'c':
        opt_c = 1;
        break;
      case 'n':
        opt_n = 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 (SIGCLD, handler);
  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 (SIGCLD, handler) != handler)
            printf ("Signal handler not inherited!\n");
        }
      else
        {
          ++n;
          if (verb >= 1)
            printf ("fork%d: forked fork has pid %d\n", v0, i);
        }
    } 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 ((t & 0xff) == 0)
                printf ("fork%d: process %d terminated normally, rc=%d\n",
                        v0, p, t >> 8);
              else if ((t & 0xff) == 127)
                printf ("fork%d: process %d stopped by signal %d\n",
                        v0, p, t >> 8);
              else
                printf ("fork%d: process %d terminated by signal %d\n",
                        v0, p, t & 0xff);
            }
        }
    }
  if (opt_c)
    printf ("fork%d: clock=%ld\n", v0, (long)clock ());
  if (verb >= 2)
    printf ("fork%d: end\n", v0);
  return (0);
}
