1. wxProcess changes to make capturing subprocess output easier (and more

reliable), now works in both sync and async modes
2. wxSafeYieldBug() corrected, wxWindowDisabler which is now used in it
   added and documented
3. exec sample updated to illustrate capturing the subprocess output
4. wxStreamBase::IsOk() added
5. wxInputStream::Eof() added and non-blocking Eof() implementation in
   wxPipeInputStream used by wxExecute


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@6400 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2000-03-02 19:06:13 +00:00
parent 21e5527b59
commit cd6ce4a90c
16 changed files with 570 additions and 297 deletions

View File

@@ -26,7 +26,9 @@ None
\wxheading{See also} \wxheading{See also}
\helpref{wxBeginBusyCursor}{wxbeginbusycursor}, \helpref{wxEndBusyCursor}{wxendbusycursor} \helpref{wxBeginBusyCursor}{wxbeginbusycursor},\rtfsp
\helpref{wxEndBusyCursor}{wxendbusycursor},\rtfsp
\helpref{wxWindowDisabler}{wxwindowdisabler}
\latexignore{\rtfignore{\wxheading{Members}}} \latexignore{\rtfignore{\wxheading{Members}}}

View File

@@ -1117,7 +1117,7 @@ registered with the dynamic class system using DECLARE... and IMPLEMENT... macro
Called when wxWindows exits, to clean up the DDE system. This no longer needs to be Called when wxWindows exits, to clean up the DDE system. This no longer needs to be
called by the application. called by the application.
See also helpref{wxDDEInitialize}{wxddeinitialize}. See also \helpref{wxDDEInitialize}{wxddeinitialize}.
\wxheading{Include files} \wxheading{Include files}
@@ -1259,6 +1259,8 @@ wxWindows errors. See also \helpref{wxFatalError}{wxfatalerror}.
\func{long}{wxExecute}{\param{char **}{argv}, \param{bool }{sync = FALSE}, \param{wxProcess *}{callback = NULL}} \func{long}{wxExecute}{\param{char **}{argv}, \param{bool }{sync = FALSE}, \param{wxProcess *}{callback = NULL}}
\func{long}{wxExecute}{\param{const wxString\& }{command}, \param{wxArrayString\& }{output}}
Executes another program in Unix or Windows. Executes another program in Unix or Windows.
The first form takes a command string, such as {\tt "emacs file.txt"}. The first form takes a command string, such as {\tt "emacs file.txt"}.
@@ -1266,6 +1268,9 @@ The first form takes a command string, such as {\tt "emacs file.txt"}.
The second form takes an array of values: a command, any number of The second form takes an array of values: a command, any number of
arguments, terminated by NULL. arguments, terminated by NULL.
The semantics of the third version is different from the first two and is
described in more details below.
If {\it sync} is FALSE (the default), flow of control immediately returns. If {\it sync} is FALSE (the default), flow of control immediately returns.
If TRUE, the current application waits until the other program has terminated. If TRUE, the current application waits until the other program has terminated.
@@ -1285,6 +1290,10 @@ parameter can not be non NULL for synchronous execution),
\helpref{wxProcess::OnTerminate}{wxprocessonterminate} will be called when \helpref{wxProcess::OnTerminate}{wxprocessonterminate} will be called when
the process finishes. the process finishes.
Finally, you may use the third overloaded version of this function to execute
a process (always synchronously) and capture its output in the array
{\it output}.
See also \helpref{wxShell}{wxshell}, \helpref{wxProcess}{wxprocess}, See also \helpref{wxShell}{wxshell}, \helpref{wxProcess}{wxprocess},
\helpref{Exec sample}{sampleexec}. \helpref{Exec sample}{sampleexec}.

View File

@@ -34,6 +34,12 @@ Destructor.
Returns the first character in the input queue and removes it. Returns the first character in the input queue and removes it.
\membersection{wxInputStream::Eof}\label{wxinputstreameof}
\constfunc{wxInputStream}{Eof}{\void}
Returns TRUE if the end of stream has been reached.
\membersection{wxInputStream::LastRead}\label{wxinputstreamlastread} \membersection{wxInputStream::LastRead}\label{wxinputstreamlastread}
\constfunc{size\_t}{LastRead}{\void} \constfunc{size\_t}{LastRead}{\void}

View File

@@ -90,7 +90,7 @@ accordingly until all requested data is read.
\wxheading{Return value} \wxheading{Return value}
It returns the size of the data read. If thereturned size is different of the specified It returns the size of the data read. If the returned size is different of the specified
{\it size}, an error has occured and should be tested using {\it size}, an error has occured and should be tested using
\helpref{LastError}{wxstreambaselasterror}. \helpref{LastError}{wxstreambaselasterror}.

View File

@@ -39,6 +39,16 @@ Creates a dummy stream object. It doesn't do anything.
Destructor. Destructor.
\membersection{wxStreamBase::IsOk}\label{wxstreambaseisok}
\constfunc{wxStreamError}{IsOk}{\void}
Returns TRUE if no error occured on the stream.
\wxheading{See also}
\helpref{LastError}{wxstreambaselasterror}
\membersection{wxStreamBase::LastError}\label{wxstreambaselasterror} \membersection{wxStreamBase::LastError}\label{wxstreambaselasterror}
\constfunc{wxStreamError}{LastError}{\void} \constfunc{wxStreamError}{LastError}{\void}
@@ -84,7 +94,7 @@ See \helpref{OnSysRead}{wxstreambaseonsysread}.
\constfunc{size\_t}{GetSize}{\void} \constfunc{size\_t}{GetSize}{\void}
This function returns the size of the stream. For example, for a file it is the size of This function returns the size of the stream. For example, for a file it is the size of
the file). the file.
\wxheading{Warning} \wxheading{Warning}

View File

