//------------------------------------------------------------------------------
// emProcess.h
//
// Copyright (C) 2006-2010,2014,2017-2018,2022 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// 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 version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emProcess_h
#define emProcess_h

#ifndef emStd2_h
#include <emCore/emStd2.h>
#endif

struct emProcessPrivate;


//==============================================================================
//================================= emProcess ==================================
//==============================================================================

class emProcess : public emUncopyable {

public:

        // This class helps in creating and managing system processes.

        emProcess();
                // Construct with no child process running.

        virtual ~emProcess();
                // If a child process is still running, Terminate() is called.

        enum StartFlags {
                SF_SHARE_STDIN    = 1<<0,
                SF_PIPE_STDIN     = 1<<1,
                SF_SHARE_STDOUT   = 1<<2,
                SF_PIPE_STDOUT    = 1<<3,
                SF_SHARE_STDERR   = 1<<4,
                SF_PIPE_STDERR    = 1<<5,
                SF_NO_WINDOW      = 1<<6,
                SF_USE_CTRL_BREAK = 1<<7
        };

        void TryStart(
                const emArray<emString> & args,
                const emArray<emString> & extraEnv=emArray<emString>(),
                const char * dirPath=NULL,
                int flags=SF_SHARE_STDIN|SF_SHARE_STDOUT|SF_SHARE_STDERR
        );
                // Start a managed child process.
                // Arguments:
                //   args     - Program arguments for the child process. The
                //              first entry is the program name itself, either
                //              as a file path, or just the name (PATH is
                //              searched then). The array must not be empty.
                //   extraEnv - The child process inherits the environment from
                //              this process, and in addition, it gets the
                //              environment variables given here. The array can
                //              have any number of environment variables, each
                //              of the form <Name>[=<value>]. If it's just the
                //              name, the variable is removed (on UNIX: only if
                //              supported by putenv), otherwise the variable is
                //              set or changed to the given value (which can be
                //              empty).
                //   dirPath  - Current working directory for the child process,
                //              or NULL for inheriting the current directory
                //              from this process.
                //   flags    - Combination of start flags from enum StartFlags.
                //              The flags SF_SHARE_STDIN and SF_PIPE_STDIN
                //              control the type of the standard input handle
                //              for the child process. Giving none of these
                //              flags means to have no standard input (closed
                //              handle). SF_SHARE_STDIN means to inherit the
                //              handle from this process. SF_PIPE_STDIN means to
                //              create a pipe (see methods TryWrite and
                //              CloseWrite). Setting both flags is not allowed.
                //              The other SF_*_STD* flags are for standard
                //              output and standard error analogously.
                //              SF_NO_WINDOW means to omit the console window of
                //              a console process on Windows. SF_USE_CTRL_BREAK
                //              means to terminate the process by a Ctrl+Break
                //              event instead of WM_QUIT on Windows. Therefore
                //              an intermediate adapter process is run
                //              (emWndsAdapterProc), which translates WM_QUIT to
                //              Ctrl+Break, assuming that this calling process
                //              is not a console process (an optimization for
                //              the case that the caller is a console process
                //              has not yet been implemented).
                // Throws: An error message on failure.

        static void TryStartUnmanaged(
                const emArray<emString> & args,
                const emArray<emString> & extraEnv=emArray<emString>(),
                const char * dirPath=NULL,
                int flags=SF_SHARE_STDIN|SF_SHARE_STDOUT|SF_SHARE_STDERR
        );
                // This function(!) is like the method TryStart, but the new
                // process is not managed by an emProcess object. Pipelining is
                // not possible with this. On UNIX, the new process is detached
                // from this process, so that it does not become a child
                // process.

        int TryWrite(const char * buf, int len);
                // Write to standard input of the child process, without
                // blocking. The child process should have been started with
                // SF_PIPE_STDIN.
                // Arguments:
                //   buf - Array of bytes to be written.
                //   len - Number of bytes to be written.
                // Returns:
                //   >0 - Number of bytes actually written.
                //    0 - No writing possible at this moment, please try again
                //        later (or wait by calling WaitPipes).
                //   -1 - Any end of the pipe has been closed (e.g. child
                //        process exited).
                // Throws: An error message on failure.

        int TryRead(char * buf, int maxLen);
                // Read from standard output of the child process, without
                // blocking. The child process should have been started with
                // SF_PIPE_STDOUT.
                // Arguments:
                //   buf - Array for storing the bytes.
                //   len - Maximum number of bytes to be read.
                // Returns:
                //   >0 - Number of bytes actually read.
                //    0 - No bytes available at this moment, please try again
                //        later (or wait by calling WaitPipes).
                //   -1 - Any end of the pipe has been closed (e.g. child
                //        process exited).
                // Throws: An error message on failure.

        int TryReadErr(char * buf, int maxLen);
                // Like TryRead, but for standard error (SF_PIPE_STDERR).

        enum WaitFlags {
                WF_WAIT_STDIN  = 1<<0,
                WF_WAIT_STDOUT = 1<<1,
                WF_WAIT_STDERR = 1<<2
        };
        void WaitPipes(int waitFlags, unsigned timeoutMS=UINT_MAX);
                // Wait until a pipe is ready for writing and/or reading.
                // Arguments:
                //   waitFlags - Combination of flags from enum WaitFlags. This
                //               specifies the pipes to wait for. The method
                //               returns when at least one of these pipes is
                //               ready.
                //   timeoutMS - After this time-out in milliseconds, the
                //               method returns even if no pipe is ready.
                //               UINT_MAX means infinite.

        void CloseWriting();
        void CloseReading();
        void CloseReadingErr();
                // Close this end of a pipe.

        void SendTerminationSignal();
                // Send a termination request to the child process. On UNIX, the
                // signal SIGTERM is sent to the child process. On Windows, the
                // message WM_QUIT is sent to the primary thread of the child
                // process. Note that this does not work with a normal Windows
                // console program when it is not explicitly programmed for
                // receiving WM_QUIT. For having an intermediate process that
                // translates WM_QUIT to Ctrl+Break, please set the
                // SF_USE_CTRL_BREAK flag when calling TryStart.

        void SendKillSignal();
                // Hardly kill the child process. Usually this should never be
                // called. On UNIX, the signal SIGKILL is sent to the child
                // process. On Windows, the SDK function TerminateProcess is
                // called.

        bool WaitForTermination(unsigned timeoutMS=UINT_MAX);
                // Wait for the child process to terminate.
                // Arguments:
                //   timeoutMS - Time-out in milliseconds. UINT_MAX means
                //               infinite.
                // Returns:
                //   true  - Child process terminated (or never started).
                //   false - Timed out.

        bool IsRunning();
                // true if a child process has been started and not yet
                // terminated.

        void Terminate(unsigned fatalTimeoutMS=20000);
                // Like SendTerminationSignal plus WaitForTermination. But if
                // the process does not terminate within the given time-out in
                // milliseconds, emFatalError is called (because it is assumed
                // to have a programming error somewhere). The time-out should
                // be quite large, for the case of a badly overloaded system.

        int GetExitStatus() const;
                // Get the exit status of a terminated child process.

private:

        emProcessPrivate * P;

};


#endif