+ // let wxExecute() know that the process has terminated
+ proc_data->pid = 0;
+ }
+}
+
+#endif // wxUSE_GUI
+
+// ----------------------------------------------------------------------------
+// wxStream classes to support IO redirection in wxExecute
+// ----------------------------------------------------------------------------
+
+#if wxUSE_STREAMS
+
+// ----------------------------------------------------------------------------
+// wxProcessFileInputStream: stream for reading from a pipe
+// ----------------------------------------------------------------------------
+
+class wxProcessFileInputStream : public wxFileInputStream
+{
+public:
+ wxProcessFileInputStream(int fd) : wxFileInputStream(fd) { }
+
+ // return TRUE if we have anything to read, don't block
+ bool IsAvailable() const;
+};
+
+bool wxProcessFileInputStream::IsAvailable() const
+{
+ if ( m_lasterror == wxSTREAM_EOF )
+ return TRUE;
+
+ // check if there is any input available
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ const int fd = m_file->fd();
+
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ switch ( select(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
+ return TRUE;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// wxStreamTempInputBuffer
+// ----------------------------------------------------------------------------
+
+/*
+ Extract of a mail to wx-users to give the context of the problem we are
+ trying to solve here:
+
+ MC> If I run the command:
+ MC> find . -name "*.h" -exec grep linux {} \;
+ MC> in the exec sample synchronously from the 'Capture command output'
+ MC> menu, wxExecute never returns. I have to xkill it. Has anyone
+ MC> else encountered this?
+
+ Yes, I can reproduce it too.
+
+ I even think I understand why it happens: before launching the external
+ command we set up a pipe with a valid file descriptor on the reading side
+ when the output is redirected. So the subprocess happily writes to it ...
+ until the pipe buffer (which is usually quite big on Unix, I think the
+ default is 4Kb) is full. Then the writing process stops and waits until we
+ read some data from the pipe to be able to continue writing to it but we
+ never do it because we wait until it terminates to start reading and so we
+ have a classical deadlock.
+
+ Here is the fix: we now read the output as soon as it appears into a temp
+ buffer (wxStreamTempInputBuffer object) and later just stuff it back into the
+ stream when the process terminates. See supporting code in wxExecute()
+ itself as well.
+
+ Note that this is horribly inefficient for large amounts of output (count
+ the number of times we copy the data around) and so a better API is badly
+ needed!
+*/
+
+class wxStreamTempInputBuffer
+{
+public:
+ wxStreamTempInputBuffer();
+
+ // call to associate a stream with this buffer, otherwise nothing happens
+ // at all
+ void Init(wxProcessFileInputStream *stream);
+
+ // check for input on our stream and cache it in our buffer if any
+ void Update();
+
+ ~wxStreamTempInputBuffer();
+
+private:
+ // the stream we're buffering, if NULL we don't do anything at all
+ wxProcessFileInputStream *m_stream;
+
+ // the buffer of size m_size (NULL if m_size == 0)
+ void *m_buffer;
+
+ // the size of the buffer
+ size_t m_size;
+};
+
+wxStreamTempInputBuffer::wxStreamTempInputBuffer()
+{
+ m_stream = NULL;
+ m_buffer = NULL;
+ m_size = 0;
+}
+
+void wxStreamTempInputBuffer::Init(wxProcessFileInputStream *stream)
+{
+ m_stream = stream;
+}
+
+void wxStreamTempInputBuffer::Update()
+{
+ if ( m_stream && m_stream->IsAvailable() )
+ {
+ // realloc in blocks of 4Kb: this is the default (and minimal) buffer
+ // size of the Unix pipes so it should be the optimal step
+ static const size_t incSize = 4096;
+
+ void *buf = realloc(m_buffer, m_size + incSize);
+ if ( !buf )
+ {
+ // don't read any more, we don't have enough memory to do it
+ m_stream = NULL;
+ }
+ else // got memory for the buffer
+ {
+ m_buffer = buf;
+ m_stream->Read((char *)m_buffer + m_size, incSize);
+ m_size += m_stream->LastRead();
+ }
+ }
+}