]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/utilsunx.cpp
VC6 compilation fix: using access declaration can only be used with immediate base...
[wxWidgets.git] / src / unix / utilsunx.cpp
index 3616d4ae5acc9edfc375db30581cac19c5a83b0c..7e8b8f23d53a2c21b29ba1e6ff4df77b7a5c2ca6 100644 (file)
@@ -263,106 +263,74 @@ int wxKill(long pid, wxSignal sig, wxKillError *rc, int flags)
     return err;
 }
 
-#define WXEXECUTE_NARGS   127
-
-#if defined(__DARWIN__)
-long wxMacExecute(wxChar **argv,
-               int flags,
-               wxProcess *process);
-#endif
-
-long wxExecute( const wxString& command, int flags, wxProcess *process )
+// Shutdown or reboot the PC
+bool wxShutdown(wxShutdownFlags wFlags)
 {
-    wxCHECK_MSG( !command.empty(), 0, wxT("can't exec empty command") );
-
-    wxLogTrace(wxT("exec"), wxT("Executing \"%s\""), command.c_str());
-
-#if wxUSE_THREADS
-    // fork() doesn't mix well with POSIX threads: on many systems the program
-    // deadlocks or crashes for some reason. Probably our code is buggy and
-    // doesn't do something which must be done to allow this to work, but I
-    // don't know what yet, so for now just warn the user (this is the least we
-    // can do) about it
-    wxASSERT_MSG( wxThread::IsMain(),
-                    _T("wxExecute() can be called only from the main thread") );
-#endif // wxUSE_THREADS
+    wxChar level;
+    switch ( wFlags )
+    {
+        case wxSHUTDOWN_POWEROFF:
+            level = _T('0');
+            break;
 
-    int argc = 0;
-    wxChar *argv[WXEXECUTE_NARGS];
-    wxString argument;
-    const wxChar *cptr = command.c_str();
-    wxChar quotechar = wxT('\0'); // is arg quoted?
-    bool escaped = false;
+        case wxSHUTDOWN_REBOOT:
+            level = _T('6');
+            break;
 
-    // split the command line in arguments
-    do
-    {
-        argument = wxEmptyString;
-        quotechar = wxT('\0');
+        default:
+            wxFAIL_MSG( _T("unknown wxShutdown() flag") );
+            return false;
+    }
 
-        // eat leading whitespace:
-        while ( wxIsspace(*cptr) )
-            cptr++;
+    return system(wxString::Format(_T("init %c"), level).mb_str()) == 0;
+}
 
-        if ( *cptr == wxT('\'') || *cptr == wxT('"') )
-            quotechar = *cptr++;
+// ----------------------------------------------------------------------------
+// wxStream classes to support IO redirection in wxExecute
+// ----------------------------------------------------------------------------
 
-        do
-        {
-            if ( *cptr == wxT('\\') && ! escaped )
-            {
-                escaped = true;
-                cptr++;
-                continue;
-            }
+#if HAS_PIPE_INPUT_STREAM
 
-            // all other characters:
-            argument += *cptr++;
-            escaped = false;
+bool wxPipeInputStream::CanRead() const
+{
+    if ( m_lasterror == wxSTREAM_EOF )
+        return false;
 
-            // have we reached the end of the argument?
-            if ( (*cptr == quotechar && ! escaped)
-                 || (quotechar == wxT('\0') && wxIsspace(*cptr))
-                 || *cptr == wxT('\0') )
-            {
-                wxASSERT_MSG( argc < WXEXECUTE_NARGS,
-                              wxT("too many arguments in wxExecute") );
+    // check if there is any input available
+    struct timeval tv;
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
 
-                argv[argc] = new wxChar[argument.length() + 1];
-                wxStrcpy(argv[argc], argument.c_str());
-                argc++;
+    const int fd = m_file->fd();
 
-                // if not at end of buffer, swallow last character:
-                if(*cptr)
-                    cptr++;
+    fd_set readfds;
 
-                break; // done with this one, start over
-            }
-        } while(*cptr);
-    } while(*cptr);
-    argv[argc] = NULL;
+    wxFD_ZERO(&readfds);
+    wxFD_SET(fd, &readfds);
 
-    long lRc;
-#if defined(__DARWIN__)
-    // wxMacExecute only executes app bundles.
-    // It returns an error code if the target is not an app bundle, thus falling
-    // through to the regular wxExecute for non app bundles.
-    lRc = wxMacExecute(argv, flags, process);
-    if( lRc != ((flags & wxEXEC_SYNC) ? -1 : 0))
-        return lRc;
-#endif
+    switch ( select(fd + 1, &readfds, NULL, NULL, &tv) )
+    {
+        case -1:
+            wxLogSysError(_("Impossible to get child process input"));
+            // fall through
 
-    // do execute the command
-    lRc = wxExecute(argv, flags, process);
+        case 0:
+            return false;
 
-    // clean up
-    argc = 0;
-    while( argv[argc] )
-        delete [] argv[argc++];
+        default:
+            wxFAIL_MSG(_T("unexpected select() return value"));
+            // still fall through
 
-    return lRc;
+        case 1:
+            // 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();
+    }
 }
 
