#include "rslib.h"
#pragma hdrstop

#ifdef XP_WIN
#define IFCloseHandle(a) { if (a) {CloseHandle(a); a=NULL;} }

PipeStream::PipeStream(const char* fn,bool detached) : Stream(Stream::ReadWrite),
FileName(fn)
{
   bool i;

   p1i=p1o=p2i=p2o=p3i=p3o=NULL;
   hwrite=hread=herr=NULL;
   pinfo.hProcess=NULL;
   pinfo.hThread=NULL;

   memset(&sa,0,sizeof(SECURITY_ATTRIBUTES));
   sa.nLength=sizeof(SECURITY_ATTRIBUTES);
   sa.bInheritHandle=TRUE;

	HANDLE prochand;
	prochand=GetCurrentProcess();

   if (!CreatePipe(&p1i,&p1o,&sa,0))
	 goto error;

   if (!CreatePipe(&p2i,&p2o,&sa,0))
    goto error;

   if (!CreatePipe(&p3i,&p3o,&sa,0))
    goto error;

	if (DuplicateHandle(prochand,p1o,prochand,&hwrite,
			0,FALSE,DUPLICATE_SAME_ACCESS)==FALSE)
    goto error;

  	if (DuplicateHandle(prochand,p2i,prochand,&hread,
			0,FALSE,DUPLICATE_SAME_ACCESS)==FALSE)
    goto error;

	if (DuplicateHandle(prochand,p3i,prochand,&herr,
			0,FALSE,DUPLICATE_SAME_ACCESS)==FALSE)
    goto error;

   memset(&sinfo,0,sizeof(STARTUPINFO));
   memset(&pinfo,0,sizeof(PROCESS_INFORMATION));

   sinfo.cb=sizeof(STARTUPINFO);
   sinfo.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
   sinfo.wShowWindow=SW_HIDE;
   sinfo.hStdInput=p1i;
   sinfo.hStdOutput=p2o;
   sinfo.hStdError=p3o;

   IFCloseHandle(p1o);
   IFCloseHandle(p2i);
   IFCloseHandle(p3i);
   i = CreateProcess(NULL,FileName,
   	NULL, NULL, TRUE,detached?DETACHED_PROCESS:0,
   	NULL, NULL,&sinfo,&pinfo);

   if (i)
   {
   StdErr = new AnonymousStream(herr,0);
   IFCloseHandle(p1i);
   IFCloseHandle(p2o);
   IFCloseHandle(p3o);
   }
   else goto error;
   return;

error:
	IFCloseHandle(p1i);
	IFCloseHandle(p1o);
	IFCloseHandle(p2i);
	IFCloseHandle(p2o);
	IFCloseHandle(p3i);
	IFCloseHandle(p3o);
	IFCloseHandle(hwrite);
	IFCloseHandle(hread);
	IFCloseHandle(herr);
   throw xdb("unable to open a pipe",fn);
}

PipeStream::~PipeStream()
{
 	IFCloseHandle(pinfo.hProcess);
   IFCloseHandle(pinfo.hThread);

	IFCloseHandle(p1i);
	IFCloseHandle(p1o);
	IFCloseHandle(p2i);
	IFCloseHandle(p2o);
	IFCloseHandle(p3i);
	IFCloseHandle(p3o);
	IFCloseHandle(hwrite);
	IFCloseHandle(hread);
	IFCloseHandle(herr);
}

static int read(HANDLE hread,char * dest,int maxcopy)
{//hread is the child's stdout. Ignore stderr for now.
 DWORD j=0;
 if (hread) ReadFile(hread,dest,maxcopy,&j,NULL);
 return j;
}

int AnonymousStream::read(char * dest,int maxcopy)
{
 return ::read(hread,dest,maxcopy);
}

int PipeStream::read(char * dest,int maxcopy)
{
 return ::read(hread,dest,maxcopy);
}

static bool canread(HANDLE hread)
{
 char c;
 DWORD read=0,avila=0,waiting=0;
 if (hread) PeekNamedPipe(hread,&c,1,&read,&avila,&waiting);
 return read;
}

bool AnonymousStream::canread()
{
 return ::canread(hread);
}

bool PipeStream::canread()
{
 return ::canread(hread);
}


bool AnonymousStream::canwrite()
{
 if (!hwrite) return false;
 DWORD j=0;
 return WriteFile(hwrite,"",0,&j,NULL);
}

bool PipeStream::canwrite()
{
  return WaitForSingleObject(pinfo.hProcess,0) != WAIT_OBJECT_0;
}

static int write(HANDLE hwrite,const char * src,int maxcopy)
{//hwrite is the child's stdin
 DWORD j=0;
 if (hwrite) WriteFile(hwrite,src,maxcopy,&j,NULL);
 return j;
}

int AnonymousStream::write(const char * src,int maxcopy)
{
 return ::write(hwrite,src,maxcopy);
}

int PipeStream::write(const char * src,int maxcopy)
{
 return ::write(hwrite,src,maxcopy);
}

AnonymousStream::AnonymousStream(HANDLE r, HANDLE w): hread(r), hwrite(w),
   Stream( (TType)((r?ReadOnly:0) | (w?WriteOnly:0)))
{
}

AnonymousStream::~AnonymousStream()
{
}
#endif
