/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>

char reverse[256],line[1024];

typedef struct window_struct {
  struct window_struct *next;
  int in,out,count;
  char line[1024];
  int pid,done;
} WINDOW;
WINDOW *windows = NULL;

int sigpipe,sigio,ready;
void pipe_handler(sig, code, scp, addr)
     int sig, code;
     struct sigcontext *scp;
     char *addr;
{
  sigpipe = 1;
  printf("Sigpipe detected.\n"); 
  fflush(stdout);
}

void child_handler(sig, code, scp, addr)
     int sig, code;
     struct sigcontext *scp;
     char *addr;
{
  wait(0);
  printf("Child reaped.\n");
  fflush(stdout);
}

void io_handler(sig, code, scp, addr)
     int sig, code;
     struct sigcontext *scp;
     char *addr;
{
  printf("Sigio detected.\n");
  sigio = 1;
}

WINDOW *window(label)
     char *label;
{
  /* In is towards parent, out is towards child */
  int in[2],out[2],pid;
  WINDOW *temp;
  char *exclamation = "Hi there!\n";

  printf("Opening window \"%s\".\n",label);
  temp = (WINDOW *) malloc(sizeof(WINDOW));
  if (!temp)
    {
      fprintf(stderr,"Error malloc'ing a window structure.\n");
      return NULL;
    }
  if (pipe(in))
    {
      fprintf(stderr,"Error creating pipes, window not opened.\n");
      return NULL;
    }
  if (pipe(out))
    {
      close(in[0]); 
      close(in[1]);
      fprintf(stderr,"Error creating pipes, window not opened.\n");
      return NULL;
    }
  if (pid=fork())
    {
      /* Parent process */
      temp->count = 0;
      temp->in = in[0];
      fcntl(temp->in,F_SETOWN,getpid());
      fcntl(temp->in,F_SETFL,FASYNC|FNDELAY|fcntl(temp->in,F_GETFL,0));
      temp->out = out[1];
      fcntl(temp->out,F_SETOWN,getpid());
      fcntl(temp->out,F_SETFL,FASYNC|FNDELAY|fcntl(temp->out,F_GETFL,0));
      temp->pid = pid;
      temp->done = 0;
      close(in[1]);
      close(out[0]);
      write(temp->out,exclamation,strlen(exclamation));
      return temp;
    }
  else
    {
      /* Child process */
      dup2(in[1],fileno(stdout));
      close(in[0]);
      close(in[1]);
      dup2(out[0],fileno(stdin));
      close(out[0]);
      close(out[1]);
      execl("/local/u/yosul/sunview/window","window",label,0);
      exit(1);
    }
}

main()
{
  int i,result;
  WINDOW *new,*temp,**tempp;
  char c;
  struct timeval timeout;
  
  /* Setup */
  for (i=0;i<256;i++)
    reverse[i] = i;
  for (i='A';i<='Z';i++)
    reverse[i] = i-'A'+'a';
  for (i='a';i<='z';i++)
    reverse[i] = i-'a'+'A';

  /* Catch signals */
  (void)signal(SIGPIPE,pipe_handler);
  (void)signal(SIGCHLD,child_handler);
  (void)signal(SIGIO,io_handler);
  sigio = 1;

  /* Create window */
  new = window("Test window.");
  new->next = windows;
  windows = new;

  /* Main loop */
  printf("Window created.\n");
  printf("Main loop.\n");
  while (windows)
    {
      timeout.tv_sec = 30;
      timeout.tv_usec = 0;
      if (!sigio)
	select(0,0,0,0,&timeout);
      sigio = 0;
      ready = 0;
      printf("Polling windows.\n");
      for (temp=windows;temp;temp=temp->next)
	if (!temp->done)
	  {
	    result = read(temp->in,&c,1);
	    if ((result==0)||((result==-1)&&(errno!=EWOULDBLOCK)))
	      {
		/* Its bad */
		temp->done = 1;
		close(temp->in);
		close(temp->out);
		continue;
	      }
	    if (result==1)
	      {
		ready = 1;
		temp->line[temp->count++] = reverse[c];
		if (c=='\n')
		  {
		    /* Write out the reversed text */
		    sigpipe = 0;
		    result = write(temp->out,temp->line,temp->count);
		    if ((result<=0)||sigpipe)
		      {
			temp->done = 1;
			close(temp->in);
			close(temp->out);
			continue;
		      }
		    /* Shutdown on "Done." */
		    temp->line[temp->count] = '\0';
		    if (strcmp("Done.\n",temp->line)==0)
		      {
			/* Close down the window */
			temp->done = 1;
			close(temp->in);
			close(temp->out);
			continue;
		      }
		    else
		      {
			/* Open a new window */
			new = window(temp->line);
			new->next = windows;
			windows = new;
			temp->count = 0;
		      }
		  }
	      }
	  }
      for (tempp=&windows;(*tempp);(*tempp) && (tempp = &((*tempp)->next)))
	while ((*tempp) && (*tempp)->done)
	  (*tempp) = (*tempp)->next;
      if (ready) sigio = 1;
    }
  printf("Done.\n");
}

