/*
 * $Id: hbproces.c 13196 2009-12-10 09:42:34Z druzus $
 */

/*
 * Harbour Project source code:
 * low level functions to create, wait and terminate processes
 *
 * Copyright 2009 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 * www - http://www.harbour-project.org
 * based on xHarbour code by
 * Copyright 2003 Giancarlo Niccolai <gian@niccolai.ws>
 * www - http://www.xharbour.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#define HB_OS_WIN_USED

/* #define HB_IO_WIN_OFF */

#include "hbapi.h"
#include "hbapifs.h"
#include "hbvm.h"

#if defined( HB_OS_UNIX )
#  include <unistd.h>
#  include <sys/time.h>
#  include <sys/types.h>
#  include <sys/wait.h>
#  include <sys/stat.h>
#  include <fcntl.h>
#  include <signal.h>
#elif defined( HB_OS_OS2 ) || ( defined( HB_OS_WIN ) && !defined( HB_IO_WIN ) )
#  include <io.h>
#  include <process.h>
#  include <fcntl.h>
#  if defined( HB_OS_OS2 ) && defined( __GNUC__ )
#    include <sys/wait.h>
#  endif
#elif defined( HB_OS_DOS )
#  include <process.h>
#  include <fcntl.h>
#  if defined( __DJGPP__ )
#     include <sys/stat.h>
#     include <unistd.h>
#  else
#     include <io.h>
#  endif
#endif

#if defined( HB_OS_DOS ) || defined( HB_OS_OS2 ) || defined( HB_OS_UNIX ) || \
    ( defined( HB_OS_WIN ) && !defined( HB_IO_WIN ) )

/* convert command to argument list using standard bourne shell encoding:
 * "" and '' can be used to group parameters with blank characters,
 * the escape character is '\', quoting by '' disables escape character.
 */
static char ** hb_buildArgs( const char *pszFilename )
{
   const char * src;
   char ** argv, * dst, cQuote = 0;
   int argc = 0;

   while( HB_ISSPACE( *pszFilename ) )
      ++pszFilename;
   src = pszFilename;
   while( *src )
   {
#if defined( HB_OS_UNIX )
      if( *src == '\\' && cQuote != '\'' )
      {
         if( src[ 1 ] )
            ++src;
      }
      else
#endif
      if( *src == cQuote )
         cQuote = 0;
      else if( cQuote == 0 )
      {
#if defined( HB_OS_UNIX )
         if( *src == '"' || *src == '\'' )
#else
         if( *src == '"' )
#endif
            cQuote = *src;
         else if( HB_ISSPACE( *src ) )
         {
            while( HB_ISSPACE( src[ 1 ] ) )
               ++src;
            if( src[ 1 ] )
               ++argc;
         }
      }
      ++src;
   }
   dst = ( char * ) hb_xgrab( strlen( pszFilename ) + 1 );
   argv = ( char ** ) hb_xgrab( ( argc + 2 ) * sizeof( char * ) );
   argv[ 0 ] = dst;
   argv[ argc + 1 ] = NULL;
   argc = 0;

   cQuote = 0;
   src = pszFilename;
   while( *src )
   {
#if defined( HB_OS_UNIX )
      if( *src == '\\' && cQuote != '\'' )
      {
         if( src[ 1 ] )
         {
            *dst++ = src[ 1 ];
            ++src;
         }
      }
      else
#endif
      if( *src == cQuote )
         cQuote = 0;
      else if( cQuote != 0 )
         *dst++ = *src;
      else
      {
#if defined( HB_OS_UNIX )
         if( *src == '"' || *src == '\'' )
#else
         if( *src == '"' )
#endif
            cQuote = *src;
         else if( HB_ISSPACE( *src ) )
         {
            *dst++ = '\0';
            while( HB_ISSPACE( src[ 1 ] ) )
               ++src;
            if( src[ 1 ] )
               argv[ ++argc ] = dst;
         }
         else
            *dst++ = *src;
      }
      ++src;
   }
   *dst = 0;

   return argv;
}

static void hb_freeArgs( char ** argv )
{
   hb_xfree( argv[ 0 ] );
   hb_xfree( argv );
}

#endif

#if defined( HB_OS_WIN_CE )
static void hb_getCommand( const char *pszFilename,
                           LPTSTR * lpAppName, LPTSTR * lpParams )
{
   const char * src, * params;
   char cQuote = 0;

   while( HB_ISSPACE( *pszFilename ) )
      ++pszFilename;

   params = NULL;
   src = pszFilename;
   while( *src )
   {
      if( *src == cQuote )
         cQuote = 0;
      else if( cQuote == 0 )
      {
         if( *src == '"' )
            cQuote = *src;
         else if( HB_ISSPACE( *src ) )
         {
            params = src;
            while( HB_ISSPACE( *params ) )
               ++params;
            if( *params == 0 )
               params = NULL;
            break;
         }
      }
      ++src;
   }

#if defined( UNICODE )
   *lpParams = params ? hb_mbtowc( params ) : NULL;
   *lpAppName = hb_mbntowc( pszFilename, ( ULONG ) ( src - pszFilename ) );
#else
   *lpParams = params ? hb_strdup( params ) : NULL;
   *lpAppName = hb_strndup( pszFilename, ( ULONG ) ( src - pszFilename ) );
#endif
}
#endif