@@ -0,0 +1,31 @@
\section{\class{wxWindowDisabler}}\label{wxwindowdisabler}
This class disables all windows of the application (may be with the exception
of one of them) in its constructor and enables them back in its destructor.
This comes in handy when you want to indicate to the user that the application
is currently busy and cannot respond to user input.
\wxheading{Derived from}
None
\wxheading{Include files}
<wx/utils.h>
\wxheading{See also}
\helpref{wxBusyCursor}{wxbusycursor}
\latexignore{\rtfignore{\wxheading{Members}}}
\membersection{wxWindowDisabler::wxWindowDisabler}
\func{}{wxWindowDisabler}{\param{wxWindow *}{winToSkip = NULL}}
Disables all top level windows of the applications with the exception of
{\it winToSkip} if it is not {\tt NULL}.
\membersection{wxWindowDisabler::\destruct{wxWindowDisabler}}
Reenables back the windows disabled by the constructor.

View File

@@ -52,30 +52,42 @@ class WXDLLEXPORT wxProcess : public wxEvtHandler
DECLARE_DYNAMIC_CLASS(wxProcess) DECLARE_DYNAMIC_CLASS(wxProcess)
public: public:
wxProcess(wxEvtHandler *parent = (wxEvtHandler *) NULL, bool needPipe = FALSE, int id = -1); wxProcess(wxEvtHandler *parent = (wxEvtHandler *) NULL, int id = -1)
~wxProcess(); { Init(parent, id, FALSE); }
wxProcess(wxEvtHandler *parent, bool redirect)
{ Init(parent, -1, redirect); }
virtual ~wxProcess();
// may be overridden to be notified about process termination
virtual void OnTerminate(int pid, int status); virtual void OnTerminate(int pid, int status);
// call this before passing the object to wxExecute() to redirect the
// launched process stdin/stdout, then use GetInputStream() and
// GetOutputStream() to get access to them
void Redirect() { m_redirect = TRUE; }
bool IsRedirected() const { return m_redirect; }
// detach from the parent - should be called by the parent if it's deleted // detach from the parent - should be called by the parent if it's deleted
// before the process it started terminates // before the process it started terminates
void Detach(); void Detach();
// Pipe handling // Pipe handling
wxInputStream *GetInputStream() const; wxInputStream *GetInputStream() const { return m_inputStream; }
wxOutputStream *GetOutputStream() const; wxOutputStream *GetOutputStream() const { return m_outputStream; }
// These functions should not be called by the usual user. They are only // implementation only (for wxExecute)
// intended to be used by wxExecute. void SetPipeStreams(wxInputStream *inStream, wxOutputStream *outStream);
// Install pipes
void SetPipeStreams(wxInputStream *in_stream, wxOutputStream *out_stream);
bool NeedPipe() const;
protected: protected:
void Init(wxEvtHandler *parent, int id, bool redirect);
int m_id; int m_id;
bool m_needPipe;
wxInputStream *m_in_stream; wxInputStream *m_inputStream;
wxOutputStream *m_out_stream; wxOutputStream *m_outputStream;
bool m_redirect;
}; };
typedef void (wxObject::*wxProcessEventFunction)(wxProcessEvent&); typedef void (wxObject::*wxProcessEventFunction)(wxProcessEvent&);

View File

