//========================================================================
// PtProcess.cxx
//========================================================================
// Portable Process with  pipes Handling
// Features easy File and Pipe/Process opening and reading uniformization 
// support of read "r" or write "w" popen modes
// Copyright F. Costantini 2006
// Version 1.0
//========================================================================
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License v2 as published
// by the Free Software Foundation.
//========================================================================

#if defined(WIN32) 
#include <windows.h>
#include <io.h>
#include <FCNTL.H>
#endif
#include <stdio.h>

#include "PtProcess.h"

#ifdef WIN32
void CPtProcess::clean_close(HANDLE& h) {
  if (h!= INVALID_HANDLE_VALUE) CloseHandle(h);
  h = INVALID_HANDLE_VALUE;
}

bool CPtProcess::createPipe(HANDLE * h, BOOL bInheritHnd) {
  SECURITY_ATTRIBUTES sa;

  sa.nLength = sizeof(sa);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = bInheritHnd; 

  return CreatePipe (&h[0],&h[1],&sa,0) ? true : false;
}


FILE * CPtProcess::freeHandles() {
	clean_close(pin[0]);    clean_close(pin[1]);
	clean_close(pout[0]);   clean_close(pout[1]);
	clean_close(perr[0]);   clean_close(perr[1]);
	
	return NULL; // convenient for error management
}
#endif 


CPtProcess::CPtProcess() {
	_fpt= NULL;
}

CPtProcess::~CPtProcess() {
	if (_fpt) {
		close();
	}
}

// file open :
FILE * CPtProcess::fopen(const char *cmd, const char *mode) {
#ifdef WIN32
	pin[0] = pin[1] = pout[0] = pout[1] = perr[0] = perr[1] = INVALID_HANDLE_VALUE;
#endif
	if (_fpt) close(); // close first before reuse
	_fpt = ::fopen(cmd,mode);
	return _fpt;
}

// pipe open :
FILE * CPtProcess::popen(const char *cmd, const char *mode) {
#if defined(WIN32) 

  // PRECONDITIONS
  if (!mode || !*mode || (*mode!='r' && *mode!='w') ) return NULL;
  if (_fpt) close(); // close first before reuse
  
  ptmode = *mode;
  
  pin[0] = pin[1] = pout[0] = pout[1] = perr[0] = perr[1] = INVALID_HANDLE_VALUE;
  // stderr to stdout wanted ?
  int fusion = (strstr(cmd,"2>&1") !=NULL);


  // Create windows pipes
  if (!createPipe(pin) || !createPipe(pout) || (!fusion && !createPipe(perr) ) )
	return freeHandles(); // error

  // Initialize Startup Info
  ZeroMemory(&si, sizeof(STARTUPINFO));
  si.cb           = sizeof(STARTUPINFO);
  si.dwFlags    = STARTF_USESTDHANDLES;
  si.hStdInput    = pin[0];
  si.hStdOutput   = pout[1];
  si.hStdError  = fusion ? pout[1] : perr [1];


  if ( CreateProcess(NULL, (LPTSTR) cmd,NULL,NULL,TRUE,
	  DETACHED_PROCESS,NULL,NULL, &si, &pi)) {
	  // don't need theses handles inherited by child process:
	  clean_close(pin[0]); clean_close(pout[1]); clean_close(perr[1]); 
	  HANDLE & h = *mode == 'r' ? pout[0] : pin[1];
	  _fpt = _fdopen(_open_osfhandle((long) h,_O_BINARY),mode);
	  h= INVALID_HANDLE_VALUE;  // reset the handle pointer that is shared 
								// with _fpt so we don't free it twice
  }

  if (!_fpt)  freeHandles();
  return _fpt;
#else  
  _fpt=::popen(cmd,mode);
  return _fpt;
#endif 
}

// close: 
int CPtProcess::close() {
#if defined(WIN32)
  if (_fpt) {
    fclose(_fpt);
    clean_close(perr[0]);
    fclose(_fpt);
	clean_close(pin[1]);
	clean_close(pout[0]);
	_fpt = NULL;
    return 0;
  }
  return -1;
#else  
  int ret = ::pclose(_fpt);
  _fpt=NULL;
  return ret;
#endif 
}
