#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>


#define BUF_SIZE 8192
//#define STRING(str) #str
//#define BUF_FILL_FORMAT "%" STRING(BUF_SIZE) "c"
#define BUF_FILL_FORMAT "%8192c"

#define IPC_DIR "ipc"
#define PATH_SEPARATOR "/"

#define   CLIENT_LOCK_PATH         IPC_DIR PATH_SEPARATOR "client.lock"
#define   SERVER_LOCK_PATH         IPC_DIR PATH_SEPARATOR "server.lock"
#define   SERVER_ENVIRONMENT_PATH  IPC_DIR PATH_SEPARATOR "server.environment"
#define   SERVER_STDIN_PATH        IPC_DIR PATH_SEPARATOR "server.stdin"
#define   SERVER_STDOUT_PATH       IPC_DIR PATH_SEPARATOR "server.stdout"
#define   SERVER_STDERR_PATH       IPC_DIR PATH_SEPARATOR "server.stderr"

#define SERVER_RB "nohup ruby ./server.cgi </dev/null >/dev/null 2>&1 &"

static int argc;
static char **argv;
static char **env;
static FILE *fs_client_lock;
static FILE *fs_server_lock;
static FILE *fs_environment;
static FILE *fs_stdin;
static FILE *fs_stderr;
static FILE *fs_stdout;
static struct flock lock;
static char **e;
static char buf[BUF_SIZE];
static int n;

static void
ensure_server_running ()
{
  int ret;
  char *cmd;
  fs_server_lock = fopen (SERVER_LOCK_PATH, "w");
  lock.l_type = F_WRLCK;
  if(fcntl (fileno (fs_server_lock), F_SETLK, &lock) == 0)
    {
      cmd = SERVER_RB;
      system (cmd);
      lock.l_type = F_UNLCK;
      fcntl (fileno (fs_server_lock), F_SETLKW, &lock);
    }
}
static void
aquire_lock ()
{
  fs_client_lock = fopen (CLIENT_LOCK_PATH, "w");
  lock.l_type = F_WRLCK;
  fcntl (fileno (fs_client_lock), F_SETLKW, &lock);
}
static void
send_env ()
{
  fs_environment = fopen (SERVER_ENVIRONMENT_PATH, "w");
  for (e = env; *e; e++)
    {
      fprintf (fs_environment, "%s%c", *e, '\0');
    }
  fclose (fs_environment);
}
static void
send_stdin ()
{
  buf[0] = n = 0;
  fs_stdin = fopen (SERVER_STDIN_PATH, "w");
  while ((n = fscanf (stdin, BUF_FILL_FORMAT, &buf[0])) > 0)
    {
      fprintf (fs_stdin, "%*s", n, buf);
    }
  fclose (fs_stdin);
}
static void
open_stdout_and_stderr ()
{
  fs_stdout = fopen (SERVER_STDOUT_PATH, "r");
  fs_stderr = fopen (SERVER_STDERR_PATH, "r");
}
static void
receive_stdout ()
{
  buf[0] = n = 0;
  while ((n = fscanf (fs_stdout, BUF_FILL_FORMAT, &buf[0])) > 0)
    {
      fprintf (stdout, "%*s", n, buf);
    }
  fclose (fs_stdout);
}
static void
receive_stderr ()
{
  buf[0] = n = 0;
  while ((n = fscanf (fs_stderr, BUF_FILL_FORMAT, &buf[0])) > 0)
    {
      fprintf (stderr, "%*s", n, buf);
    }
  fclose (fs_stderr);
}
static void
release_lock ()
{
  lock.l_type = F_UNLCK;
  fcntl (fileno (fs_client_lock), F_SETLK, &lock);
}

int
main (__argc, __argv, __env)
     int __argc;
     char **__argv;
     char **__env;
{
  argc = __argc;
  argv = __argv;
  env = __env;

  putenv ("ACGI_SERVER=1");

  ensure_server_running ();
  aquire_lock ();
  send_env ();
  send_stdin ();
  open_stdout_and_stderr ();
  receive_stdout ();
  receive_stderr ();
  release_lock ();
  return 0;
}