#if defined( HB_OS_DOS ) || defined( HB_OS_OS2 ) || defined( HB_OS_WIN_CE )
static int hb_fsProcessExec( const char *pszFilename,
                             HB_FHANDLE hStdin, HB_FHANDLE hStdout,
                             HB_FHANDLE hStderr )
{
   int iResult = FS_ERROR;

   HB_TRACE(HB_TR_DEBUG, ("hb_fsProcessExec(%s, %p, %p, %p, %d, %p)", pszFilename, ( void * ) ( HB_PTRDIFF ) hStdin, ( void * ) ( HB_PTRDIFF ) hStdout, ( void * ) ( HB_PTRDIFF ) hStderr));

#if defined( HB_OS_WIN_CE )
{
   LPTSTR lpAppName, lpParams;
   BOOL fError;

   HB_SYMBOL_UNUSED( hStdin );
   HB_SYMBOL_UNUSED( hStdout );
   HB_SYMBOL_UNUSED( hStderr );

   hb_getCommand( pszFilename, &lpAppName, &lpParams );

   hb_vmUnlock();
   fError = ! CreateProcess( lpAppName,      /* lpAppName */
                             lpParams,       /* lpCommandLine */
                             NULL,           /* lpProcessAttr */
                             NULL,           /* lpThreadAttr */
                             FALSE,          /* bInheritHandles */
                             0,              /* dwCreationFlags */
                             NULL,           /* lpEnvironment */
                             NULL,           /* lpCurrentDirectory */
                             NULL,           /* lpStartupInfo */
                             NULL );         /* lpProcessInformation */
   hb_fsSetIOError( !fError, 0 );
   if( !fError )
   {
      hb_fsSetIOError( !fError, 0 );
      iResult = 0;
   }
   hb_vmLock();

   if( lpAppName )
      hb_xfree( lpAppName );
   if( lpParams )
      hb_xfree( lpParams );
}
#elif defined( HB_OS_DOS ) || defined( HB_OS_WIN ) || defined( HB_OS_OS2 ) || \
      defined( HB_OS_UNIX )
{
   int iStdIn, iStdOut, iStdErr;
   char ** argv;

   argv = hb_buildArgs( pszFilename );

   hb_vmUnlock();

   iStdIn = iStdOut = iStdErr = FS_ERROR;

   if( hStdin != FS_ERROR )
   {
      iStdIn  = dup( 0 );
      dup2( hStdin,  0 );
   }
   if( hStdout != FS_ERROR )
   {
      iStdOut = dup( 1 );
      dup2( hStdout, 1 );
   }
   if( hStderr != FS_ERROR )
   {
      iStdErr = dup( 2 );
      dup2( hStderr, 2 );
   }
#if defined( HB_OS_UNIX )
   {
      pid_t pid = fork();
      if( pid == 0 )
      {
         /* close all non std* handles */
         {
            int iMaxFD, i;
            iMaxFD = sysconf( _SC_OPEN_MAX );
            if( iMaxFD < 3 )
               iMaxFD = 1024;
            for( i = 3; i < iMaxFD; ++i )
               close( i );
         }
         /* reset extended process attributes */
         setuid( getuid() );
         setgid( getgid() );

         /* execute command */
         execvp( argv[ 0 ], argv );
         exit(1);
      }
      else if( pid != -1 )
      {
         int iStatus;
         iResult = waitpid( pid, &iStatus, 0 );
#ifdef ERESTARTSYS
         if( iResult < 0 && errno != ERESTARTSYS )
#else
         if( iResult < 0 )
#endif
            iResult = -2;
         else if( iResult == 0 )
            iResult = -1;
         else
            iResult = WIFEXITED( iStatus ) ? WEXITSTATUS( iStatus ) : 0;
      }
   }
#elif defined( _MSC_VER ) || defined( __LCC__ ) || \
    defined( __XCC__ ) || defined( __POCC__ )
   iResult = _spawnvp( _P_WAIT, argv[ 0 ], argv );
#elif defined( __MINGW32__ ) || defined( __WATCOMC__ )
   iResult = spawnvp( P_WAIT, argv[ 0 ], ( const char * const * ) argv );
#else
   iResult = spawnvp( P_WAIT, argv[ 0 ], ( char * const * ) argv );
#endif
   hb_fsSetIOError( iResult >= 0, 0 );

   if( iStdIn != FS_ERROR )
      dup2( iStdIn,  0 );
   if( iStdOut != FS_ERROR )
      dup2( iStdOut, 1 );
   if( iStdErr != FS_ERROR )
      dup2( iStdErr, 2 );

   hb_vmLock();
   hb_freeArgs( argv );
}
#else
{
   int TODO; /* TODO: for given platform */

   HB_SYMBOL_UNUSED( pszFilename );
   HB_SYMBOL_UNUSED( hStdin );
   HB_SYMBOL_UNUSED( hStdout );
   HB_SYMBOL_UNUSED( hStderr );

   hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#endif

   return iResult;
}
#endif

HB_FHANDLE hb_fsProcessOpen( const char *pszFilename,
                             HB_FHANDLE *phStdin, HB_FHANDLE *phStdout,
                             HB_FHANDLE *phStderr,
                             BOOL fDetach, ULONG *pulPID )
{
   HB_FHANDLE hResult = FS_ERROR;
   char * pszFree = NULL;

   HB_TRACE(HB_TR_DEBUG, ("hb_fsProcessOpen(%s, %p, %p, %p, %d, %p)", pszFilename, phStdin, phStdout, phStderr, fDetach, pulPID));

   pszFilename = hb_osEncodeCP( pszFilename, &pszFree, NULL );

#if defined( HB_IO_WIN )
{

#if defined( HB_OS_WIN_CE )
#  define CreatePipe( hIn, hOut, sa, flags )    ( FALSE )
#endif

   BOOL fError = FALSE;
   HANDLE hPipes[ 6 ];
   SECURITY_ATTRIBUTES sa;
   int i;

   for( i = 0; i < 6; ++i )
      hPipes[ i ] = INVALID_HANDLE_VALUE;

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

   if( phStdin != NULL )
      fError = !CreatePipe( &hPipes[0], &hPipes[1], &sa, 0 );
   if( !fError && phStdout != NULL )
      fError = !CreatePipe( &hPipes[2], &hPipes[3], &sa, 0 );
   if( !fError && phStderr != NULL )
   {
      if( phStdout == phStderr )
      {
         hPipes[4] = hPipes[2];
         hPipes[5] = hPipes[3];
      }
      else
         fError = !CreatePipe( &hPipes[4], &hPipes[5], &sa, 0 );
   }

   if( fError )
      hb_fsSetIOError( FALSE, 0 );
   else
   {
      PROCESS_INFORMATION pi;
      STARTUPINFO si;
      DWORD dwFlags = 0;
#if defined( UNICODE )
      LPWSTR lpCommand = hb_mbtowc( pszFilename );
#else
      char * lpCommand = hb_strdup( pszFilename );
#endif
      memset( &pi, 0, sizeof( pi ) );
      memset( &si, 0, sizeof( si ) );
      si.cb = sizeof( si );
#ifdef STARTF_USESTDHANDLES
      si.dwFlags = STARTF_USESTDHANDLES;
#endif
      if( fDetach )
      {
#ifdef STARTF_USESHOWWINDOW
         si.dwFlags |= STARTF_USESHOWWINDOW;
#endif
         si.wShowWindow = SW_HIDE;
         si.hStdInput  = hPipes[ 0 ];
         si.hStdOutput = hPipes[ 3 ];
         si.hStdError  = hPipes[ 5 ];
#ifdef DETACHED_PROCESS
         dwFlags |= DETACHED_PROCESS;
#endif
      }
      else
      {
         si.hStdInput  = phStdin  ? hPipes[ 0 ] : GetStdHandle( STD_INPUT_HANDLE );
         si.hStdOutput = phStdout ? hPipes[ 3 ] : GetStdHandle( STD_OUTPUT_HANDLE );
         si.hStdError  = phStderr ? hPipes[ 5 ] : GetStdHandle( STD_ERROR_HANDLE );
      }
      fError = ! CreateProcess( NULL,           /* lpAppName */
                                lpCommand,
                                NULL,           /* lpProcessAttr */
                                NULL,           /* lpThreadAttr */
                                TRUE,           /* bInheritHandles */
                                dwFlags,        /* dwCreationFlags */
                                NULL,           /* lpEnvironment */
                                NULL,           /* lpCurrentDirectory */
                                &si,
                                &pi );
      hb_fsSetIOError( !fError, 0 );
      hb_xfree( lpCommand );
      if( !fError )
      {
         if( phStdin != NULL )
         {
            *phStdin = ( HB_FHANDLE ) hPipes[ 1 ];
            hPipes[ 1 ] = INVALID_HANDLE_VALUE;
         }
         if( phStdout != NULL )
         {
            *phStdout = ( HB_FHANDLE ) hPipes[ 2 ];
            hPipes[ 2 ] = INVALID_HANDLE_VALUE;
         }
         if( phStderr != NULL )
         {
            *phStderr = ( HB_FHANDLE ) hPipes[ 4 ];
            hPipes[ 4 ] = INVALID_HANDLE_VALUE;
         }
         if( pulPID )
            *pulPID = pi.dwProcessId;
         CloseHandle( pi.hThread );
         hResult = ( HB_FHANDLE ) pi.hProcess;
      }
   }

   for( i = phStdout == phStderr ? 3 : 5; i >= 0; --i )
   {
      if( hPipes[ i ] != INVALID_HANDLE_VALUE )
         CloseHandle( hPipes[ i ] );
   }
}
#elif defined( HB_OS_UNIX )
{
   BOOL fError = FALSE;
   HB_FHANDLE hPipeIn [ 2 ] = { FS_ERROR, FS_ERROR },
              hPipeOut[ 2 ] = { FS_ERROR, FS_ERROR },
              hPipeErr[ 2 ] = { FS_ERROR, FS_ERROR };

   if( phStdin != NULL )
      fError = pipe( hPipeIn ) != 0;
   if( !fError && phStdout != NULL )
      fError = pipe( hPipeOut ) != 0;
   if( !fError && phStderr != NULL )
   {
      if( phStdout == phStderr )
      {
         hPipeErr[ 0 ] = hPipeOut[ 0 ];
         hPipeErr[ 1 ] = hPipeOut[ 1 ];
      }
      else
         fError = pipe( hPipeErr ) != 0;
   }

   if( !fError )
   {
      pid_t pid = fork();

      if( pid == -1 )
         fError = TRUE;
      else if( pid != 0 )    /* parent process */
      {
         if( phStdin != NULL )
         {
            *phStdin = ( HB_FHANDLE ) hPipeIn[ 1 ];
            hPipeIn[ 1 ] = FS_ERROR;
         }
         if( phStdout != NULL )
         {
            *phStdout = ( HB_FHANDLE ) hPipeOut[ 0 ];
            hPipeOut[ 0 ] = FS_ERROR;
         }
         if( phStderr != NULL )
         {
            *phStderr = ( HB_FHANDLE ) hPipeErr[ 0 ];
            hPipeErr[ 0 ] = FS_ERROR;
         }
         if( pulPID )
            *pulPID = pid;
         hResult = ( HB_FHANDLE ) pid;
      }
      else                    /* child process */
      {
         if( fDetach && ( !phStdin || !phStdout || !phStderr ) )
         {
            HB_FHANDLE hNull = open( "/dev/null", O_RDWR );

            if( !phStdin )
               dup2( hNull, 0 );
            if( !phStdout )
               dup2( hNull, 1 );
            if( !phStderr )
               dup2( hNull, 2 );

            if( hNull != FS_ERROR )
               close( hNull );
         }

         if( phStdin != NULL )
            dup2( hPipeIn[ 0 ], 0 );
         if( phStdout != NULL )
            dup2( hPipeOut[ 1 ], 1 );
         if( phStderr != NULL )
            dup2( hPipeErr[ 1 ], 2 );

         /* close all non std* handles */
         {
            int iMaxFD, i;
            iMaxFD = sysconf( _SC_OPEN_MAX );
            if( iMaxFD < 3 )
               iMaxFD = 1024;
            for( i = 3; i < iMaxFD; ++i )
               close( i );
         }

         /* reset extended process attributes */
         setuid( getuid() );
         setgid( getgid() );

         /* execute command */
         {
#if 0
            char * argv[4];

            argv[0] = ( char * ) "sh";
            argv[1] = ( char * ) "-c";
            argv[2] = ( char * ) pszFilename;
            argv[3] = ( char * ) 0;
            execv( "/bin/sh", argv );
#else
            char ** argv;

            argv = hb_buildArgs( pszFilename );
#if defined( __WATCOMC__ )
            execvp( argv[ 0 ], ( const char ** ) argv );
#else
            execvp( argv[ 0 ], argv );
#endif
            hb_freeArgs( argv );
#endif
            exit(1);
         }
      }
   }

   hb_fsSetIOError( !fError, 0 );

   if( hPipeIn[ 0 ] != FS_ERROR )
      close( hPipeIn[ 0 ] );
   if( hPipeIn[ 1 ] != FS_ERROR )
      close( hPipeIn[ 1 ] );
   if( hPipeOut[ 0 ] != FS_ERROR )
      close( hPipeOut[ 0 ] );
   if( hPipeOut[ 1 ] != FS_ERROR )
      close( hPipeOut[ 1 ] );
   if( phStdout != phStderr )
   {
      if( hPipeErr[ 0 ] != FS_ERROR )
         close( hPipeErr[ 0 ] );
      if( hPipeErr[ 1 ] != FS_ERROR )
         close( hPipeErr[ 1 ] );
   }
}
#elif defined( HB_OS_OS2 ) || defined( HB_OS_WIN )
{

#if defined( HB_OS_OS2 ) && defined( __GNUC__ )

#  define _hb_pipe( e, p )    do { \
                                 (e) = pipe( (p) ) != 0; \
                                 if( !(e) ) \
                                 { \
                                    setmode( (p)[ 0 ], O_BINARY ); \
                                    setmode( (p)[ 1 ], O_BINARY ); \
                                 } \
                              } while( 0 )
#else

#  define pid_t               int
#  define _hb_pipe( e, p )    do { \
                                 (e) = _pipe( (p), 2048, _O_BINARY ) != 0; \
                              } while( 0 )
#endif

   BOOL fError = FALSE;
   HB_FHANDLE hPipeIn [ 2 ] = { FS_ERROR, FS_ERROR },
              hPipeOut[ 2 ] = { FS_ERROR, FS_ERROR },
              hPipeErr[ 2 ] = { FS_ERROR, FS_ERROR };

   if( phStdin != NULL )
   {
      _hb_pipe( fError, hPipeIn );
   }
   if( !fError && phStdout != NULL )
   {
      _hb_pipe( fError, hPipeOut );
   }
   if( !fError && phStderr != NULL )
   {
      if( phStdout == phStderr )
      {
         hPipeErr[ 0 ] = hPipeOut[ 0 ];
         hPipeErr[ 1 ] = hPipeOut[ 1 ];
      }
      else
      {
         _hb_pipe( fError, hPipeErr );
      }
   }

   if( !fError )
   {
      int hStdIn, hStdOut, hStdErr;
      char ** argv;
      pid_t pid;

      hStdIn  = dup( 0 );
      hStdOut = dup( 1 );
      hStdErr = dup( 2 );

      if( fDetach && ( !phStdin || !phStdout || !phStderr ) )
      {
         HB_FHANDLE hNull = open( "NUL:", O_RDWR );

         if( !phStdin )
            dup2( hNull, 0 );
         if( !phStdout )
            dup2( hNull, 1 );
         if( !phStderr )
            dup2( hNull, 2 );

         if( hNull != FS_ERROR )
            close( hNull );
      }

      if( phStdin != NULL )
         dup2( hPipeIn[ 0 ], 0 );
      if( phStdout != NULL )
         dup2( hPipeOut[ 1 ], 1 );
      if( phStderr != NULL )
         dup2( hPipeErr[ 1 ], 2 );

      argv = hb_buildArgs( pszFilename );

#if defined( _MSC_VER ) || defined( __LCC__ ) || \
    defined( __XCC__ ) || defined( __POCC__ )
      pid = _spawnvp( _P_NOWAIT, argv[ 0 ], argv );
#elif defined( __MINGW32__ ) || defined( __WATCOMC__ )
      pid = spawnvp( P_NOWAIT, argv[ 0 ], ( const char * const * ) argv );
#else
      pid = spawnvp( P_NOWAIT, argv[ 0 ], ( char * const * ) argv );
#endif
      hb_freeArgs( argv );

      dup2( hStdIn,  0 );
      dup2( hStdOut, 1 );
      dup2( hStdErr, 2 );

      if( pid < 0 )
         fError = TRUE;
      else if( pid != 0 )    /* parent process */
      {
         if( phStdin != NULL )
         {
            *phStdin = ( HB_FHANDLE ) hPipeIn[ 1 ];
            hPipeIn[ 1 ] = FS_ERROR;
         }
         if( phStdout != NULL )
         {
            *phStdout = ( HB_FHANDLE ) hPipeOut[ 0 ];
            hPipeOut[ 0 ] = FS_ERROR;
         }
         if( phStderr != NULL )
         {
            *phStderr = ( HB_FHANDLE ) hPipeErr[ 0 ];
            hPipeErr[ 0 ] = FS_ERROR;
         }
         if( pulPID )
            *pulPID = pid;
         hResult = ( HB_FHANDLE ) pid;
      }
   }

   hb_fsSetIOError( !fError, 0 );

   if( hPipeIn[ 0 ] != FS_ERROR )
      close( hPipeIn[ 0 ] );
   if( hPipeIn[ 1 ] != FS_ERROR )
      close( hPipeIn[ 1 ] );
   if( hPipeOut[ 0 ] != FS_ERROR )
      close( hPipeOut[ 0 ] );
   if( hPipeOut[ 1 ] != FS_ERROR )
      close( hPipeOut[ 1 ] );
   if( phStdout != phStderr )
   {
      if( hPipeErr[ 0 ] != FS_ERROR )
         close( hPipeErr[ 0 ] );
      if( hPipeErr[ 1 ] != FS_ERROR )
         close( hPipeErr[ 1 ] );
   }
}
#else
{
   int TODO; /* TODO: for given platform */

   HB_SYMBOL_UNUSED( pszFilename );
   HB_SYMBOL_UNUSED( phStdin );
   HB_SYMBOL_UNUSED( phStdout );
   HB_SYMBOL_UNUSED( phStderr );
   HB_SYMBOL_UNUSED( fDetach );
   HB_SYMBOL_UNUSED( pulPID );

   hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#endif

   if( pszFree )
      hb_xfree( pszFree );

   return hResult;
}

