]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/utilsunx.cpp
wxMac removed debugging code
[wxWidgets.git] / src / unix / utilsunx.cpp
index 94ecc08e2da59b3782419b3ac50d899277c0d64b..70d467e744b424e795899c4ed0339f1b8f90a555 100644 (file)
@@ -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__
 #  endif
 #endif // HAVE_STATFS
 
+// not only the statfs syscall is called differently depending on platform, but
+// we also can't use "struct statvfs" under Solaris because it breaks down if
+// HAVE_LARGEFILE_SUPPORT == 1 and we must use statvfs_t instead
 #ifdef HAVE_STATVFS
     #include <sys/statvfs.h>
 
     #define statfs statvfs
-#endif // HAVE_STATVFS
+    #define wxStatFs statvfs_t
+#elif HAVE_STATFS
+    #define wxStatFs struct statfs
+#endif // HAVE_STAT[V]FS
 
 #if wxUSE_GUI
     #include "wx/unix/execute.h"
@@ -247,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;
@@ -290,6 +300,29 @@ bool wxShell(const wxString& command, wxArrayString& output)
     return wxExecute(wxMakeShellCommand(command), output);
 }
 
+// Shutdown or reboot the PC
+bool wxShutdown(wxShutdownFlags wFlags)
+{
+    wxChar level;
+    switch ( wFlags )
+    {
+        case wxSHUTDOWN_POWEROFF:
+            level = _T('0');
+            break;
+
+        case wxSHUTDOWN_REBOOT:
+            level = _T('6');
+            break;
+
+        default:
+            wxFAIL_MSG( _T("unknown wxShutdown() flag") );
+            return FALSE;
+    }
+
+    return system(wxString::Format(_T("init %c"), level).mb_str()) == 0;
+}
+
+
 #if wxUSE_GUI
 
 void wxHandleProcessTermination(wxEndProcessData *proc_data)
@@ -320,35 +353,20 @@ 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;
-};
+// ----------------------------------------------------------------------------
+// wxProcessFileInputStream: stream for reading from a pipe
+// ----------------------------------------------------------------------------
 
-class wxProcessFileOutputStream : public wxOutputStream
+class wxProcessFileInputStream : public wxFileInputStream
 {
 public:
-    wxProcessFileOutputStream(int fd) { m_fd = fd; }
-    ~wxProcessFileOutputStream() { close(m_fd); }
-
-protected:
-    size_t OnSysWrite(const void *buffer, size_t bufsize);
+    wxProcessFileInputStream(int fd) : wxFileInputStream(fd) { }
 
-protected:
-    int m_fd;
+    // return TRUE if we have anything to read, don't block
+    bool IsAvailable() const;
 };
 
-bool wxProcessFileInputStream::Eof() const
+bool wxProcessFileInputStream::IsAvailable() const
 {
     if ( m_lasterror == wxSTREAM_EOF )
         return TRUE;
@@ -358,10 +376,12 @@ bool wxProcessFileInputStream::Eof() const
     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"));
@@ -375,49 +395,13 @@ bool wxProcessFileInputStream::Eof() const
             // 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;
-    }
-
-    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;
+            // input available
+            return TRUE;
     }
-
-    return ret;
 }
 
 // ----------------------------------------------------------------------------
