#include <fcntl.h>
#include <stat.h>
#include <types.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define __MINT__
#include <mintbind.h>


extern int __mint;

#if defined(DEBUG)
#  include <stdio.h>
#endif

#ifndef _COMPILER_H
#  include <compiler.h>
#endif
__EXTERN char *tmpnam __PROTO((char *buf));

/**
 ** (sjk)++ These routines simulate a eunchs pipe() call using temporary 
 **         files. A linked list of type *_pipe is kept as to allow as 
 **         many open pipes as there are valid file descriptors. The only 
 **         problem is that the pipe is initiallialy opened for writing, 
 **         and all output must be done to the pipe, then call pipeclose(fd);
 **         and it sets up the pipe for reading. After all input is done, 
 **         call pipeclose(fd) again and this will close the pipe and delete
 **         the temporary file that the pipe created.
 **    
 **/

struct _pipe { char p_name[40];
	       char flag;               /* "r"-->read, "w"-->write */
	       int  file_des;
	       struct _pipe *next;
             };
static struct _pipe *__pipes = NULL;
static int del_list (struct _pipe *);

__EXTERN int pipeclose __PROTO((int fd));
extern int (*pipeclose_p) __PROTO((int fd));

/*---------------------------------------------------------------------------*/
/* Make a pipe, open the tempory file, set it up for writing, add the pipe   */
/* descriptor to our linked list of pipes, and return.                       */
/*---------------------------------------------------------------------------*/
int pipe(fildes)
int fildes[2];
{ int       fd; 
  char name[40];
  struct _pipe *p;

  tmpnam(name);                              /* Make a temporary file name. */
  
  /*----------------------------------------------*/
  /* Open the pipe for reading and writing.       */
  /*----------------------------------------------*/
  fd = open(name,O_CREAT | O_TRUNC | O_RDWR | O_PIPE, 0644); 

#if defined(DEBUG)
  fprintf(stderr,"opened pipe - fd : %d.\n",fd);
#endif

  if (fd < 0)
#if defined(DEBUG)
    fprintf(stderr,"fd returns : %d! PIPE(%s) failed.\n",fd, name);
#else
    /* nothing */;
#endif
  else 
    { /*------------------------------------------*/
      /* Get a pipe description block and         */
      /* put in the (1)file name,                 */
      /*            (2)file descriptor,           */
      /*            (3)file mode = "w".           */
      /*------------------------------------------*/
      /*-------------------------------------------------------------------*/
      p = (struct _pipe *)malloc(sizeof (struct _pipe)); 
      strcpy(p->p_name,name);           /* fill in name.                   */  
      p->flag = 'w';                    /* initially for write.            */
      p->file_des = fd;                 /* fill in the pipe descriptor.    */
      if(!__pipes)
	pipeclose_p = pipeclose;	/* patch in addr of close routine  */
      /*-------------------------------------------------------------------*/
      p->next = __pipes;                /* Link to our list of open pipes. */
      __pipes     = p;
      /*-------------------------------------------------------------------*/ 
      fildes[0] = fildes[1] = fd;       /* we fill in fildes[].            */
      if (__mint)
	(void) Fcntl(fd,0L,2);          /* Make fd shared across Pexecs()  */
    }
  return(fd);                           /* Return the file descriptor.     */
}
 
/*---------------------------------------------------------------------*/
/* close a pipe, if the pipe was open for writing, set it to a read    */
/* read pipe, and lseek to the begining of the pipe. if it was open    */
/* for reading then close the pipe, delete the temporary file, and     */
/* remove the pipe descriptor from the __pipes linked list.            */
/*---------------------------------------------------------------------*/
int
pipeclose(fd)
int fd;
{  struct _pipe *p;
   int    ifd;
   for (p=__pipes; p; p=p->next)
    { if (fd == p->file_des) 
       { 
#if defined(DEBUG)
        fprintf(stderr,"Deleting pipe - mode : %c, fd : %d\n",p->flag,fd);
#endif
        if (p->flag == 'r') 
	  { int res;
	    ifd = __OPEN_INDEX(fd);
            __open_stat[ifd].pipe = 0;            /* So close wont recurse */
	    res = close(fd);
	    unlink(p->p_name);                     /* Delete it.            */
            del_list(p);                           /* delete from the list. */
            return(res);                               
          }
        else 
        if (p->flag =='w')
          { p->flag = 'r';                         /* now make it read.     */
            lseek(p->file_des,0,SEEK_SET);         /* read from the start.  */
            return(0);
          }
      }
    }
#if defined(DEBUG)
   fprintf(stderr,"attempt to close nonexistant pipe.\n");
#endif
   return(-1);
}

/*---------------------------------------------------------------------*/
/* delete a pipe descriptor from our list of pipes rooted ar __pipes.  */
/*---------------------------------------------------------------------*/
static int del_list(p)
struct _pipe *p;
{ struct _pipe *q,*r;
  
  if (p == __pipes)                      /* Is it the first element?    */
    { __pipes = p->next; 
      free(p);
      return(0);
    }
  else 
    { r = __pipes->next; q = __pipes;    /* No it is interior.           */
      for (; r; r = r->next)
        { if (r == p)
           { q->next = r->next;              /* pass over it.   */
             free(r);                        /* free its space. */
             return(0);                      /* go home.        */
	   }
          q = r;                        /* update trailing pointer.    */
	}
    }
#if defined(DEBUG)
  fprintf(stderr,"Attempt to delete a nonexisting pipe form list (__pipes).");
#endif
  return(-1);
}