int hb_fsProcessValue( HB_FHANDLE hProcess, BOOL fWait )
{
   int iRetStatus = -1;

   HB_TRACE(HB_TR_DEBUG, ("hb_fsProcessValue(%p, %d)", ( void * ) ( HB_PTRDIFF ) hProcess, fWait));

#if defined( HB_IO_WIN )
{
   BOOL fError = TRUE;
   DWORD dwResult;
   HANDLE hProc = ( HANDLE ) hb_fsGetOsHandle( hProcess );

   if( hProc )
   {
      hb_vmUnlock();
      dwResult = WaitForSingleObject( hProc, fWait ? INFINITE : 0 );
      if( dwResult == WAIT_OBJECT_0 )
      {
         fError = !GetExitCodeProcess( hProc, &dwResult );
         iRetStatus = !fError ? ( int ) dwResult : -2;
      }
      hb_fsSetIOError( !fError, 0 );
      if( !fError )
         CloseHandle( hProc );
      hb_vmLock();
   }
   else
      hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#elif defined( HB_OS_UNIX ) || ( defined( HB_OS_OS2 ) && defined( __GNUC__ ) )
{
   int iStatus;
   pid_t pid = ( pid_t ) hProcess;

   if( pid > 0 )
   {
      hb_vmUnlock();
      iRetStatus = waitpid( pid, &iStatus, fWait ? 0 : WNOHANG );
      hb_fsSetIOError( iRetStatus >= 0, 0 );
#ifdef ERESTARTSYS
      if( iRetStatus < 0 && errno != ERESTARTSYS )
#else
      if( iRetStatus < 0 )
#endif
         iRetStatus = -2;
      else if( iRetStatus == 0 )
         iRetStatus = -1;
      else
         iRetStatus = WIFEXITED( iStatus ) ? WEXITSTATUS( iStatus ) : 0;
      hb_vmLock();
   }
   else
      hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#elif defined( HB_OS_OS2 ) || defined( HB_OS_WIN )
{
   int iPid = ( int ) hProcess;

   HB_SYMBOL_UNUSED( fWait );

   if( iPid > 0 )
   {
      hb_vmUnlock();
#if defined( __BORLANDC__ )
      iPid = cwait( &iRetStatus, iPid, 0 );
#else
      iPid = _cwait( &iRetStatus, iPid, 0 );
#endif
      hb_fsSetIOError( iPid > 0, 0 );
      if( iPid != ( int ) hProcess )
         iRetStatus = -1;
      hb_vmLock();
   }
   else
      hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#else
{
   int TODO; /* TODO: for given platform */

   HB_SYMBOL_UNUSED( hProcess );
   HB_SYMBOL_UNUSED( fWait );
   hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#endif
   return iRetStatus;
}

/* Closes/kills process. The handle is still valid until you
 * catch it with hb_fsProcessValue.
 */
BOOL hb_fsProcessClose( HB_FHANDLE hProcess, BOOL fGentle )
{
   BOOL fResult = FALSE;

   HB_TRACE(HB_TR_DEBUG, ("hb_fsProcessClose(%p, %d)", ( void * ) ( HB_PTRDIFF ) hProcess, fGentle));

#if defined( HB_IO_WIN )
{
   HANDLE hProc = ( HANDLE ) hb_fsGetOsHandle( hProcess );

   if( hProc )
   {
      if( TerminateProcess( hProc, fGentle ? 0 : 1 ) )
         fResult = TRUE;
      hb_fsSetIOError( fResult, 0 );
   }
   else
      hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#elif defined( HB_OS_UNIX ) || ( defined( HB_OS_OS2 ) && defined( __GNUC__ ) )
{
   pid_t pid = ( pid_t ) hProcess;
   if( pid > 0 )
   {
      if( kill( pid, fGentle ? SIGTERM : SIGKILL ) == 0 )
         fResult = TRUE;
      hb_fsSetIOError( fResult, 0 );
   }
   else
      hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#elif defined( HB_OS_WIN )
{
   HANDLE hProc = OpenProcess( PROCESS_TERMINATE, FALSE, hProcess );

   if( hProc )
   {
      if( TerminateProcess( hProc, fGentle ? 0 : 1 ) )
         fResult = TRUE;
      hb_fsSetIOError( fResult, 0 );
      CloseHandle( hProc );
   }
   else
      hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#else
{
   int TODO; /* TODO: for given platform */

   HB_SYMBOL_UNUSED( hProcess );
   HB_SYMBOL_UNUSED( fGentle );
   hb_fsSetError( ( HB_ERRCODE ) FS_ERROR );
}
#endif
   return fResult;
}

#define HB_STD_BUFFER_SIZE    4096

int hb_fsProcessRun( const char * pszFilename,
                     const char * pStdInBuf, ULONG ulStdInLen,
                     char ** pStdOutPtr, ULONG * pulStdOut,
                     char ** pStdErrPtr, ULONG * pulStdErr,
                     BOOL fDetach )
{
   HB_FHANDLE hStdin, hStdout, hStderr, *phStdin, *phStdout, *phStderr;
   char * pOutBuf, *pErrBuf;
   ULONG ulOutSize, ulErrSize, ulOutBuf, ulErrBuf;
   char * pszFree = NULL;
   int iResult;

   HB_TRACE(HB_TR_DEBUG, ("hb_fsProcessRun(%s, %p, %lu, %p, %p, %p, %p, %d)", pStdInBuf, pStdInBuf, ulStdInLen, pStdOutPtr, pulStdOut, pStdErrPtr, pulStdErr, fDetach));

   pszFilename = hb_osEncodeCP( pszFilename, &pszFree, NULL );

   ulOutBuf = ulErrBuf = ulOutSize = ulErrSize = 0;
   pOutBuf = pErrBuf = NULL;
   hStdin = hStdout = hStderr = FS_ERROR;
   phStdin = pStdInBuf ? &hStdin : NULL;
   phStdout = pStdOutPtr && pulStdOut ? &hStdout : NULL;
   phStderr = pStdErrPtr && pulStdErr ? &hStderr : NULL;

#if defined( HB_OS_DOS ) || defined( HB_OS_OS2 ) || defined( HB_OS_WIN_CE )
{

#if defined( HB_OS_WIN_CE )
#  define _HB_NULLHANDLE()    FS_ERROR
#elif defined( HB_OS_UNIX )
#  define _HB_NULLHANDLE()    open( "/dev/null", O_RDWR )
#else
#  define _HB_NULLHANDLE()    open( "NUL:", O_RDWR )
#endif
   char sTmpIn[ HB_PATH_MAX ];
   char sTmpOut[ HB_PATH_MAX ];
   char sTmpErr[ HB_PATH_MAX ];

   sTmpIn[ 0 ] = sTmpOut[ 0 ] = sTmpErr[ 0 ] = '\0';
   if( pStdInBuf )
   {
      hStdin = hb_fsCreateTempEx( sTmpIn, NULL, NULL, NULL, FC_NORMAL );
      if( ulStdInLen )
      {
         hb_fsWriteLarge( hStdin, pStdInBuf, ulStdInLen );
         hb_fsSeek( hStdin, 0, FS_SET );
      }
   }
   else if( fDetach )
      hStdin = _HB_NULLHANDLE();

   if( pStdOutPtr && pulStdOut )
      hStdout = hb_fsCreateTempEx( sTmpOut, NULL, NULL, NULL, FC_NORMAL );
   else if( fDetach )
      hStdout = _HB_NULLHANDLE();

   if( pStdErrPtr && pulStdErr )
      hStderr = hb_fsCreateTempEx( sTmpErr, NULL, NULL, NULL, FC_NORMAL );
   else if( fDetach )
      hStderr = _HB_NULLHANDLE();

   iResult = hb_fsProcessExec( pszFilename, hStdin, hStdout, hStderr );

   if( hStdin != FS_ERROR )
   {
      hb_fsClose( hStdin );
      if( sTmpIn[ 0 ] )
         hb_fsDelete( sTmpIn );
   }
   if( hStdout != FS_ERROR )
   {
      if( pStdOutPtr && pulStdOut )
      {
         ulOutBuf = hb_fsSeek( hStdout, 0, FS_END );
         if( ulOutBuf )
         {
            pOutBuf = ( char * ) hb_xgrab( ulOutBuf + 1 );
            hb_fsSeek( hStdout, 0, FS_SET );
            ulOutBuf = hb_fsReadLarge( hStdout, pOutBuf, ulOutBuf );
         }
      }
      hb_fsClose( hStdout );
      if( sTmpOut[ 0 ] )
         hb_fsDelete( sTmpOut );
   }
   if( hStderr != FS_ERROR )
   {
      if( pStdErrPtr && pulStdErr )
      {
         ulErrBuf = hb_fsSeek( hStderr, 0, FS_END );
         if( ulErrBuf )
         {
            pErrBuf = ( char * ) hb_xgrab( ulErrBuf + 1 );
            hb_fsSeek( hStderr, 0, FS_SET );
            ulErrBuf = hb_fsReadLarge( hStderr, pErrBuf, ulErrBuf );
         }
      }
      hb_fsClose( hStderr );
      if( sTmpErr[ 0 ] )
         hb_fsDelete( sTmpErr );
   }
}

#else
{
   HB_FHANDLE hProcess;

   iResult = -1;
   hProcess = hb_fsProcessOpen( pszFilename, phStdin, phStdout, phStderr,
                                fDetach, NULL );
   if( hProcess != FS_ERROR )
   {
#if defined( HB_IO_WIN )

      DWORD dwResult, dwCount;
      HANDLE lpHandles[ 4 ];
      ULONG ul;

      if( ulStdInLen == 0 && hStdin != FS_ERROR )
      {
         hb_fsClose( hStdin );
         hStdin = FS_ERROR;
      }

      for( ;; )
      {
         dwCount = 0;
         if( ulStdInLen && hStdin != FS_ERROR )
            lpHandles[ dwCount++ ] = ( HANDLE ) hb_fsGetOsHandle( hStdin );
         if( hStdout != FS_ERROR )
            lpHandles[ dwCount++ ] = ( HANDLE ) hb_fsGetOsHandle( hStdout );
         if( hStderr != FS_ERROR )
            lpHandles[ dwCount++ ] = ( HANDLE ) hb_fsGetOsHandle( hStderr );

         lpHandles[ dwCount++ ] = ( HANDLE ) hb_fsGetOsHandle( hProcess );

         dwResult = WaitForMultipleObjects( dwCount, lpHandles, FALSE, INFINITE );

         if( /* dwResult >= WAIT_OBJECT_0 && */ dwResult < WAIT_OBJECT_0 + dwCount )
         {
            if( ulStdInLen && hStdin != FS_ERROR &&
                lpHandles[ dwResult ] == ( HANDLE ) hb_fsGetOsHandle( hStdin ) )
            {
               ul = hb_fsWriteLarge( hStdin, pStdInBuf, ulStdInLen );
               pStdInBuf += ul;
               ulStdInLen -= ul;
               if( ulStdInLen == 0 )
               {
                  hb_fsClose( hStdin );
                  hStdin = FS_ERROR;
               }
            }
            else if( hStdout != FS_ERROR &&
                     lpHandles[ dwResult ] == ( HANDLE ) hb_fsGetOsHandle( hStdout ) )
            {
               if( ulOutBuf == ulOutSize )
               {
                  ulOutSize += HB_STD_BUFFER_SIZE;
                  pOutBuf = ( char * ) hb_xrealloc( pOutBuf, ulOutSize + 1 );
               }
               ul = hb_fsReadLarge( hStdout, pOutBuf + ulOutBuf, ulOutSize - ulOutBuf );
               if( ul == 0 )
               {
                  hb_fsClose( hStdout );
                  hStdout = FS_ERROR;
               }
               else
                  ulOutBuf += ul;
            }
            else if( hStderr != FS_ERROR && lpHandles[ dwResult ] == ( HANDLE ) hb_fsGetOsHandle( hStderr ) )
            {
               if( ulErrBuf == ulErrSize )
               {
                  ulErrSize += HB_STD_BUFFER_SIZE;
                  pErrBuf = ( char * ) hb_xrealloc( pErrBuf, ulErrSize + 1 );
               }
               ul = hb_fsReadLarge( hStderr, pErrBuf + ulErrBuf, ulErrSize - ulErrBuf );
               if( ul == 0 )
               {
                  hb_fsClose( hStderr );
                  hStderr = FS_ERROR;
               }
               else
                  ulErrBuf += ul;
            }
            else if( lpHandles[ dwResult ] == ( HANDLE ) hb_fsGetOsHandle( hProcess ) )
            {
               if( GetExitCodeProcess( ( HANDLE ) hb_fsGetOsHandle( hProcess ), &dwResult ) )
                  iResult = ( int ) dwResult;
               else
                  iResult = -2;
               break;
            }
         }
         else
            break;
      }

      if( hStdin != FS_ERROR )
         hb_fsClose( hStdin );
      if( hStdout != FS_ERROR )
         hb_fsClose( hStdout );
      if( hStderr != FS_ERROR )
         hb_fsClose( hStderr );

#elif defined( HB_OS_UNIX )

      fd_set rfds, wfds, *prfds, *pwfds;
      HB_FHANDLE fdMax;
      ULONG ul;
      int n;

      if( ulStdInLen == 0 && hStdin != FS_ERROR )
      {
         hb_fsClose( hStdin );
         hStdin = FS_ERROR;
      }

      for( ;; )
      {
         fdMax = 0;
         prfds = pwfds = NULL;
         if( hStdout != FS_ERROR || hStderr != FS_ERROR )
         {
            FD_ZERO( &rfds );
            if( hStdout != FS_ERROR )
            {
               FD_SET( hStdout, &rfds );
               if( hStdout > fdMax )
                  fdMax = hStdout;
            }
            if( hStderr != FS_ERROR )
            {
               FD_SET( hStderr, &rfds );
               if( hStderr > fdMax )
                  fdMax = hStderr;
            }
            prfds = &rfds;
         }
         if( ulStdInLen && hStdin != FS_ERROR )
         {
            FD_ZERO( &wfds );
            FD_SET( hStdin, &wfds );
            if( hStdin > fdMax )
               fdMax = hStdin;
            pwfds = &wfds;
         }
         if( prfds == NULL && pwfds == NULL )
            break;

         hb_vmUnlock();
         n = select( fdMax + 1, prfds, pwfds, NULL, NULL );
         if( n > 0 )
         {
            if( ulStdInLen && hStdin != FS_ERROR && FD_ISSET( hStdin, &wfds ) )
            {
               ul = hb_fsWriteLarge( hStdin, pStdInBuf, ulStdInLen );
               pStdInBuf += ul;
               ulStdInLen -= ul;
               if( ulStdInLen == 0 )
               {
                  hb_fsClose( hStdin );
                  hStdin = FS_ERROR;
               }
            }

            if( hStdout != FS_ERROR && FD_ISSET( hStdout, &rfds ) )
            {
               if( ulOutBuf == ulOutSize )
               {
                  ulOutSize += HB_STD_BUFFER_SIZE;
                  pOutBuf = ( char * ) hb_xrealloc( pOutBuf, ulOutSize + 1 );
               }
               ul = hb_fsReadLarge( hStdout, pOutBuf + ulOutBuf, ulOutSize - ulOutBuf );
               if( ul == 0 )
               {
                  /* zero bytes read after positive select()
                   * - writing process closed the pipe
                   */
                  hb_fsClose( hStdout );
                  hStdout = FS_ERROR;
               }
               else
                  ulOutBuf += ul;
            }

            if( hStderr != FS_ERROR && FD_ISSET( hStderr, &rfds ) )
            {
               if( ulErrBuf == ulErrSize )
               {
                  ulErrSize += HB_STD_BUFFER_SIZE;
                  pErrBuf = ( char * ) hb_xrealloc( pErrBuf, ulErrSize + 1 );
               }
               ul = hb_fsReadLarge( hStderr, pErrBuf + ulErrBuf, ulErrSize - ulErrBuf );
               if( ul == 0 )
               {
                  /* zero bytes read after positive select()
                   * - writing process closed the pipe
                   */
                  hb_fsClose( hStderr );
                  hStderr = FS_ERROR;
               }
               else
                  ulErrBuf += ul;
            }
         }
         hb_vmLock();
      }

      if( hStdin != FS_ERROR )
         hb_fsClose( hStdin );
      if( hStdout != FS_ERROR )
         hb_fsClose( hStdout );
      if( hStderr != FS_ERROR )
         hb_fsClose( hStderr );

      iResult = hb_fsProcessValue( hProcess, TRUE );

#else

      int TODO;

      HB_SYMBOL_UNUSED( ulStdInLen );

#endif
   }
}
#endif

   if( phStdout )
   {
      *pStdOutPtr = pOutBuf;
      *pulStdOut = ulOutBuf;
   }
   if( phStderr )
   {
      *pStdErrPtr = pErrBuf;
      *pulStdErr = ulErrBuf;
   }

   if( pszFree )
      hb_xfree( pszFree );

   return iResult;
}

/* temporary hack for still missing sysconf() in Watcom 1.8 */
#if defined( HB_OS_LINUX ) && defined( __WATCOMC__ )
_WCRTLINK long sysconf( int __name )
{
   int iTODO;

   switch( __name )
   {
      case _SC_OPEN_MAX:
         return 1024;
      case _SC_CLK_TCK:
         return 100;
      case /* _SC_PAGE_SIZE */ 30:
         return 4096;
   }
   return -1;
}
#endif