-// wxStreamTempBuffer
+// wxStreamTempInputBuffer
 // ----------------------------------------------------------------------------
 
 /*
@@ -436,34 +420,38 @@ size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
     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 4Mb) is full. Then the writing process stops and waits until we
+    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
+   buffer (wxStreamTempInputBuffer object) and later just stuff it back into the
    stream when the process terminates. See supporting code in wxExecute()
-   itself as well.   
+   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
+class wxStreamTempInputBuffer
 {
 public:
-    wxStreamTempBuffer();
+    wxStreamTempInputBuffer();
 
     // call to associate a stream with this buffer, otherwise nothing happens
     // at all
-    void Init(wxInputStream *stream);
+    void Init(wxProcessFileInputStream *stream);
 
     // check for input on our stream and cache it in our buffer if any
     void Update();
 
-    ~wxStreamTempBuffer();
+    ~wxStreamTempInputBuffer();
 
 private:
     // the stream we're buffering, if NULL we don't do anything at all
-    wxInputStream *m_stream;
+    wxProcessFileInputStream *m_stream;
 
     // the buffer of size m_size (NULL if m_size == 0)
     void *m_buffer;
@@ -472,25 +460,25 @@ private:
     size_t m_size;
 };
 
-wxStreamTempBuffer::wxStreamTempBuffer()
+wxStreamTempInputBuffer::wxStreamTempInputBuffer()
 {
-    m_stream = NULL; 
+    m_stream = NULL;
     m_buffer = NULL;
     m_size = 0;
 }
 
-void wxStreamTempBuffer::Init(wxInputStream *stream)
+void wxStreamTempInputBuffer::Init(wxProcessFileInputStream *stream)
 {
     m_stream = stream;
 }
 
-void wxStreamTempBuffer::Update()
+void wxStreamTempInputBuffer::Update()
 {
-    if ( m_stream && !m_stream->Eof() )
+    if ( m_stream && m_stream->IsAvailable() )
     {
-        // realloc in blocks of 1Kb - surely not the best strategy but which
-        // one is?
-        static const size_t incSize = 1024;
+        // 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 )
@@ -502,12 +490,12 @@ void wxStreamTempBuffer::Update()
         {
             m_buffer = buf;
             m_stream->Read((char *)m_buffer + m_size, incSize);
-            m_size += incSize;
+            m_size += m_stream->LastRead();
         }
     }
 }
 
-wxStreamTempBuffer::~wxStreamTempBuffer()
+wxStreamTempInputBuffer::~wxStreamTempInputBuffer()
 {
     if ( m_buffer )
     {
@@ -518,6 +506,90 @@ wxStreamTempBuffer::~wxStreamTempBuffer()
 
 #endif // wxUSE_STREAMS
 
+// ----------------------------------------------------------------------------
+// wxPipe: this encapsulates pipe() system call
+// ----------------------------------------------------------------------------
+
+class wxPipe
+{
+public:
+    // the symbolic names for the pipe ends
+    enum Direction
+    {
+        Read,
+        Write
+    };
+
+    enum
+    {
+        INVALID_FD = -1
+    };
+
+    // default ctor doesn't do anything
+    wxPipe() { m_fds[Read] = m_fds[Write] = INVALID_FD; }
+
+    // create the pipe, return TRUE if ok, FALSE on error
+    bool Create()
+    {
+        if ( pipe(m_fds) == -1 )
+        {
+            wxLogSysError(_("Pipe creation failed"));
+
+            return FALSE;
+        }
+
+        return TRUE;
+    }
+
+    // return TRUE if we were created successfully
+    bool IsOk() const { return m_fds[Read] != INVALID_FD; }
+
+    // return the descriptor for one of the pipe ends
+    int operator[](Direction which) const
+    {
+        wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_fds),
+                      _T("invalid pipe index") );
+
+        return m_fds[which];
+    }
+
+    // detach a descriptor, meaning that the pipe dtor won't close it, and
+    // return it
+    int Detach(Direction which)
+    {
+        wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_fds),
+                      _T("invalid pipe index") );
+
+        int fd = m_fds[which];
+        m_fds[which] = INVALID_FD;
+
+        return fd;
+    }
+
+    // close the pipe descriptors
+    void Close()
+    {
+        for ( size_t n = 0; n < WXSIZEOF(m_fds); n++ )
+        {
+            if ( m_fds[n] != INVALID_FD )
+                close(m_fds[n]);
+        }
+    }
+
+    // dtor closes the pipe descriptors
+    ~wxPipe() { Close(); }
+
+private:
+    int m_fds[2];
+};
+
+// ----------------------------------------------------------------------------
+// wxExecute: the real worker function
+// ----------------------------------------------------------------------------
+#ifdef __VMS
+#pragma message disable codeunreachable
+#endif
+
 long wxExecute(wxChar **argv,
                int flags,
                wxProcess *process)
@@ -556,10 +628,9 @@ long wxExecute(wxChar **argv,
 
 #if wxUSE_GUI
     // create pipes
-    int end_proc_detect[2];
-    if ( pipe(end_proc_detect) == -1 )
+    wxPipe pipeEndProcDetect;
+    if ( !pipeEndProcDetect.Create() )
     {
-        wxLogSysError( _("Pipe creation failed") );
         wxLogError( _("Failed to execute '%s'\n"), *argv );
 
         ARGS_CLEANUP;
@@ -569,25 +640,14 @@ long wxExecute(wxChar **argv,
 #endif // wxUSE_GUI
 
     // pipes for inter process communication
-    int pipeIn[2],      // stdin
-        pipeOut[2],     // stdout
-        pipeErr[2];     // stderr
-
-    pipeIn[0] = pipeIn[1] =
-    pipeOut[0] = pipeOut[1] =
-    pipeErr[0] = pipeErr[1] = -1;
+    wxPipe pipeIn,      // stdin
+           pipeOut,     // stdout
+           pipeErr;     // stderr
 
     if ( process && process->IsRedirected() )
     {
-        if ( pipe(pipeIn) == -1 || pipe(pipeOut) == -1 || pipe(pipeErr) == -1 )
+        if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
         {
-#if wxUSE_GUI
-            // free previously allocated resources
-            close(end_proc_detect[0]);
-            close(end_proc_detect[1]);
-#endif // wxUSE_GUI
-
-            wxLogSysError( _("Pipe creation failed") );
             wxLogError( _("Failed to execute '%s'\n"), *argv );
 
             ARGS_CLEANUP;
@@ -605,17 +665,6 @@ long wxExecute(wxChar **argv,
 
     if ( pid == -1 )     // error?
     {
-#if wxUSE_GUI
-        close(end_proc_detect[0]);
-        close(end_proc_detect[1]);
-        close(pipeIn[0]);
-        close(pipeIn[1]);
-        close(pipeOut[0]);
-        close(pipeOut[1]);
-        close(pipeErr[0]);
-        close(pipeErr[1]);
-#endif // wxUSE_GUI
-
         wxLogSysError( _("Fork failed") );
 
         ARGS_CLEANUP;
@@ -624,10 +673,6 @@ long wxExecute(wxChar **argv,
     }
     else if ( pid == 0 )  // we're in child
     {
-#if wxUSE_GUI
-        close(end_proc_detect[0]); // close reading side
-#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
@@ -636,9 +681,11 @@ long wxExecute(wxChar **argv,
         {
             for ( int fd = 0; fd < FD_SETSIZE; fd++ )
             {
-                if ( fd == pipeIn[0] || fd == pipeOut[1] || fd == pipeErr[1]
+                if ( fd == pipeIn[wxPipe::Read]
+                        || fd == pipeOut[wxPipe::Write]
+                        || fd == pipeErr[wxPipe::Write]
 #if wxUSE_GUI
-                     || fd == end_proc_detect[1]
+                        || fd == pipeEndProcDetect[wxPipe::Write]
 #endif // wxUSE_GUI
                    )
                 {
@@ -646,32 +693,41 @@ long wxExecute(wxChar **argv,
                     continue;
                 }
 
-                // leave stderr opened too, it won't do any hurm
+                // leave stderr opened too, it won't do any harm
                 if ( fd != STDERR_FILENO )
                     close(fd);
             }
+        }
 
-            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();
-            }
+#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 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 stdio, stdout and stderr
-        if ( pipeIn[0] != -1 )
+        // redirect stdin, stdout and stderr
+        if ( pipeIn.IsOk() )
         {
-            if ( dup2(pipeIn[0], STDIN_FILENO) == -1 ||
-                 dup2(pipeOut[1], STDOUT_FILENO) == -1 ||
-                 dup2(pipeErr[1], STDERR_FILENO) == -1 )
+            if ( dup2(pipeIn[wxPipe::Read], STDIN_FILENO) == -1 ||
+                 dup2(pipeOut[wxPipe::Write], STDOUT_FILENO) == -1 ||
+                 dup2(pipeErr[wxPipe::Write], STDERR_FILENO) == -1 )
             {
                 wxLogSysError(_("Failed to redirect child process input/output"));
             }
 
-            close(pipeIn[0]);
-            close(pipeOut[1]);
-            close(pipeErr[1]);
+            pipeIn.Close();
+            pipeOut.Close();
+            pipeErr.Close();
         }
 
         execvp (*mb_argv, mb_argv);
@@ -682,6 +738,9 @@ long wxExecute(wxChar **argv,
         // some compilers complain about missing return - of course, they
         // should know that exit() doesn't return but what else can we do if
         // they don't?
+        //
+        // and, sure enough, other compilers complain about unreachable code
+        // after exit() call, so we can just always have return here...
 #if defined(__VMS) || defined(__INTEL_COMPILER)
         return 0;
 #endif
@@ -690,33 +749,52 @@ 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[1]);
-            wxInputStream *inStream = new wxProcessFileInputStream(pipeOut[0]);
-            wxInputStream *errStream = new wxProcessFileInputStream(pipeErr[0]);
+            wxOutputStream *inStream =
+                new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
+
+            wxProcessFileInputStream *outStream =
+                new wxProcessFileInputStream(pipeOut.Detach(wxPipe::Read));
 
-            process->SetPipeStreams(inStream, outStream, errStream);
+            wxProcessFileInputStream *errStream =
+                new wxProcessFileInputStream(pipeErr.Detach(wxPipe::Read));
 
-            bufIn.Init(inStream);
-            bufErr.Init(inStream);
+            process->SetPipeStreams(outStream, inStream, errStream);
+
+            bufOut.Init(outStream);
+            bufErr.Init(errStream);
 #endif // wxUSE_STREAMS
+        }
 
-            close(pipeIn[0]); // close reading side
-            close(pipeOut[1]); // close writing side
-            close(pipeErr[1]); // close writing side
+        if ( pipeIn.IsOk() )
+        {
+            pipeIn.Close();
+            pipeOut.Close();
+            pipeErr.Close();
         }
 
 #if wxUSE_GUI && !defined(__WXMICROWIN__)
         wxEndProcessData *data = new wxEndProcessData;
 
+        data->tag = wxAddProcessCallback
+                    (
+                        data,
+                        pipeEndProcDetect.Detach(wxPipe::Read)
+                    );
+
+        pipeEndProcDetect.Close();
+
         if ( flags & wxEXEC_SYNC )
         {
             // we may have process for capturing the program output, but it's
@@ -725,9 +803,6 @@ long wxExecute(wxChar **argv,
 
             // sync execution: indicate it by negating the pid
             data->pid = -pid;
-            data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
-
-            close(end_proc_detect[1]); // close writing side
 
             wxBusyCursor bc;
             wxWindowDisabler wd;
@@ -737,7 +812,7 @@ long wxExecute(wxChar **argv,
             while ( data->pid != 0 )
             {
 #if wxUSE_STREAMS
-                bufIn.Update();
+                bufOut.Update();
                 bufErr.Update();
 #endif // wxUSE_STREAMS
 
@@ -759,9 +834,6 @@ long wxExecute(wxChar **argv,
             // will be deleted in GTK_EndProcessDetector
             data->process  = process;
             data->pid      = pid;
-            data->tag      = wxAddProcessCallback(data, end_proc_detect[0]);
-
-            close(end_proc_detect[1]); // close writing side
 
             return pid;
         }
@@ -779,7 +851,12 @@ 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
@@ -993,6 +1070,11 @@ int wxGetOsVersion(int *majorVsn, int *minorVsn)
 
 #endif // !wxUSE_GUI
 
+unsigned long wxGetProcessId()
+{
+    return (unsigned long)getpid();
+}
+
 long wxGetFreeMemory()
 {
 #if defined(__LINUX__)
@@ -1026,31 +1108,36 @@ 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
-    struct statfs fs;
-    if ( statfs((char *)path.fn_str(), &fs) != 0 )
+    wxStatFs fs;
+    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;
     }
 
-    // under Solaris we might have to use fs.f_frsize instead as I think it
-    // may be a multiple of the block size in general (TODO)
+    // under Solaris we also have to use f_frsize field instead of f_bsize
+    // which is in general a multiple of f_frsize
+#ifdef HAVE_STATVFS
+    wxLongLong blockSize = fs.f_frsize;
+#else // HAVE_STATFS
+    wxLongLong blockSize = fs.f_bsize;
+#endif // HAVE_STATVFS/HAVE_STATFS
 
     if ( pTotal )
     {
-        *pTotal = wxLongLong(fs.f_blocks) * fs.f_bsize;
+        *pTotal = wxLongLong(fs.f_blocks) * blockSize;
     }
 
     if ( pFree )
     {
-        *pFree = wxLongLong(fs.f_bavail) * fs.f_bsize;
+        *pFree = wxLongLong(fs.f_bavail) * blockSize;
     }
 
     return TRUE;
-#endif // HAVE_STATFS
-
+#else // !HAVE_STATFS && !HAVE_STATVFS
     return FALSE;
+#endif // HAVE_STATFS
 }
 
 // ----------------------------------------------------------------------------