+#endif // HAS_PIPE_INPUT_STREAM
+
 // ----------------------------------------------------------------------------
 // wxShell
 // ----------------------------------------------------------------------------
@@ -396,79 +364,148 @@ bool wxShell(const wxString& command, wxArrayString& output)
     return wxExecute(wxMakeShellCommand(command), output);
 }
 
-// Shutdown or reboot the PC
-bool wxShutdown(wxShutdownFlags wFlags)
+namespace
 {
-    wxChar level;
-    switch ( wFlags )
+
+// helper class for storing arguments as char** array suitable for passing to
+// execvp(), whatever form they were passed to us
+class ArgsArray
+{
+public:
+    ArgsArray(const wxArrayString& args)
     {
-        case wxSHUTDOWN_POWEROFF:
-            level = _T('0');
-            break;
+        Init(args.size());
 
-        case wxSHUTDOWN_REBOOT:
-            level = _T('6');
-            break;
+        for ( int i = 0; i < m_argc; i++ )
+        {
+            m_argv[i] = wxStrdup(args[i]);
+        }
+    }
 
-        default:
-            wxFAIL_MSG( _T("unknown wxShutdown() flag") );
-            return false;
+#if wxUSE_UNICODE
+    ArgsArray(wchar_t **wargv)
+    {
+        int argc = 0;
+        while ( *wargv++ )
+            argc++;
+
+        Init(argc);
+
+        for ( int i = 0; i < m_argc; i++ )
+        {
+            m_argv[i] = wxSafeConvertWX2MB(wargv[i]).release();
+        }
     }
+#endif // wxUSE_UNICODE
 
-    return system(wxString::Format(_T("init %c"), level).mb_str()) == 0;
-}
+    ~ArgsArray()
+    {
+        for ( int i = 0; i < m_argc; i++ )
+        {
+            free(m_argv[i]);
+        }
+
+        delete [] m_argv;
+    }
+
+    operator char**() const { return m_argv; }
+
+private:
+    void Init(int argc)
+    {
+        m_argc = argc;
+        m_argv = new char *[m_argc + 1];
+        m_argv[m_argc] = NULL;
+    }
+
+    int m_argc;
+    char **m_argv;
+
+    DECLARE_NO_COPY_CLASS(ArgsArray)
+};
+
+} // anonymous namespace
 
 // ----------------------------------------------------------------------------
-// wxStream classes to support IO redirection in wxExecute
+// wxExecute implementations
 // ----------------------------------------------------------------------------
 
-#if HAS_PIPE_INPUT_STREAM
+#if defined(__DARWIN__)
+bool wxMacLaunch(char **argv);
+#endif
 