@@ -38,12 +38,8 @@ WXDLLEXPORT wxOutputStream& wxEndL(wxOutputStream& o_stream);
// wxStream: base classes // wxStream: base classes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#define wxStream_NOERROR wxSTREAM_NOERROR typedef enum
#define wxStream_EOF wxSTREAM_EOF {
#define wxStream_WRITE_ERR wxSTREAM_WRITE_ERROR
#define wxStream_READ_ERR wxSTREAM_READ_ERROR
typedef enum {
wxSTREAM_NO_ERROR = 0, wxSTREAM_NO_ERROR = 0,
wxSTREAM_NO_ERR = wxSTREAM_NO_ERROR, wxSTREAM_NO_ERR = wxSTREAM_NO_ERROR,
wxSTREAM_NOERROR = wxSTREAM_NO_ERROR, wxSTREAM_NOERROR = wxSTREAM_NO_ERROR,
@@ -58,14 +54,24 @@ typedef enum {
} wxStreamError; } wxStreamError;
// compatibility
#define wxStream_NOERROR wxSTREAM_NOERROR
#define wxStream_EOF wxSTREAM_EOF
#define wxStream_WRITE_ERR wxSTREAM_WRITE_ERROR
#define wxStream_READ_ERR wxSTREAM_READ_ERROR
class WXDLLEXPORT wxStreamBase class WXDLLEXPORT wxStreamBase
{ {
public: public:
wxStreamBase(); wxStreamBase();
virtual ~wxStreamBase(); virtual ~wxStreamBase();
bool operator!() const { return (LastError() != wxSTREAM_NOERROR); } // error testing
wxStreamError LastError() const { return m_lasterror; } wxStreamError LastError() const { return m_lasterror; }
wxStreamError GetLastError() const { return m_lasterror; }
bool IsOk() const { return LastError() == wxSTREAM_NOERROR; }
bool operator!() const { return LastError() != wxSTREAM_NOERROR; }
virtual size_t GetSize() const { return ~((size_t)0); } virtual size_t GetSize() const { return ~((size_t)0); }
size_t StreamSize() const { return GetSize(); } size_t StreamSize() const { return GetSize(); }
@@ -87,6 +93,9 @@ public:
wxInputStream(); wxInputStream();
virtual ~wxInputStream(); virtual ~wxInputStream();
// is the stream at EOF?
virtual bool Eof() const;
// IO functions // IO functions
virtual char Peek(); virtual char Peek();
char GetC(); char GetC();

View File

@@ -39,6 +39,7 @@
class WXDLLEXPORT wxProcess; class WXDLLEXPORT wxProcess;
class WXDLLEXPORT wxFrame; class WXDLLEXPORT wxFrame;
class WXDLLEXPORT wxWindow; class WXDLLEXPORT wxWindow;
class WXDLLEXPORT wxWindowList;
// FIXME should use wxStricmp() instead // FIXME should use wxStricmp() instead
#if defined(__GNUWIN32__) #if defined(__GNUWIN32__)
@@ -146,6 +147,9 @@ WXDLLEXPORT long wxExecute(wxChar **argv, bool sync = FALSE,
WXDLLEXPORT long wxExecute(const wxString& command, bool sync = FALSE, WXDLLEXPORT long wxExecute(const wxString& command, bool sync = FALSE,
wxProcess *process = (wxProcess *) NULL); wxProcess *process = (wxProcess *) NULL);
// execute the command capturing its output into an array line by line
WXDLLEXPORT long wxExecute(const wxString& command, wxArrayString& output);
enum wxSignal enum wxSignal
{ {
wxSIGNONE = 0, // verify if the process exists under Unix wxSIGNONE = 0, // verify if the process exists under Unix
@@ -272,6 +276,18 @@ WXDLLEXPORT bool wxCheckForInterrupt(wxWindow *wnd);
// Consume all events until no more left // Consume all events until no more left
WXDLLEXPORT void wxFlushEvents(); WXDLLEXPORT void wxFlushEvents();
// a class which disables all windows (except, may be, thegiven one) in its
// ctor and enables them back in its dtor
class WXDLLEXPORT wxWindowDisabler
{
public:
wxWindowDisabler(wxWindow *winToSkip = (wxWindow *)NULL);
~wxWindowDisabler();
private:
wxWindowList *m_winDisabled;
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Cursors // Cursors
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -66,6 +66,10 @@ public:
virtual bool OnInit(); virtual bool OnInit();
}; };
// Define an array of process pointers used by MyFrame
class MyPipedProcess;
WX_DEFINE_ARRAY(MyPipedProcess *, MyProcessesArray);
// Define a new frame type: this is going to be our main frame // Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame class MyFrame : public wxFrame
{ {
@@ -86,7 +90,12 @@ public:
void OnAbout(wxCommandEvent& event); void OnAbout(wxCommandEvent& event);
// polling output of async processes
void OnIdle(wxIdleEvent& event);
// for MyPipedProcess // for MyPipedProcess
void OnProcessTerminated(MyPipedProcess *process)
{ m_running.Remove(process); }
wxListBox *GetLogListBox() const { return m_lbox; } wxListBox *GetLogListBox() const { return m_lbox; }
private: private:
@@ -94,6 +103,8 @@ private:
wxListBox *m_lbox; wxListBox *m_lbox;
MyProcessesArray m_running;
// any class wishing to process wxWindows events must use this macro // any class wishing to process wxWindows events must use this macro
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };
@@ -125,10 +136,12 @@ public:
MyPipedProcess(MyFrame *parent, const wxString& cmd) MyPipedProcess(MyFrame *parent, const wxString& cmd)
: MyProcess(parent, cmd) : MyProcess(parent, cmd)
{ {
m_needPipe = TRUE; Redirect();
} }
virtual void OnTerminate(int pid, int status); virtual void OnTerminate(int pid, int status);
bool HasInput();
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -169,6 +182,8 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec) EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
EVT_MENU(Exec_About, MyFrame::OnAbout) EVT_MENU(Exec_About, MyFrame::OnAbout)
EVT_IDLE(MyFrame::OnIdle)
END_EVENT_TABLE() END_EVENT_TABLE()
// Create a new application object: this macro will allow wxWindows to create // Create a new application object: this macro will allow wxWindows to create
@@ -347,17 +362,58 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
if ( !cmd ) if ( !cmd )
return; return;
wxProcess *process = new MyPipedProcess(this, cmd); bool sync;
if ( !wxExecute(cmd, FALSE /* async */, process) ) switch ( wxMessageBox(_T("Execute it synchronously?"),
_T("Exec question"),
wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
{ {
wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); case wxYES:
sync = TRUE;
break;
delete process; case wxNO:
sync = FALSE;
break;
default:
return;
} }
else
if ( sync )
{ {
m_cmdLast = cmd; wxArrayString output;
int code = wxExecute(cmd, output);
wxLogStatus(_T("command '%s' terminated with exit code %d."),
cmd.c_str(), code);
if ( code != -1 )
{
m_lbox->Append(wxString::Format(_T("--- Output of '%s' ---"),
cmd.c_str()));
size_t count = output.GetCount();
for ( size_t n = 0; n < count; n++ )
{
m_lbox->Append(output[n]);
}
}
} }
else // async exec
{
MyPipedProcess *process = new MyPipedProcess(this, cmd);
if ( !wxExecute(cmd, FALSE /* async */, process) )
{
wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
delete process;
}
else
{
m_running.Add(process);
}
}
m_cmdLast = cmd;
} }
void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
@@ -402,6 +458,19 @@ void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
#endif // __WINDOWS__ #endif // __WINDOWS__
} }
// input polling
void MyFrame::OnIdle(wxIdleEvent& event)
{
size_t count = m_running.GetCount();
for ( size_t n = 0; n < count; n++ )
{
if ( m_running[n]->HasInput() )
{
event.RequestMore();
}
}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// MyProcess // MyProcess
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -415,17 +484,38 @@ void MyProcess::OnTerminate(int pid, int status)
delete this; delete this;
} }
// ----------------------------------------------------------------------------
// MyPipedProcess
// ----------------------------------------------------------------------------
bool MyPipedProcess::HasInput()
{
wxInputStream& is = *GetInputStream();
if ( !is.Eof() )
{
wxTextInputStream tis(is);
// this assumes that the output is always line buffered
wxString msg;
msg << m_cmd << _T(": ") << tis.ReadLine();
m_parent->GetLogListBox()->Append(msg);
return TRUE;
}
else
{
return FALSE;
}
}
void MyPipedProcess::OnTerminate(int pid, int status) void MyPipedProcess::OnTerminate(int pid, int status)
{ {
// show the program output // show the rest of the output
wxListBox *lbox = m_parent->GetLogListBox(); while ( HasInput() )
lbox->Append(wxString::Format(_T("--- Output of '%s' ---"), m_cmd.c_str())); ;
wxTextInputStream tis(*m_in_stream); m_parent->OnProcessTerminated(this);
while ( !m_in_stream->LastError() )
{
lbox->Append(tis.ReadLine());
}
MyProcess::OnTerminate(pid, status); MyProcess::OnTerminate(pid, status);
} }

