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

typedef struct {
  char* filename; /* this file is the pipe (set by user) */
  char is_server; /* this is set by open_control_file */
  int fd; /* this is set by open_control_file */
} single_instance_struct;

/* returns fd, is_server is set to -1 if server, 0 if client */
int open_control_file(single_instance_struct* str)
{
  struct stat buf;

  if(stat(str->filename,&buf)) {
    mkfifo(str->filename,128|256);
    str->is_server=-1;
    str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
  } else {
    str->fd=open(str->filename,O_NONBLOCK|O_WRONLY);
    if(errno==ENXIO) {
      str->is_server=-1;
      str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
    } else
      str->is_server=0;
  }

  return(str->fd);
}

void delete_control_file(single_instance_struct* str)
{
  remove(str->filename);
}

void close_control_file(single_instance_struct* str)
{
  close(str->fd);
}

typedef void (*event_dispatcher)(char* message);

int get_next_message(char* buffer,int len,single_instance_struct* str,int usecs)
{
  struct timeval tv;
  fd_set fdset;
  int num_fds;

  FD_ZERO(&fdset);
  FD_SET(str->fd,&fdset);
  tv.tv_sec=0;
  tv.tv_usec=usecs;

  num_fds=select(str->fd+1,&fdset,NULL,NULL,&tv);
  if(num_fds) {
    int reallen;

    reallen=read(str->fd,buffer,len);
    if(reallen==0) {
      close(str->fd);
      str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
      num_fds--;
    }
    buffer[reallen]=0;
#ifdef DEBUG_1INSTANCE
    if(reallen!=0) fprintf(stderr,"message received: %s.\n",buffer);
#endif
  }

  return(num_fds);
}

int dispatch_event(single_instance_struct* str,event_dispatcher dispatcher,int usecs)
{
  char buffer[1024];
  int num_fds;

  if((num_fds=get_next_message(buffer,1024,str,usecs)) && buffer[0])
    dispatcher(buffer);

  return(num_fds);
}

int loop_if_server(single_instance_struct* str,event_dispatcher dispatcher)
{
  open_control_file(str);
  if(str->is_server) {
    while(1)
      dispatch_event(str,dispatcher,50);
  }

  return(str->fd);
}

void send_message(single_instance_struct* str,char* message)
{
#ifdef DEBUG_1INSTANCE
  int i=
#endif
  write(str->fd,message,strlen(message));
#ifdef DEBUG_1INSTANCE
  fprintf(stderr,"send: %s => %d(%d)\n",message,i,strlen(message));
#endif
}

#ifdef DEBUG_MAIN

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

single_instance_struct str1 = { "/tmp/1instance" };

void my_dispatcher(char* message)
{
#ifdef DEBUG_1INSTANCE
  fprintf(stderr,"Message arrived: %s.\n",message);
#endif
  if(!strcmp(message,"quit")) {
    delete_control_file(str1);
    exit(0);
  }
}

int main(int argc,char** argv)
{
  int i;

  loop_if_server(str1,my_dispatcher);

  for(i=1;i<argc;i++)
    send_event(str1,argv[i]);

  return(0);
}

#endif