-bool wxPipeInputStream::CanRead() const
+long wxExecute(const wxString& command, int flags, wxProcess *process)
 {
-    if ( m_lasterror == wxSTREAM_EOF )
-        return false;
+    wxArrayString args;
 
-    // check if there is any input available
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
+    const char *cptr = command.c_str();
 
-    const int fd = m_file->fd();
+    // split the command line in arguments
+    //
+    // TODO: combine this with wxCmdLineParser::ConvertStringToArgs(), it
+    //       doesn't do exactly the same thing right now but it's pretty close
+    //       and we shouldn't maintain 2 copies of this code
+    do
+    {
+        wxString argument;
+        char quotechar = '\0'; // is arg quoted?
+        bool escaped = false;
 
-    fd_set readfds;
+        // eat leading whitespace:
+        while ( wxIsspace(*cptr) )
+            cptr++;
 
-    wxFD_ZERO(&readfds);
-    wxFD_SET(fd, &readfds);
+        if ( *cptr == '\'' || *cptr == '"' )
+            quotechar = *cptr++;
 
-    switch ( select(fd + 1, &readfds, NULL, NULL, &tv) )
-    {
-        case -1:
-            wxLogSysError(_("Impossible to get child process input"));
-            // fall through
+        do
+        {
+            if ( *cptr == '\\' && !escaped )
+            {
+                escaped = true;
+                cptr++;
+                continue;
+            }
 
-        case 0:
-            return false;
+            // all other characters:
+            argument += *cptr++;
+            escaped = false;
 
-        default:
-            wxFAIL_MSG(_T("unexpected select() return value"));
-            // still fall through
+            // have we reached the end of the argument?
+            if ( (*cptr == quotechar && !escaped)
+                 || (quotechar == '\0' && wxIsspace(*cptr))
+                 || *cptr == '\0' )
+            {
+                args.push_back(argument);
 
-        case 1:
-            // 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();
-    }
+                // if not at end of buffer, swallow last character:
+                if ( *cptr )
+                    cptr++;
+
+                break; // done with this one, start over
+            }
+        } while ( *cptr );
+    } while ( *cptr );
+
+    ArgsArray argv(args);
+
+    // do execute the command
+    return wxExecute(argv, flags, process);
 }
 
-#endif // HAS_PIPE_INPUT_STREAM
+#if wxUSE_UNICODE
 
-// ----------------------------------------------------------------------------
-// wxExecute: the real worker function
-// ----------------------------------------------------------------------------
+long wxExecute(wchar_t **wargv, int flags, wxProcess *process)
+{
+    ArgsArray argv(wargv);
+
+    return wxExecute(argv, flags, process);
+}
 
-long wxExecute(wxChar **argv, int flags, wxProcess *process)
+#endif // wxUSE_UNICODE
+
+// wxExecute: the real worker function
+long wxExecute(char **argv, int flags, wxProcess *process)
 {
     // for the sync execution, we return -1 to indicate failure, but for async
     // case we return 0 which is never a valid PID
@@ -479,38 +516,29 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
 
     wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );
 
-#if wxUSE_UNICODE
-    int mb_argc = 0;
-    char *mb_argv[WXEXECUTE_NARGS];
+#if wxUSE_THREADS
+    // fork() doesn't mix well with POSIX threads: on many systems the program
+    // deadlocks or crashes for some reason. Probably our code is buggy and
+    // doesn't do something which must be done to allow this to work, but I
+    // don't know what yet, so for now just warn the user (this is the least we
+    // can do) about it
+    wxASSERT_MSG( wxThread::IsMain(),
+                    _T("wxExecute() can be called only from the main thread") );
+#endif // wxUSE_THREADS
 
-    while (argv[mb_argc])
+#if defined(__DARWIN__)
+    // wxMacLaunch() only executes app bundles and only does it asynchronously.
+    // It returns false if the target is not an app bundle, thus falling
+    // through to the regular code for non app bundles.
+    if ( !(flags & wxEXEC_SYNC) && wxMacLaunch(argv) )
     {
-        wxWX2MBbuf mb_arg = wxSafeConvertWX2MB(argv[mb_argc]);
-        mb_argv[mb_argc] = strdup(mb_arg);
-        mb_argc++;
+        // we don't have any PID to return so just make up something non null
+        return -1;
     }
-    mb_argv[mb_argc] = (char *) NULL;
-
-    // this macro will free memory we used above
-    #define ARGS_CLEANUP                                 \
-        for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
-            free(mb_argv[mb_argc])
-#else // ANSI
-    // no need for cleanup
-    #define ARGS_CLEANUP
-
-    wxChar **mb_argv = argv;
-#endif // Unicode/ANSI
-
-    // we want this function to work even if there is no wxApp so ensure that
-    // we have a valid traits pointer
-    wxConsoleAppTraits traitsConsole;
-    wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
-    if ( !traits )
-        traits = &traitsConsole;
-
-    // this struct contains all information which we pass to and from
-    // wxAppTraits methods
+#endif // __DARWIN__
+
+
+    // this struct contains all information which we use for housekeeping
     wxExecuteData execData;
     execData.flags = flags;
     execData.process = process;