View File

@@ -29,23 +29,21 @@
IMPLEMENT_DYNAMIC_CLASS(wxProcess, wxEvtHandler) IMPLEMENT_DYNAMIC_CLASS(wxProcess, wxEvtHandler)
IMPLEMENT_DYNAMIC_CLASS(wxProcessEvent, wxEvent) IMPLEMENT_DYNAMIC_CLASS(wxProcessEvent, wxEvent)
wxProcess::wxProcess(wxEvtHandler *parent, bool needPipe, int id) void wxProcess::Init(wxEvtHandler *parent, int id, bool redirect)
{ {
if (parent) if ( parent )
SetNextHandler(parent); SetNextHandler(parent);
m_id = id; m_id = id;
m_needPipe = needPipe; m_redirect = redirect;
m_in_stream = NULL; m_inputStream = NULL;
m_out_stream = NULL; m_outputStream = NULL;
} }
wxProcess::~wxProcess() wxProcess::~wxProcess()
{ {
if (m_in_stream) delete m_inputStream;
delete m_in_stream; delete m_outputStream;
if (m_out_stream)
delete m_out_stream;
} }
void wxProcess::OnTerminate(int pid, int status) void wxProcess::OnTerminate(int pid, int status)
@@ -65,21 +63,7 @@ void wxProcess::Detach()
void wxProcess::SetPipeStreams(wxInputStream *in_stream, wxOutputStream *out_stream) void wxProcess::SetPipeStreams(wxInputStream *in_stream, wxOutputStream *out_stream)
{ {
m_in_stream = in_stream; m_inputStream = in_stream;
m_out_stream = out_stream; m_outputStream = out_stream;
} }
wxInputStream *wxProcess::GetInputStream() const
{
return m_in_stream;
}
wxOutputStream *wxProcess::GetOutputStream() const
{
return m_out_stream;
}
bool wxProcess::NeedPipe() const
{
return m_needPipe;
}

View File

