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

/* Test handle relocation for sockets.  This program requires
   TCP/IP. */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>

extern int _nfiles;

static void open_file (int case_no, int expected)
{
  int h;

  h = open ("nul", O_RDONLY);
  if (h != expected)
    {
      fprintf (stderr, "open_file(%d,%d): handle is %d\n",
               case_no, expected, h);
      exit (1);
    }
}


static void open_socket (int case_no, int expected)
{
  int h;

  h = socket (AF_INET, SOCK_STREAM, 0);
  if (h != expected)
    {
      fprintf (stderr, "open_socket(%d,%d): handle is %d\n",
               case_no, expected, h);
      exit (1);
    }
}


static void duplicate (int case_no, int source, int expected)
{
  int h;

  h = dup (source);
  if (h != expected)
    {
      fprintf (stderr, "duplicate(%d,%d,%d): handle is %d\n",
               case_no, source, expected, h);
      exit (1);
    }
}


static void check_avail (int h)
{
  struct stat st;

  if (fstat (h, &st) == 0)
    {
      fprintf (stderr, "fstat(%d) does not fail\n", h);
      exit (1);
    }
  else if (errno != EBADF)
    {
      fprintf (stderr, "fstat(%d): errno is %s instead of EBADF\n",
               h, strerror (errno));
      exit (1);
    }
}


static void check_file (int h)
{
  struct stat st;

  if (fstat (h, &st) != 0)
    {
      fprintf (stderr, "fstat(%d): %s\n", h, strerror (errno));
      exit (1);
    }
  if (S_ISSOCK (st.st_mode))
    {
      fprintf (stderr, "Handle %d is not associated with a file\n", h);
      exit (1);
    }
}


static void check_socket (int h)
{
  struct stat st;

  if (fstat (h, &st) != 0)
    {
      fprintf (stderr, "fstat(%d): %s\n", h, strerror (errno));
      exit (1);
    }
  if (!S_ISSOCK (st.st_mode))
    {
      fprintf (stderr, "Handle %d is not associated with a socket\n", h);
      exit (1);
    }
}


int main (void)
{
  int i;

  /* First, close all handles but 0 through 2 to make results
     reproducable even if we inherit some file handles from our parent
     process. */

  for (i = 3; i < _nfiles; ++i)
    close (i);

  /* Initialize TCP/IP now as this creates two handles (INET$ and
     IFNET$?). */

  gethostid ();

  /* Open a file.  The handle should be 5 as 0 through 4 are in use
     (see above). */

  open_file (1, 5);

  /* Open a socket.  The handle should be 3 as one of the two handles
     created by sock_init() will be used. */

  open_socket (2, 3);

  /* Open a socket.  The handle should be 4 as one of the two handles
     created by sock_init() will be used. */

  open_socket (3, 4);

  /* Open a socket.  The handle should be 6. */

  open_socket (4, 6);

  /* Open a file.  The handle should be 7.  This requires relocation
     as the lowest numbered available OS/2 file handle is 6. */

  open_file (5, 7);

  /* Duplicate a socket handle. */

  duplicate (6, 3, 8);

  /* Duplicate a file handle.  This requires relocation. */

  duplicate (7, 7, 9);

  /* Check whether the handles are correct. */

  check_socket (3);
  check_socket (4);
  check_file (5);
  check_socket (6);
  check_file (7);
  check_socket (8);
  check_file (9);

  /* Close handles and check if they are reused for sockets. */

  close (6); close (7);
  check_avail (6);
  check_avail (7);
  open_socket (8, 6);
  open_socket (9, 7);

  /* Check whether the handles are correct. */

  check_socket (3);
  check_socket (4);
  check_file (5);
  check_socket (6);
  check_socket (7);
  check_socket (8);
  check_file (9);

  /* Close handles and check if they are reused for files. */

  close (6); close (7);
  check_avail (6);
  check_avail (7);
  open_file (10, 6);
  open_file (11, 7);

  /* Check whether the handles are correct. */

  check_socket (3);
  check_socket (4);
  check_file (5);
  check_file (6);
  check_file (7);
  check_socket (8);
  check_file (9);

  return 0;
}
