X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b477f956e0421cb8d8e40635f4c372a0298fd739..8a646b6b9f7593b433a328ce644f5f274685b356:/src/unix/utilsunx.cpp diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 94da23f516..4cb88a61b9 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: utilsunx.cpp +// Name: unix/utilsunx.cpp // Purpose: generic Unix implementation of many wx functions // Author: Vadim Zeitlin // Id: $Id$ @@ -26,7 +26,7 @@ #include "wx/process.h" #include "wx/thread.h" -#include "wx/stream.h" +#include "wx/wfstream.h" #ifdef HAVE_STATFS # ifdef __BSD__ @@ -253,7 +253,11 @@ long wxExecute( const wxString& command, int flags, wxProcess *process ) argv[argc] = NULL; // do execute the command +#if wxUSE_UNICODE + long lRc = -1; +#else long lRc = wxExecute(argv, flags, process); +#endif // clean up argc = 0; @@ -349,210 +353,66 @@ void wxHandleProcessTermination(wxEndProcessData *proc_data) #if wxUSE_STREAMS -class wxProcessFileInputStream : public wxInputStream -{ -public: - wxProcessFileInputStream(int fd) { m_fd = fd; } - ~wxProcessFileInputStream() { close(m_fd); } - - virtual bool Eof() const; - -protected: - size_t OnSysRead(void *buffer, size_t bufsize); - -protected: - int m_fd; -}; +// ---------------------------------------------------------------------------- +// wxPipeInputStream: stream for reading from a pipe +// ---------------------------------------------------------------------------- -class wxProcessFileOutputStream : public wxOutputStream +class wxPipeInputStream : public wxFileInputStream { public: - wxProcessFileOutputStream(int fd) { m_fd = fd; } - ~wxProcessFileOutputStream() { close(m_fd); } + wxPipeInputStream(int fd) : wxFileInputStream(fd) { } -protected: - size_t OnSysWrite(const void *buffer, size_t bufsize); + // return TRUE if the pipe is still opened + bool IsOpened() const { return !Eof(); } -protected: - int m_fd; + // return TRUE if we have anything to read, don't block + bool IsAvailable() const; }; -bool wxProcessFileInputStream::Eof() const +bool wxPipeInputStream::IsAvailable() const { if ( m_lasterror == wxSTREAM_EOF ) - return TRUE; + return FALSE; // 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(m_fd, &readfds); - switch ( select(m_fd + 1, &readfds, NULL, NULL, &tv) ) + 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; + return FALSE; 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) -{ - int ret = read(m_fd, buffer, bufsize); - if ( ret == 0 ) - { - m_lasterror = wxSTREAM_EOF; - } - else if ( ret == -1 ) - { - m_lasterror = wxSTREAM_READ_ERROR; - ret = 0; - } - else - { - m_lasterror = wxSTREAM_NOERROR; + // input available -- or maybe not, as select() returns 1 when a + // read() will complete without delay, but it could still not read + // anything + return !Eof(); } - - return ret; } -size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize) -{ - int ret = write(m_fd, buffer, bufsize); - if ( ret == -1 ) - { - m_lasterror = wxSTREAM_WRITE_ERROR; - ret = 0; - } - else - { - m_lasterror = wxSTREAM_NOERROR; - } - - return ret; -} - -// ---------------------------------------------------------------------------- -// wxStreamTempBuffer -// ---------------------------------------------------------------------------- - -/* - 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 (wxStreamTempBuffer 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 wxStreamTempBuffer -{ -public: - wxStreamTempBuffer(); - - // call to associate a stream with this buffer, otherwise nothing happens - // at all - void Init(wxInputStream *stream); - - // check for input on our stream and cache it in our buffer if any - void Update(); - - ~wxStreamTempBuffer(); - -private: - // the stream we're buffering, if NULL we don't do anything at all - wxInputStream *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; -}; - -wxStreamTempBuffer::wxStreamTempBuffer() -{ - m_stream = NULL; - m_buffer = NULL; - m_size = 0; -} - -void wxStreamTempBuffer::Init(wxInputStream *stream) -{ - m_stream = stream; -} - -void wxStreamTempBuffer::Update() -{ - if ( m_stream && !m_stream->Eof() ) - { - // 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(); - } - } -} - -wxStreamTempBuffer::~wxStreamTempBuffer() -{ - if ( m_buffer ) - { - m_stream->Ungetch(m_buffer, m_size); - free(m_buffer); - } -} +// define this to let wxexec.cpp know that we know what we're doing +#define _WX_USED_BY_WXEXECUTE_ +#include "../common/execcmn.cpp" #endif // wxUSE_STREAMS // ---------------------------------------------------------------------------- -// wxPipe: this encpasulates pipe() system call +// wxPipe: this encapsulates pipe() system call // ---------------------------------------------------------------------------- class wxPipe @@ -632,6 +492,10 @@ private: // wxExecute: the real worker function // ---------------------------------------------------------------------------- +#ifdef __VMS + #pragma message disable codeunreachable +#endif + long wxExecute(wxChar **argv, int flags, wxProcess *process) @@ -699,13 +563,17 @@ long wxExecute(wxChar **argv, } // fork the process -#ifdef HAVE_VFORK - pid_t pid = vfork(); + // + // NB: do *not* use vfork() here, it completely breaks this code for some + // reason under Solaris (and maybe others, although not under Linux) + // But on OpenVMS we do not have fork so we have to use vfork and + // cross our fingers that it works. +#ifdef __VMS + pid_t pid = vfork(); #else - pid_t pid = fork(); + pid_t pid = fork(); #endif - - if ( pid == -1 ) // error? + if ( pid == -1 ) // error? { wxLogSysError( _("Fork failed") ); @@ -715,12 +583,6 @@ long wxExecute(wxChar **argv, } else if ( pid == 0 ) // we're in child { -#if wxUSE_GUI - // reading side can be safely closed but we should keep the write one - // opened - pipeEndProcDetect.Detach(wxPipe::Write); -#endif // wxUSE_GUI - // These lines close the open file descriptors to to avoid any // 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 to @@ -745,18 +607,25 @@ long wxExecute(wxChar **argv, if ( fd != STDERR_FILENO ) close(fd); } + } -#ifndef __VMS - if ( flags & wxEXEC_MAKE_GROUP_LEADER ) - { - // Set process group to child process' pid. Then killing -pid - // of the parent will kill the process and all of its children. - setsid(); - } -#endif // !__VMS +#if !defined(__VMS) && !defined(__EMX__) + if ( flags & wxEXEC_MAKE_GROUP_LEADER ) + { + // Set process group to child process' pid. Then killing -pid + // of the parent will kill the process and all of its children. + setsid(); } +#endif // !__VMS - // redirect stdio, stdout and stderr +#if wxUSE_GUI + // reading side can be safely closed but we should keep the write one + // opened + pipeEndProcDetect.Detach(wxPipe::Write); + pipeEndProcDetect.Close(); +#endif // wxUSE_GUI + + // redirect stdin, stdout and stderr if ( pipeIn.IsOk() ) { if ( dup2(pipeIn[wxPipe::Read], STDIN_FILENO) == -1 || @@ -790,27 +659,30 @@ long wxExecute(wxChar **argv, { ARGS_CLEANUP; - // pipe initialization: construction of the wxStreams + // prepare for IO redirection + #if wxUSE_STREAMS - wxStreamTempBuffer bufIn, bufErr; + // the input buffer bufOut is connected to stdout, this is why it is + // called bufOut and not bufIn + wxStreamTempInputBuffer bufOut, + bufErr; #endif // wxUSE_STREAMS if ( process && process->IsRedirected() ) { #if wxUSE_STREAMS - // in/out for subprocess correspond to our out/in - wxOutputStream *outStream = - new wxProcessFileOutputStream(pipeIn.Detach(wxPipe::Write)); + wxOutputStream *inStream = + new wxFileOutputStream(pipeIn.Detach(wxPipe::Write)); - wxInputStream *inStream = - new wxProcessFileInputStream(pipeOut.Detach(wxPipe::Read)); + wxPipeInputStream *outStream = + new wxPipeInputStream(pipeOut.Detach(wxPipe::Read)); - wxInputStream *errStream = - new wxProcessFileInputStream(pipeErr.Detach(wxPipe::Read)); + wxPipeInputStream *errStream = + new wxPipeInputStream(pipeErr.Detach(wxPipe::Read)); - process->SetPipeStreams(inStream, outStream, errStream); + process->SetPipeStreams(outStream, inStream, errStream); - bufIn.Init(inStream); + bufOut.Init(outStream); bufErr.Init(errStream); #endif // wxUSE_STREAMS } @@ -850,7 +722,7 @@ long wxExecute(wxChar **argv, while ( data->pid != 0 ) { #if wxUSE_STREAMS - bufIn.Update(); + bufOut.Update(); bufErr.Update(); #endif // wxUSE_STREAMS @@ -889,8 +761,14 @@ long wxExecute(wxChar **argv, return exitcode; #endif // wxUSE_GUI } + + return ERROR_RETURN_CODE; } +#ifdef __VMS + #pragma message enable codeunreachable +#endif + #undef ERROR_RETURN_CODE #undef ARGS_CLEANUP @@ -901,13 +779,13 @@ long wxExecute(wxChar **argv, const wxChar* wxGetHomeDir( wxString *home ) { *home = wxGetUserHome( wxString() ); - wxString tmp; + wxString tmp; if ( home->IsEmpty() ) *home = wxT("/"); #ifdef __VMS - tmp = *home; - if ( tmp.Last() != wxT(']')) - if ( tmp.Last() != wxT('/')) *home << wxT('/'); + tmp = *home; + if ( tmp.Last() != wxT(']')) + if ( tmp.Last() != wxT('/')) *home << wxT('/'); #endif return home->c_str(); } @@ -1142,9 +1020,9 @@ bool wxGetDiskSpace(const wxString& path, wxLongLong *pTotal, wxLongLong *pFree) #if defined(HAVE_STATFS) || defined(HAVE_STATVFS) // the case to "char *" is needed for AIX 4.3 wxStatFs fs; - if ( statfs((char *)path.fn_str(), &fs) != 0 ) + if ( statfs((char *)(const char*)path.fn_str(), &fs) != 0 ) { - wxLogSysError("Failed to get file system statistics"); + wxLogSysError( wxT("Failed to get file system statistics") ); return FALSE; }