@@ -494,6 +494,22 @@ wxInputStream::~wxInputStream()
free(m_wback); free(m_wback);
} }
bool wxInputStream::Eof() const
{
wxInputStream *self = (wxInputStream *)this; // const_cast
char c;
self->Read(&c, 1);
if ( GetLastError() == wxSTREAM_EOF )
{
return TRUE;
}
self->Ungetch(c);
return FALSE;
}
char *wxInputStream::AllocSpaceWBack(size_t needed_size) char *wxInputStream::AllocSpaceWBack(size_t needed_size)
{ {
/* get number of bytes left from previous wback buffer */ /* get number of bytes left from previous wback buffer */

View File

@@ -6,7 +6,7 @@
// Created: 28/06/98 // Created: 28/06/98
// RCS-ID: $Id$ // RCS-ID: $Id$
// Copyright: (c) Guilhem Lavaux // Copyright: (c) Guilhem Lavaux
// Licence: wxWindows license // Licence: wxWindows license
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__ #ifdef __GNUG__
@@ -52,13 +52,13 @@ wxChar wxTextInputStream::NextNonSeparators()
wxChar c = (wxChar) 0; wxChar c = (wxChar) 0;
for (;;) for (;;)
{ {
if (!m_input) return (wxChar) 0; if (!m_input) return (wxChar) 0;
c = m_input.GetC(); c = m_input.GetC();
if (c != wxT('\n') && if (c != wxT('\n') &&
c != wxT('\r') && c != wxT('\r') &&
!m_separators.Contains(c)) !m_separators.Contains(c))
return c; return c;
} }
} }
@@ -88,6 +88,8 @@ void wxTextInputStream::SkipIfEndOfLine( wxChar c )
wxUint32 wxTextInputStream::Read32() wxUint32 wxTextInputStream::Read32()
{ {
/* I only implemented a simple integer parser */ /* I only implemented a simple integer parser */
// VZ: what about using strtol()?? (TODO)
int sign; int sign;
wxInt32 i; wxInt32 i;
@@ -142,6 +144,7 @@ wxUint8 wxTextInputStream::Read8()
double wxTextInputStream::ReadDouble() double wxTextInputStream::ReadDouble()
{ {
/* I only implemented a simple float parser */ /* I only implemented a simple float parser */
// VZ: what about using strtod()?? (TODO)
double f; double f;
int sign; int sign;
@@ -184,32 +187,32 @@ double wxTextInputStream::ReadDouble()
c = m_input.GetC(); c = m_input.GetC();
while (isdigit(c)) while (isdigit(c))
{ {
f += (c-wxT('0'))*f_multiplicator; f += (c-wxT('0'))*f_multiplicator;
f_multiplicator /= 10; f_multiplicator /= 10;
c = m_input.GetC(); c = m_input.GetC();
} }
if (c == wxT('e')) if (c == wxT('e'))
{ {
double f_multiplicator = 0.0; double f_multiplicator = 0.0;
int i, e; int i, e;
c = m_input.GetC(); c = m_input.GetC();
switch (c) switch (c)
{ {
case wxT('-'): f_multiplicator = 0.1; break; case wxT('-'): f_multiplicator = 0.1; break;
case wxT('+'): f_multiplicator = 10.0; break; case wxT('+'): f_multiplicator = 10.0; break;
} }
e = Read8(); // why only max 256 ? e = Read8(); // why only max 256 ?
for (i=0;i<e;i++) for (i=0;i<e;i++)
f *= f_multiplicator; f *= f_multiplicator;
} }
else else
SkipIfEndOfLine( c ); SkipIfEndOfLine( c );
} }
else else
{ {
@@ -223,7 +226,7 @@ double wxTextInputStream::ReadDouble()
wxString wxTextInputStream::ReadString() wxString wxTextInputStream::ReadString()
{ {
return ReadLine(); return ReadLine();
} }
wxString wxTextInputStream::ReadLine() wxString wxTextInputStream::ReadLine()
@@ -231,12 +234,14 @@ wxString wxTextInputStream::ReadLine()
wxChar c; wxChar c;
wxString line; wxString line;
for (;;) while ( !m_input.Eof() )
{ {
if (!m_input) break;
c = m_input.GetC(); c = m_input.GetC();
if ( !m_input )
break;
if (EatEOL(c)) break; if (EatEOL(c))
break;
line += c; line += c;
} }
@@ -246,22 +251,28 @@ wxString wxTextInputStream::ReadLine()
wxString wxTextInputStream::ReadWord() wxString wxTextInputStream::ReadWord()
{ {
if (!m_input) return "";
wxString word; wxString word;
wxChar c=NextNonSeparators();
if (c==(wxChar)0) return "";
for (;;) if ( !m_input )
return word;
wxChar c = NextNonSeparators();
if ( !c )
return word;
while ( !m_input.Eof() )
{ {
if (m_separators.Contains(c)) break; if (m_separators.Contains(c))
break;
if (EatEOL(c)) break; if (EatEOL(c))
break;
word += c; word += c;
if (!m_input) break;
c = m_input.GetC(); c = m_input.GetC();
if (!m_input)
break;
} }
return word; return word;
@@ -269,8 +280,8 @@ wxString wxTextInputStream::ReadWord()
wxTextInputStream& wxTextInputStream::operator>>(wxString& word) wxTextInputStream& wxTextInputStream::operator>>(wxString& word)
{ {
word = ReadWord(); word = ReadWord();
return *this; return *this;
} }
wxTextInputStream& wxTextInputStream::operator>>(wxChar& c) wxTextInputStream& wxTextInputStream::operator>>(wxChar& c)
@@ -397,19 +408,19 @@ void wxTextOutputStream::WriteString(const wxString& string)
wxChar c = string[i]; wxChar c = string[i];
if (c == wxT('\n')) if (c == wxT('\n'))
{ {
if (m_mode == wxEOL_DOS) if (m_mode == wxEOL_DOS)
{ {
c = wxT('\r'); c = wxT('\r');
m_output.Write( (const void*)(&c), sizeof(wxChar) ); m_output.Write( (const void*)(&c), sizeof(wxChar) );
c = wxT('\n'); c = wxT('\n');
m_output.Write( (const void*)(&c), sizeof(wxChar) ); m_output.Write( (const void*)(&c), sizeof(wxChar) );
} else } else
if (m_mode == wxEOL_MAC) if (m_mode == wxEOL_MAC)
{ {
c = wxT('\r'); c = wxT('\r');
m_output.Write( (const void*)(&c), sizeof(wxChar) ); m_output.Write( (const void*)(&c), sizeof(wxChar) );
} else } else
{ {
c = wxT('\n'); c = wxT('\n');
m_output.Write( (const void*)(&c), sizeof(wxChar) ); m_output.Write( (const void*)(&c), sizeof(wxChar) );
} }

View File

@@ -48,6 +48,9 @@
#endif // wxUSE_GUI #endif // wxUSE_GUI
#endif // WX_PRECOMP #endif // WX_PRECOMP
#include "wx/process.h"
#include "wx/txtstrm.h"
#include <ctype.h> #include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -965,46 +968,61 @@ static void wxFindDisabledWindows(wxWindowList& winDisabled, wxWindow *win)
for ( node = win->GetChildren().GetFirst(); node; node = node->GetNext() ) for ( node = win->GetChildren().GetFirst(); node; node = node->GetNext() )
{ {
wxWindow *child = node->GetData(); wxWindow *child = node->GetData();
wxFindDisabledWindows(winDisabled, child);
if ( child->IsEnabled() ) if ( child->IsEnabled() )
{ {
winDisabled.Append(child); winDisabled.Append(child);
child->Disable();
} }
wxFindDisabledWindows(winDisabled, child);
} }
} }
wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
{
// remember all windows we're going to (temporarily) disable
m_winDisabled = new wxWindowList;
wxWindowList::Node *node;
for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
{
wxWindow *winTop = node->GetData();
if ( winTop->IsEnabled() )
{
wxFindDisabledWindows(*m_winDisabled, winTop);
m_winDisabled->Append(winTop);
winTop->Disable();
}
}
if ( winToSkip && m_winDisabled->Find(winToSkip) )
{
// always enable ourselves
m_winDisabled->DeleteObject(winToSkip);
winToSkip->Enable();
}
}
wxWindowDisabler::~wxWindowDisabler()
{
wxWindowList::Node *node;
for ( node = m_winDisabled->GetFirst(); node; node = node->GetNext() )
{
node->GetData()->Enable();
}
delete m_winDisabled;
}
// Yield to other apps/messages and disable user input to all windows except // Yield to other apps/messages and disable user input to all windows except
// the given one // the given one
bool wxSafeYield(wxWindow *win) bool wxSafeYield(wxWindow *win)
{ {
// remember all windows we're going to (temporarily) disable wxWindowDisabler wd;
wxWindowList winDisabled;
wxWindowList::Node *node;
for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
{
wxWindow *winTop = node->GetData();
wxFindDisabledWindows(winDisabled, winTop);
winTop->Disable();
}
if ( win )
{
// always enable ourselves
win->Enable();
}
bool rc = wxYield(); bool rc = wxYield();
// don't call wxEnableTopLevelWindows(TRUE) because this will reenable even
// the window which had been disabled before, do it manually instead
for ( node = winDisabled.GetFirst(); node; node = node->GetNext() )
{
node->GetData()->Enable();
}
return rc; return rc;
} }
@@ -1154,3 +1172,31 @@ wxString wxGetCurrentDir()
} }
#endif // 0 #endif // 0
// ----------------------------------------------------------------------------
// wxExecute
// ----------------------------------------------------------------------------
long wxExecute(const wxString& command, wxArrayString& output)
{
// create a wxProcess which will capture the output
wxProcess *process = new wxProcess;
process->Redirect();
long rc = wxExecute(command, TRUE /* sync */, process);
if ( rc != -1 )
{
wxInputStream& is = *process->GetInputStream();
wxTextInputStream tis(is);
while ( !is.Eof() )
{
wxString line = tis.ReadLine();
if ( is.LastError() )
break;
output.Add(line);
}
}
return rc;
}

View File

@@ -408,25 +408,29 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
HANDLE h_oldwritePipe; HANDLE h_oldwritePipe;
BOOL inheritHandles; BOOL inheritHandles;
// ------------------------------------ // open the pipes to which child process IO will be redirected if needed
// Pipe handling
// We are in the case of opening a pipe
inheritHandles = FALSE; inheritHandles = FALSE;
if (handler && handler->NeedPipe()) { if ( handler && handler->IsRedirected() )
{
SECURITY_ATTRIBUTES security; SECURITY_ATTRIBUTES security;
security.nLength = sizeof(security); security.nLength = sizeof(security);
security.lpSecurityDescriptor = NULL; security.lpSecurityDescriptor = NULL;
security.bInheritHandle = TRUE; security.bInheritHandle = TRUE;
if (! ::CreatePipe(&h_readPipe[0], &h_readPipe[1], &security, 0) ) { if (! ::CreatePipe(&h_readPipe[0], &h_readPipe[1], &security, 0) )
wxLogSysError(_T("Can't create the inter-process read pipe")); {
wxLogSysError(_("Can't create the inter-process read pipe"));
return 0; return 0;
} }
if (! ::CreatePipe(&h_writePipe[0], &h_writePipe[1], &security, 0) ) { if (! ::CreatePipe(&h_writePipe[0], &h_writePipe[1], &security, 0) )
wxLogSysError(_T("Can't create the inter-process read pipe")); {
::CloseHandle(h_readPipe[0]);
::CloseHandle(h_readPipe[1]);
wxLogSysError(_("Can't create the inter-process write pipe"));
return 0; return 0;
} }
@@ -464,12 +468,14 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
&pi // process info &pi // process info
) == 0 ) ) == 0 )
{ {
if (inheritHandles) { if ( inheritHandles )
{
::CloseHandle(h_writePipe[0]); ::CloseHandle(h_writePipe[0]);
::CloseHandle(h_writePipe[1]); ::CloseHandle(h_writePipe[1]);
::CloseHandle(h_readPipe[0]); ::CloseHandle(h_readPipe[0]);
::CloseHandle(h_readPipe[1]); ::CloseHandle(h_readPipe[1]);
} }
wxLogSysError(_("Execution of command '%s' failed"), command.c_str()); wxLogSysError(_("Execution of command '%s' failed"), command.c_str());
return 0; return 0;
@@ -589,16 +595,17 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
// waiting until command executed (disable everything while doing it) // waiting until command executed (disable everything while doing it)
#if wxUSE_GUI #if wxUSE_GUI
wxBeginBusyCursor(); {
wxEnableTopLevelWindows(FALSE); wxBusyCursor bc;
wxWindowDisabler wd;
#endif // wxUSE_GUI #endif // wxUSE_GUI
while ( data->state ) while ( data->state )
wxYield(); wxYield();
#if wxUSE_GUI #if wxUSE_GUI
wxEnableTopLevelWindows(TRUE); }
wxEndBusyCursor();
#endif // wxUSE_GUI #endif // wxUSE_GUI
DWORD dwExitCode = data->dwExitCode; DWORD dwExitCode = data->dwExitCode;

View File

@@ -208,8 +208,8 @@ long wxExecute( const wxString& command, bool sync, wxProcess *process )
bool wxShell(const wxString& command) bool wxShell(const wxString& command)
{ {
wxString cmd; wxString cmd;
if ( !!command ) if ( !command )
cmd.Printf(wxT("xterm -e %s"), command.c_str()); cmd = _T("xterm");
else else
cmd = command; cmd = command;
@@ -270,92 +270,109 @@ void wxHandleProcessTermination(wxEndProcessData *proc_data)
#endif // wxUSE_GUI #endif // wxUSE_GUI
#if wxUSE_GUI // ----------------------------------------------------------------------------
#define WXUNUSED_UNLESS_GUI(p) p // wxStream classes to support IO redirection in wxExecute
#else // ----------------------------------------------------------------------------
#define WXUNUSED_UNLESS_GUI(p)
#endif
// New wxStream classes to clean up the data when the process terminates class wxProcessFileInputStream : public wxInputStream
{
public:
wxProcessFileInputStream(int fd) { m_fd = fd; }
~wxProcessFileInputStream() { close(m_fd); }
#if wxUSE_GUI virtual bool Eof() const;
class wxProcessFileInputStream: public wxInputStream {
public:
wxProcessFileInputStream(int fd);
~wxProcessFileInputStream();
protected: protected:
size_t OnSysRead(void *buffer, size_t bufsize); size_t OnSysRead(void *buffer, size_t bufsize);
protected: protected:
int m_fd; int m_fd;
}; };
class wxProcessFileOutputStream: public wxOutputStream { class wxProcessFileOutputStream : public wxOutputStream
public: {
wxProcessFileOutputStream(int fd); public:
~wxProcessFileOutputStream(); wxProcessFileOutputStream(int fd) { m_fd = fd; }
~wxProcessFileOutputStream() { close(m_fd); }
protected: protected:
size_t OnSysWrite(const void *buffer, size_t bufsize); size_t OnSysWrite(const void *buffer, size_t bufsize);
protected: protected:
int m_fd; int m_fd;
}; };
wxProcessFileInputStream::wxProcessFileInputStream(int fd) bool wxProcessFileInputStream::Eof() const
{ {
m_fd = fd; if ( m_lasterror == wxSTREAM_EOF )
} return TRUE;
wxProcessFileInputStream::~wxProcessFileInputStream() // check if there is any input available
{ struct timeval tv;
close(m_fd); tv.tv_sec = 0;
tv.tv_usec = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_fd, &readfds);
switch ( select(m_fd + 1, &readfds, NULL, NULL, &tv) )
{
case -1:
wxLogSysError(_("Impossible to get child process input"));
// fall through
case 0:
return TRUE;
default:
wxFAIL_MSG(_T("unexpected select() return value"));
// still fall through
case 1:
// input available: check if there is any
return wxInputStream::Eof();
}
} }
size_t wxProcessFileInputStream::OnSysRead(void *buffer, size_t bufsize) size_t wxProcessFileInputStream::OnSysRead(void *buffer, size_t bufsize)
{ {
int ret; int ret = read(m_fd, buffer, bufsize);
if ( ret == 0 )
ret = read(m_fd, buffer, bufsize); {
m_lasterror = wxSTREAM_NOERROR; m_lasterror = wxSTREAM_EOF;
if (ret == 0)
m_lasterror = wxSTREAM_EOF;
if (ret == -1) {
m_lasterror = wxSTREAM_READ_ERROR;
ret = 0;
} }
else if ( ret == -1 )
{
m_lasterror = wxSTREAM_READ_ERROR;
ret = 0;
}
else
{
m_lasterror = wxSTREAM_NOERROR;
}
return ret; return ret;
} }
wxProcessFileOutputStream::wxProcessFileOutputStream(int fd)
{
m_fd = fd;
}
wxProcessFileOutputStream::~wxProcessFileOutputStream()
{
close(m_fd);
}
size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize) size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
{ {
int ret; int ret = write(m_fd, buffer, bufsize);
if ( ret == -1 )
ret = write(m_fd, buffer, bufsize); {
m_lasterror = wxSTREAM_NOERROR; m_lasterror = wxSTREAM_WRITE_ERROR;
if (ret == -1) { ret = 0;
m_lasterror = wxSTREAM_WRITE_ERROR;
ret = 0;
} }
else
{
m_lasterror = wxSTREAM_NOERROR;
}
return ret; return ret;
} }
#endif
long wxExecute(wxChar **argv, long wxExecute(wxChar **argv,
bool sync, bool sync,
wxProcess * WXUNUSED_UNLESS_GUI(process)) wxProcess *process)
{ {
wxCHECK_MSG( *argv, 0, wxT("can't exec empty command") ); wxCHECK_MSG( *argv, 0, wxT("can't exec empty command") );
@@ -365,9 +382,9 @@ long wxExecute(wxChar **argv,
while (argv[mb_argc]) while (argv[mb_argc])
{ {
wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]); wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]);
mb_argv[mb_argc] = strdup(mb_arg); mb_argv[mb_argc] = strdup(mb_arg);
mb_argc++; mb_argc++;
} }
mb_argv[mb_argc] = (char *) NULL; mb_argv[mb_argc] = (char *) NULL;
@@ -385,9 +402,10 @@ long wxExecute(wxChar **argv,
#if wxUSE_GUI #if wxUSE_GUI
// create pipes // create pipes
int end_proc_detect[2]; int end_proc_detect[2];
if (pipe(end_proc_detect) == -1) if ( pipe(end_proc_detect) == -1 )
{ {
wxLogSysError( _("Pipe creation failed") ); wxLogSysError( _("Pipe creation failed") );
wxLogError( _("Failed to execute '%s'\n"), *argv );
ARGS_CLEANUP; ARGS_CLEANUP;
@@ -395,25 +413,29 @@ long wxExecute(wxChar **argv,
} }
#endif // wxUSE_GUI #endif // wxUSE_GUI
#if wxUSE_GUI int pipeIn[2];
int in_pipe[2] = { -1, -1 }; int pipeOut[2];
int out_pipe[2] = { -1, -1 }; pipeIn[0] = pipeIn[1] =
// Only asynchronous mode is interresting pipeOut[0] = pipeOut[1] = -1;
if (!sync && process && process->NeedPipe())
if ( process && process->IsRedirected() )
{ {
if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) if ( pipe(pipeIn) == -1 || pipe(pipeOut) == -1 )
{ {
/* Free fds */ #if wxUSE_GUI
// free previously allocated resources
close(end_proc_detect[0]); close(end_proc_detect[0]);
close(end_proc_detect[1]); close(end_proc_detect[1]);
wxLogSysError( _("Pipe creation failed (Console pipes)") ); #endif // wxUSE_GUI
wxLogSysError( _("Pipe creation failed") );
wxLogError( _("Failed to execute '%s'\n"), *argv );
ARGS_CLEANUP; ARGS_CLEANUP;
return 0; return 0;
} }
} }
#endif // wxUSE_GUI
// fork the process // fork the process
#ifdef HAVE_VFORK #ifdef HAVE_VFORK
@@ -421,93 +443,108 @@ long wxExecute(wxChar **argv,
#else #else
pid_t pid = fork(); pid_t pid = fork();
#endif #endif
if (pid == -1)
if ( pid == -1 ) // error?
{ {
#if wxUSE_GUI #if wxUSE_GUI
close(end_proc_detect[0]); close(end_proc_detect[0]);
close(end_proc_detect[1]); close(end_proc_detect[1]);
close(in_pipe[0]); close(pipeIn[0]);
close(in_pipe[1]); close(pipeIn[1]);
close(out_pipe[0]); close(pipeOut[0]);
close(out_pipe[1]); close(pipeOut[1]);
#endif #endif // wxUSE_GUI
wxLogSysError( _("Fork failed") ); wxLogSysError( _("Fork failed") );
ARGS_CLEANUP; ARGS_CLEANUP;
return 0; return 0;
} }
else if (pid == 0) else if ( pid == 0 ) // we're in child
{ {
#if wxUSE_GUI #if wxUSE_GUI
// we're in child
close(end_proc_detect[0]); // close reading side close(end_proc_detect[0]); // close reading side
#endif // wxUSE_GUI #endif // wxUSE_GUI
// These three lines close the open file descriptors to to avoid any // These lines close the open file descriptors to to avoid any
// input/output which might block the process or irritate the user. If // input/output which might block the process or irritate the user. If
// one wants proper IO for the subprocess, the right thing to do is // one wants proper IO for the subprocess, the right thing to do is to
// to start an xterm executing it. // start an xterm executing it.
if (sync == 0) if ( !sync )
{ {
// leave stderr opened, it won't do any hurm
for ( int fd = 0; fd < FD_SETSIZE; fd++ ) for ( int fd = 0; fd < FD_SETSIZE; fd++ )
{ {
if ( fd == pipeIn[0] || fd == pipeOut[1]
#if wxUSE_GUI #if wxUSE_GUI
if ( fd == end_proc_detect[1] || fd == in_pipe[0] || fd == out_pipe[1] ) || fd == end_proc_detect[1]
continue;
#endif // wxUSE_GUI #endif // wxUSE_GUI
)
{
// don't close this one, we still need it
continue;
}
// leave stderr opened too, it won't do any hurm
if ( fd != STDERR_FILENO ) if ( fd != STDERR_FILENO )
close(fd); close(fd);
} }
} }
// Fake a console by duplicating pipes // redirect stdio and stdout
#if wxUSE_GUI // (TODO: what about stderr?)
if (in_pipe[0] != -1) { if ( pipeIn[0] != -1 )
dup2(in_pipe[0], STDIN_FILENO); {
dup2(out_pipe[1], STDOUT_FILENO); if ( dup2(pipeIn[0], STDIN_FILENO) == -1 ||
close(in_pipe[0]); dup2(pipeOut[1], STDOUT_FILENO) == -1 )
close(out_pipe[1]); {
wxLogSysError(_("Failed to redirect child process "
"input/output"));
}
close(pipeIn[0]);
close(pipeOut[1]);
} }
#endif // wxUSE_GUI
#if 0
close(STDERR_FILENO);
// some programs complain about stderr not being open, so redirect
// them:
open("/dev/null", O_RDONLY); // stdin
open("/dev/null", O_WRONLY); // stdout
open("/dev/null", O_WRONLY); // stderr
#endif
execvp (*mb_argv, mb_argv); execvp (*mb_argv, mb_argv);
// there is no return after successful exec() // there is no return after successful exec()
wxFprintf(stderr, _("Can't execute '%s'\n"), *argv);
_exit(-1); _exit(-1);
} }
else else // we're in parent
{ {
ARGS_CLEANUP;
// pipe initialization: construction of the wxStreams
if ( process && process->IsRedirected() )
{
// These two streams are relative to this process.
wxOutputStream *outStream = new wxProcessFileOutputStream(pipeIn[1]);
wxInputStream *inStream = new wxProcessFileInputStream(pipeOut[0]);
close(pipeIn[0]); // close reading side
close(pipeOut[1]); // close writing side
process->SetPipeStreams(inStream, outStream);
}
#if wxUSE_GUI #if wxUSE_GUI
wxEndProcessData *data = new wxEndProcessData; wxEndProcessData *data = new wxEndProcessData;
ARGS_CLEANUP;
if ( sync ) if ( sync )
{ {
wxASSERT_MSG( !process, wxT("wxProcess param ignored for sync exec") ); // we may have process for capturing the program output, but it's
// not used in wxEndProcessData in the case of sync execution
data->process = NULL; data->process = NULL;
// sync execution: indicate it by negating the pid // sync execution: indicate it by negating the pid
data->pid = -pid; data->pid = -pid;
data->tag = wxAddProcessCallback(data, end_proc_detect[0]); data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
// we're in parent
close(end_proc_detect[1]); // close writing side close(end_proc_detect[1]); // close writing side
wxBusyCursor bc;
wxWindowDisabler wd;
// it will be set to 0 from GTK_EndProcessDetector // it will be set to 0 from GTK_EndProcessDetector
while (data->pid != 0) while (data->pid != 0)
wxYield(); wxYield();
@@ -518,29 +555,15 @@ long wxExecute(wxChar **argv,
return exitcode; return exitcode;
} }
else else // async execution
{ {
// pipe initialization: construction of the wxStreams
if (process && process->NeedPipe()) {
// These two streams are relative to this process.
wxOutputStream *my_output_stream;
wxInputStream *my_input_stream;
my_output_stream = new wxProcessFileOutputStream(in_pipe[1]);
my_input_stream = new wxProcessFileInputStream(out_pipe[0]);
close(in_pipe[0]); // close reading side
close(out_pipe[1]); // close writing side
process->SetPipeStreams(my_input_stream, my_output_stream);
}
// async execution, nothing special to do - caller will be // async execution, nothing special to do - caller will be
// notified about the process termination if process != NULL, data // notified about the process termination if process != NULL, data
// will be deleted in GTK_EndProcessDetector // will be deleted in GTK_EndProcessDetector
data->process = process; data->process = process;
data->pid = pid; data->pid = pid;
data->tag = wxAddProcessCallback(data, end_proc_detect[0]); data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
// we're in parent
close(end_proc_detect[1]); // close writing side close(end_proc_detect[1]); // close writing side
return pid; return pid;
@@ -557,7 +580,8 @@ long wxExecute(wxChar **argv,
return exitcode; return exitcode;
#endif // wxUSE_GUI #endif // wxUSE_GUI
} }
return 0;
return 0;
#undef ARGS_CLEANUP #undef ARGS_CLEANUP
} }