@@ -520,8 +548,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
     {
         wxLogError( _("Failed to execute '%s'\n"), *argv );
 
-        ARGS_CLEANUP;
-
         return ERROR_RETURN_CODE;
     }
 
@@ -536,8 +562,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
         {
             wxLogError( _("Failed to execute '%s'\n"), *argv );
 
-            ARGS_CLEANUP;
-
             return ERROR_RETURN_CODE;
         }
     }
@@ -557,8 +581,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
     {
         wxLogSysError( _("Fork failed") );
 
-        ARGS_CLEANUP;
-
         return ERROR_RETURN_CODE;
     }
     else if ( pid == 0 )  // we're in child
@@ -618,12 +640,11 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
             pipeErr.Close();
         }
 
-        execvp (*mb_argv, mb_argv);
+        execvp(*argv, argv);
 
         fprintf(stderr, "execvp(");
-        // CS changed ppc to ppc_ as ppc is not available under mac os CW Mach-O
-        for ( char **ppc_ = mb_argv; *ppc_; ppc_++ )
-            fprintf(stderr, "%s%s", ppc_ == mb_argv ? "" : ", ", *ppc_);
+        for ( char **a = argv; *a; a++ )
+            fprintf(stderr, "%s%s", a == argv ? "" : ", ", *a);
         fprintf(stderr, ") failed with error %d!\n", errno);
 
         // there is no return after successful exec()
@@ -641,8 +662,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
     }
     else // we're in parent
     {
-        ARGS_CLEANUP;
-
         // save it for WaitForChild() use
         execData.pid = pid;
 
@@ -685,6 +704,13 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
             pipeErr.Close();
         }
 
+        // we want this function to work even if there is no wxApp so ensure
+        // that we have a valid traits pointer
+        wxConsoleAppTraits traitsConsole;
+        wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
+        if ( !traits )
+            traits = &traitsConsole;
+
         return traits->WaitForChild(execData);
     }
 
@@ -694,7 +720,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
 }
 
 #undef ERROR_RETURN_CODE
-#undef ARGS_CLEANUP
 
 // ----------------------------------------------------------------------------
 // file and directory functions
@@ -1412,18 +1437,24 @@ int wxAppTraits::WaitForChild(wxExecuteData& execData)
 {
     if ( !(execData.flags & wxEXEC_SYNC) )
     {
-        // asynchronous execution: just launch the process and return
+        // asynchronous execution: just launch the process and return,
+        // endProcData will be destroyed when it terminates (currently we leak
+        // it if the process doesn't terminate before we do and this should be
+        // fixed but it's not a real leak so it's not really very high
+        // priority)
         wxEndProcessData *endProcData = new wxEndProcessData;
-        endProcData->process  = execData.process;
-        endProcData->pid      = execData.pid;
+        endProcData->process = execData.process;
+        endProcData->pid = execData.pid;
         endProcData->tag = AddProcessCallback
                            (
                              endProcData,
                              execData.GetEndProcReadFD()
                            );
+        endProcData->async = true;
 
         return execData.pid;
     }
+    //else: synchronous execution case
 
 #if wxUSE_STREAMS
     wxProcess * const process = execData.process;
@@ -1464,9 +1495,16 @@ void wxHandleProcessTermination(wxEndProcessData *data)
         data->process->OnTerminate(data->pid, data->exitcode);
     }
 
-    // this function is only called for asynchronously executing children, now
-    // that we have received the notification about their termination there is
-    // no need to keep it around any longer
-    delete data;
+    if ( data->async )
+    {
+        // in case of asynchronous execution we don't need this data any more
+        // after the child terminates
+        delete data;
+    }
+    else // sync execution
+    {
+        // let wxExecute() know that the process has terminated
+        data->pid = 0;
+    }
 }