From: Vadim Zeitlin Date: Sun, 24 Oct 2010 22:40:11 +0000 (+0000) Subject: Add support for specifying child process cwd and env to wxExecute(). X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/164db92c14e9f1a7343259826da3653aeec945db Add support for specifying child process cwd and env to wxExecute(). Add an optional wxExecuteEnv parameter to wxExecute() which allows to specify the initial working directory and custom environment for the child process. Closes #12163. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65896 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index e189246df5..b34dfa11bc 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -405,6 +405,7 @@ Major new features in this release All: +- Added cwd and env arguments to wxExecute() (Emilien Kia). - Added "rest" argument to wxString::Before{First,Last}(). - Added wxThread::OnKill() and OnDelete() callbacks. diff --git a/include/wx/utils.h b/include/wx/utils.h index 32bed656c3..78f29a0063 100644 --- a/include/wx/utils.h +++ b/include/wx/utils.h @@ -19,6 +19,7 @@ #include "wx/object.h" #include "wx/list.h" #include "wx/filefn.h" +#include "wx/hashmap.h" #if wxUSE_GUI #include "wx/gdicmn.h" @@ -313,6 +314,17 @@ enum wxEXEC_BLOCK = wxEXEC_SYNC | wxEXEC_NOEVENTS }; +// Map storing environment variables. +typedef wxStringToStringHashMap wxEnvVariableHashMap; + +// Used to pass additional parameters for child process to wxExecute(). Could +// be extended with other fields later. +struct wxExecuteEnv +{ + wxString cwd; // If empty, CWD is not changed. + wxEnvVariableHashMap env; // If empty, environment is unchanged. +}; + // Execute another program. // // If flags contain wxEXEC_SYNC, return -1 on failure and the exit code of the @@ -320,27 +332,32 @@ enum // failure and the PID of the launched process if ok. WXDLLIMPEXP_BASE long wxExecute(const wxString& command, int flags = wxEXEC_ASYNC, - wxProcess *process = NULL); + wxProcess *process = NULL, + const wxExecuteEnv *env = NULL); WXDLLIMPEXP_BASE long wxExecute(char **argv, int flags = wxEXEC_ASYNC, - wxProcess *process = NULL); + wxProcess *process = NULL, + const wxExecuteEnv *env = NULL); #if wxUSE_UNICODE WXDLLIMPEXP_BASE long wxExecute(wchar_t **argv, int flags = wxEXEC_ASYNC, - wxProcess *process = NULL); + wxProcess *process = NULL, + const wxExecuteEnv *env = NULL); #endif // wxUSE_UNICODE // execute the command capturing its output into an array line by line, this is // always synchronous WXDLLIMPEXP_BASE long wxExecute(const wxString& command, wxArrayString& output, - int flags = 0); + int flags = 0, + const wxExecuteEnv *env = NULL); // also capture stderr (also synchronous) WXDLLIMPEXP_BASE long wxExecute(const wxString& command, wxArrayString& output, wxArrayString& error, - int flags = 0); + int flags = 0, + const wxExecuteEnv *env = NULL); #if defined(__WXMSW__) && wxUSE_IPC // ask a DDE server to execute the DDE request with given parameters @@ -479,6 +496,10 @@ inline bool wxSetEnv(const wxString& var, int value) } #endif // WXWIN_COMPATIBILITY_2_8 +// Retrieve the complete environment by filling specified map. +// Returns true on success or false if an error occurred. +WXDLLIMPEXP_BASE bool wxGetEnvMap(wxEnvVariableHashMap *map); + // ---------------------------------------------------------------------------- // Network and username functions. // ---------------------------------------------------------------------------- diff --git a/interface/wx/utils.h b/interface/wx/utils.h index c09df344ba..d62ddf1e92 100644 --- a/interface/wx/utils.h +++ b/interface/wx/utils.h @@ -157,6 +157,18 @@ void wxInfoMessageBox(wxWindow parent = NULL); /** @addtogroup group_funcmacro_env */ //@{ +/** + A map type containing environment variables names and values. + + This type is used with wxGetEnvMap() function and wxExecuteEnv structure + optionally passed to wxExecute(). + + @since 2.9.2 + + @header{wx/utils.h} +*/ +typedef wxStringToStringHashMap wxEnvVariableHashMap; + /** This is a macro defined as @c getenv() or its wide char version in Unicode mode. @@ -218,6 +230,22 @@ bool wxSetEnv(const wxString& var, const wxString& value); */ bool wxUnsetEnv(const wxString& var); +/** + Fill a map with the complete content of current environment. + + The map will contain the environment variable names as keys and their + values as values. + + @param map + The environment map to fill, must be non-@NULL. + @return + @true if environment was successfully retrieved or @false otherwise. + + @header{wx/utils.h} + + @since 2.9.2 +*/ +bool wxGetEnvMap(wxEnvVariableHashMap *map); //@} @@ -735,6 +763,36 @@ wxLinuxDistributionInfo wxGetLinuxDistributionInfo(); /** @addtogroup group_funcmacro_procctrl */ //@{ +/** + @struct wxExecuteEnv + + This structure can optionally be passed to wxExecute() to specify + additional options to use for the child process. + + @since 2.9.2 + + @header{wx/utils.h} +*/ +struct wxExecuteEnv +{ + /** + The initial working directory for the new process. + + If this field is empty, the current working directory of this process + is used. + */ + wxString cwd; + + /** + The environment variable map. + + If the map is empty, the environment variables of the current process + are also used for the child one, otherwise only the variables defined + in this map are used. + */ + wxEnvVariableHashMap env; +}; + /** Executes another program in Unix or Windows. @@ -800,6 +858,10 @@ wxLinuxDistributionInfo wxGetLinuxDistributionInfo(); their combination, in wxEXEC_SYNC case. @param callback An optional pointer to wxProcess. + @param env + An optional pointer to additional parameters for the child process, + such as its initial working directory and environment variables. This + parameter is available in wxWidgets 2.9.2 and later only. @see wxShell(), wxProcess, @ref page_samples_exec, wxLaunchDefaultApplication(), wxLaunchDefaultBrowser() @@ -811,8 +873,8 @@ wxLinuxDistributionInfo wxGetLinuxDistributionInfo(); @endWxPerlOnly */ long wxExecute(const wxString& command, int flags = wxEXEC_ASYNC, - wxProcess* callback = NULL); - + wxProcess* callback = NULL, + const wxExecuteEnv* env = NULL); //@} /** @addtogroup group_funcmacro_procctrl */ @@ -835,6 +897,10 @@ long wxExecute(const wxString& command, int flags = wxEXEC_ASYNC, their combination, in wxEXEC_SYNC case. @param callback An optional pointer to wxProcess. + @param env + An optional pointer to additional parameters for the child process, + such as its initial working directory and environment variables. This + parameter is available in wxWidgets 2.9.2 and later only. @see wxShell(), wxProcess, @ref page_samples_exec, wxLaunchDefaultApplication(), wxLaunchDefaultBrowser() @@ -846,9 +912,11 @@ long wxExecute(const wxString& command, int flags = wxEXEC_ASYNC, @endWxPerlOnly */ long wxExecute(char** argv, int flags = wxEXEC_ASYNC, - wxProcess* callback = NULL); + wxProcess* callback = NULL, + const wxExecuteEnv *env = NULL); long wxExecute(wchar_t** argv, int flags = wxEXEC_ASYNC, - wxProcess* callback = NULL); + wxProcess* callback = NULL, + const wxExecuteEnv *env = NULL); //@} /** @addtogroup group_funcmacro_procctrl */ @@ -871,6 +939,10 @@ long wxExecute(wchar_t** argv, int flags = wxEXEC_ASYNC, May include wxEXEC_NOHIDE, wxEXEC_MAKE_GROUP_LEADER (in either case) or wxEXEC_NODISABLE and wxEXEC_NOEVENTS or wxEXEC_BLOCK, which is equal to their combination. wxEXEC_SYNC is always implicitly added to the flags. + @param env + An optional pointer to additional parameters for the child process, + such as its initial working directory and environment variables. This + parameter is available in wxWidgets 2.9.2 and later only. @see wxShell(), wxProcess, @ref page_samples_exec, wxLaunchDefaultApplication(), wxLaunchDefaultBrowser() @@ -883,7 +955,8 @@ long wxExecute(wchar_t** argv, int flags = wxEXEC_ASYNC, where @c output in an array reference. @endWxPerlOnly */ -long wxExecute(const wxString& command, wxArrayString& output, int flags = 0); +long wxExecute(const wxString& command, wxArrayString& output, int flags = 0, + const wxExecuteEnv *env = NULL); /** This is an overloaded version of wxExecute(const wxString&,int,wxProcess*), @@ -904,6 +977,10 @@ long wxExecute(const wxString& command, wxArrayString& output, int flags = 0); May include wxEXEC_NOHIDE, wxEXEC_MAKE_GROUP_LEADER (in either case) or wxEXEC_NODISABLE and wxEXEC_NOEVENTS or wxEXEC_BLOCK, which is equal to their combination. wxEXEC_SYNC is always implicitly added to the flags. + @param env + An optional pointer to additional parameters for the child process, + such as its initial working directory and environment variables. This + parameter is available in wxWidgets 2.9.2 and later only. @see wxShell(), wxProcess, @ref page_samples_exec, wxLaunchDefaultApplication(), wxLaunchDefaultBrowser() @@ -917,7 +994,8 @@ long wxExecute(const wxString& command, wxArrayString& output, int flags = 0); @endWxPerlOnly */ long wxExecute(const wxString& command, wxArrayString& output, - wxArrayString& errors, int flags = 0); + wxArrayString& errors, int flags = 0, + const wxExecuteEnv *env = NULL); /** Returns the number uniquely identifying the current process in the system. diff --git a/samples/exec/exec.cpp b/samples/exec/exec.cpp index fcbe8b3029..4f72a17958 100644 --- a/samples/exec/exec.cpp +++ b/samples/exec/exec.cpp @@ -43,6 +43,8 @@ #include "wx/choicdlg.h" #include "wx/button.h" + #include "wx/checkbox.h" + #include "wx/stattext.h" #include "wx/textctrl.h" #include "wx/listbox.h" @@ -666,6 +668,147 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) } } +// ---------------------------------------------------------------------------- +// execution options dialog +// ---------------------------------------------------------------------------- + +enum ExecQueryDialogID +{ + TEXT_EXECUTABLE, + TEXT_CWD, + TEXT_ENVIRONMENT +}; + +class ExecQueryDialog : public wxDialog +{ +public: + ExecQueryDialog(const wxString& cmd); + + wxString GetExecutable() const + { + return m_executable->GetValue(); + } + + wxString GetWorkDir() const + { + return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString(); + } + + void GetEnvironment(wxEnvVariableHashMap& env); + +private: + void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event) + { + event.Enable(m_useCWD->GetValue()); + } + + void OnUpdateEnvironmentUI(wxUpdateUIEvent& event) + { + event.Enable(m_useEnv->GetValue()); + } + + wxTextCtrl* m_executable; + wxTextCtrl* m_cwdtext; + wxTextCtrl* m_envtext; + wxCheckBox* m_useCWD; + wxCheckBox* m_useEnv; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog) + EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI) + EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI) +END_EVENT_TABLE() + +ExecQueryDialog::ExecQueryDialog(const wxString& cmd) + : wxDialog(NULL, wxID_ANY, DIALOG_TITLE, + wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL); + + m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString()); + m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString()); + m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(), + wxDefaultPosition, wxSize(300, 200), + wxTE_MULTILINE|wxHSCROLL); + + const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border(); + globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "), + flagsExpand); + globalSizer->Add(m_executable, flagsExpand); + + m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: "); + globalSizer->Add(m_useCWD, flagsExpand); + globalSizer->Add(m_cwdtext, flagsExpand); + + m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: "); + globalSizer->Add(m_useEnv, flagsExpand); + globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1)); + + globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand); + SetSizerAndFit(globalSizer); + + + m_executable->SetValue(cmd); + m_cwdtext->SetValue(wxGetCwd()); + wxEnvVariableHashMap env; + if ( wxGetEnvMap(&env) ) + { + for ( wxEnvVariableHashMap::iterator it = env.begin(); + it != env.end(); + ++it ) + { + m_envtext->AppendText(it->first + '=' + it->second + '\n'); + } + } + m_useCWD->SetValue(false); + m_useEnv->SetValue(false); +} + +void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env) +{ + env.clear(); + if ( m_useEnv->GetValue() ) + { + wxString name, + value; + + const int nb = m_envtext->GetNumberOfLines(); + for ( int l = 0; l < nb; l++ ) + { + const wxString line = m_envtext->GetLineText(l).Trim(); + + if ( !line.empty() ) + { + name = line.BeforeFirst('=', &value); + if ( name.empty() ) + { + wxLogWarning("Skipping invalid environment line \"%s\".", line); + continue; + } + + env[name] = value; + } + } + } +} + +static bool QueryExec(wxString& cmd, wxExecuteEnv& env) +{ + ExecQueryDialog dialog(cmd); + + if ( dialog.ShowModal() != wxID_OK ) + return false; + + cmd = dialog.GetExecutable(); + env.cwd = dialog.GetWorkDir(); + dialog.GetEnvironment(env.env); + + return true; +} + // ---------------------------------------------------------------------------- // event handlers: exec menu // ---------------------------------------------------------------------------- @@ -694,16 +837,14 @@ void MyFrame::DoAsyncExec(const wxString& cmd) void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event)) { - wxString cmd = wxGetTextFromUser(wxT("Enter the command: "), - DIALOG_TITLE, - m_cmdLast); - - if ( !cmd ) + wxString cmd; + wxExecuteEnv env; + if ( !QueryExec(cmd, env) ) return; wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() ); - int code = wxExecute(cmd, wxEXEC_SYNC); + int code = wxExecute(cmd, wxEXEC_SYNC, NULL, &env); wxLogStatus(wxT("Process '%s' terminated with exit code %d."), cmd.c_str(), code); diff --git a/src/cocoa/utilsexc.mm b/src/cocoa/utilsexc.mm index 398834798f..7df71bf321 100644 --- a/src/cocoa/utilsexc.mm +++ b/src/cocoa/utilsexc.mm @@ -142,7 +142,8 @@ WX_IMPLEMENT_GET_OBJC_CLASS(wxTaskHandler,NSObject) long wxExecute(const wxString& command, int sync, - wxProcess *handle) + wxProcess *handle, + const wxExecuteEnv *env) { NSTask* theTask = [[NSTask alloc] init]; diff --git a/src/common/utilscmn.cpp b/src/common/utilscmn.cpp index 2667f5885b..5176c5236f 100644 --- a/src/common/utilscmn.cpp +++ b/src/common/utilscmn.cpp @@ -543,6 +543,43 @@ wxString wxGetCurrentDir() #endif // 0 +// ---------------------------------------------------------------------------- +// Environment +// ---------------------------------------------------------------------------- + +bool wxGetEnvMap(wxEnvVariableHashMap *map) +{ + wxCHECK_MSG( map, false, wxS("output pointer can't be NULL") ); + +#if defined(__VISUALC__) + wxChar **env = _tenviron; +#else // non-MSVC + // Not sure if other compilers have _tenviron so use the (more standard) + // ANSI version only for them. + char ** env = environ; +#endif + + if ( env ) + { + wxString name, + value; + while ( *env ) + { + const wxString var(*env); + + name = var.BeforeFirst(wxS('='), &value); + + (*map)[name] = value; + + env++; + } + + return true; + } + + return false; +} + // ---------------------------------------------------------------------------- // wxExecute // ---------------------------------------------------------------------------- @@ -590,13 +627,14 @@ static bool ReadAll(wxInputStream *is, wxArrayString& output) static long wxDoExecuteWithCapture(const wxString& command, wxArrayString& output, wxArrayString* error, - int flags) + int flags, + const wxExecuteEnv *env) { // create a wxProcess which will capture the output wxProcess *process = new wxProcess; process->Redirect(); - long rc = wxExecute(command, wxEXEC_SYNC | flags, process); + long rc = wxExecute(command, wxEXEC_SYNC | flags, process, env); #if wxUSE_STREAMS if ( rc != -1 ) @@ -621,17 +659,19 @@ static long wxDoExecuteWithCapture(const wxString& command, return rc; } -long wxExecute(const wxString& command, wxArrayString& output, int flags) +long wxExecute(const wxString& command, wxArrayString& output, int flags, + const wxExecuteEnv *env) { - return wxDoExecuteWithCapture(command, output, NULL, flags); + return wxDoExecuteWithCapture(command, output, NULL, flags, env); } long wxExecute(const wxString& command, wxArrayString& output, wxArrayString& error, - int flags) + int flags, + const wxExecuteEnv *env) { - return wxDoExecuteWithCapture(command, output, &error, flags); + return wxDoExecuteWithCapture(command, output, &error, flags, env); } // ---------------------------------------------------------------------------- diff --git a/src/msdos/utilsdos.cpp b/src/msdos/utilsdos.cpp index 3d1095cf59..1b47458ab4 100644 --- a/src/msdos/utilsdos.cpp +++ b/src/msdos/utilsdos.cpp @@ -314,7 +314,8 @@ bool wxShell(const wxString& command /*=wxEmptyString*/) return result == 0; } -long wxExecute(const wxString& command, int flags, wxProcess *process) +long wxExecute(const wxString& command, int flags, wxProcess *process, + const wxExecuteEnv *env) { // FIXME: shouldn't depend on wxCmdLineParser wxArrayString args(wxCmdLineParser::ConvertStringToArgs(command)); @@ -439,7 +440,8 @@ wxString wxRedirectableFd::Release() // wxExecute implementation // -long wxExecute(wxChar **argv, int flags, wxProcess *process) +long wxExecute(wxChar **argv, int flags, wxProcess *process, + const wxString* cwd, const wxEnvVariableHashMap* env) { #if wxUSE_STREAMS const int STDIN = 0; diff --git a/src/msw/utilsexc.cpp b/src/msw/utilsexc.cpp index 8e86fb663c..6fc63c0ee1 100644 --- a/src/msw/utilsexc.cpp +++ b/src/msw/utilsexc.cpp @@ -610,7 +610,8 @@ wxExecuteDDE(const wxString& ddeServer, #endif // wxUSE_IPC -long wxExecute(const wxString& cmd, int flags, wxProcess *handler) +long wxExecute(const wxString& cmd, int flags, wxProcess *handler, + const wxExecuteEnv *env) { wxCHECK_MSG( !cmd.empty(), 0, wxT("empty command in wxExecute") ); @@ -800,6 +801,55 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) wxString arguments = command.AfterFirst(wxT(' ')); #endif + wxWxCharBuffer envBuffer; + bool useCwd = false; + if ( env ) + { + useCwd = !env->cwd.empty(); + + // Translate environment variable map into NUL-terminated list of + // NUL-terminated strings. + if ( !env->env.empty() ) + { +#if wxUSE_UNICODE + // Environment variables can contain non-ASCII characters. We could + // check for it and not use this flag if everything is really ASCII + // only but there doesn't seem to be any reason to do it so just + // assume Unicode by default. + dwFlags |= CREATE_UNICODE_ENVIRONMENT; +#endif // wxUSE_UNICODE + + wxEnvVariableHashMap::const_iterator it; + + size_t envSz = 1; // ending '\0' + for ( it = env->env.begin(); it != env->env.end(); ++it ) + { + // Add size of env variable name and value, and '=' char and + // ending '\0' + envSz += it->first.length() + it->second.length() + 2; + } + + envBuffer.extend(envSz); + + wxChar *p = envBuffer.data(); + for ( it = env->env.begin(); it != env->env.end(); ++it ) + { + const wxString line = it->first + wxS("=") + it->second; + + // Include the trailing NUL which will always terminate the + // buffer returned by t_str(). + const size_t len = line.length() + 1; + + wxTmemcpy(p, line.t_str(), len); + + p += len; + } + + // And another NUL to terminate the list of NUL-terminated strings. + *p = 0; + } + } + bool ok = ::CreateProcess ( // WinCE requires appname to be non null @@ -818,8 +868,10 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) NULL, // the process and its main thread redirect, // inherit handles if we use pipes dwFlags, // process creation flags - NULL, // environment (use the same) - NULL, // current directory (use the same) + envBuffer.data(), // environment (may be NULL which is fine) + useCwd // initial working directory + ? const_cast(env->cwd.wx_str()) + : NULL, // (or use the same) &si, // startup info (unused here) &pi // process info ) != 0; @@ -1039,7 +1091,8 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler) } template -long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler) +long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { wxString command; command.reserve(1024); @@ -1078,19 +1131,21 @@ long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler) command += ' '; } - return wxExecute(command, flags, handler); + return wxExecute(command, flags, handler, env); } -long wxExecute(char **argv, int flags, wxProcess *handler) +long wxExecute(char **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { - return wxExecuteImpl(argv, flags, handler); + return wxExecuteImpl(argv, flags, handler, env); } #if wxUSE_UNICODE -long wxExecute(wchar_t **argv, int flags, wxProcess *handler) +long wxExecute(wchar_t **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { - return wxExecuteImpl(argv, flags, handler); + return wxExecuteImpl(argv, flags, handler, env); } #endif // wxUSE_UNICODE diff --git a/src/os2/utilsexc.cpp b/src/os2/utilsexc.cpp index c50a311691..199781f7cf 100644 --- a/src/os2/utilsexc.cpp +++ b/src/os2/utilsexc.cpp @@ -128,7 +128,8 @@ MRESULT APIENTRY wxExecuteWindowCbk( HWND hWnd, long wxExecute( const wxString& rCommand, int flags, - wxProcess* pHandler) + wxProcess* pHandler, + const wxExecuteEnv *env) { if (rCommand.empty()) { @@ -219,6 +220,7 @@ long wxExecute( char** ppArgv , int flags , wxProcess* pHandler +, const wxExecuteEnv *env ) { wxString sCommand; @@ -234,6 +236,7 @@ long wxExecute( return wxExecute( sCommand ,flags ,pHandler + , env ); } diff --git a/src/palmos/utilsexc.cpp b/src/palmos/utilsexc.cpp index eaa04a4e6b..2d224edacb 100644 --- a/src/palmos/utilsexc.cpp +++ b/src/palmos/utilsexc.cpp @@ -87,12 +87,14 @@ static bool wxExecuteDDE(const wxString& ddeServer, #endif // wxUSE_IPC -long wxExecute(const wxString& cmd, int flags, wxProcess *handler) +long wxExecute(const wxString& cmd, int flags, wxProcess *handler, + const wxExecuteEnv *env) { return 0; } -long wxExecute(wxChar **argv, int flags, wxProcess *handler) +long wxExecute(wxChar **argv, int flags, wxProcess *handler, + const wxExecuteEnv *env) { return 0; } diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 6126c0a2e5..fa2eb47287 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -442,27 +442,30 @@ private: bool wxMacLaunch(char **argv); #endif -long wxExecute(const wxString& command, int flags, wxProcess *process) +long wxExecute(const wxString& command, int flags, wxProcess *process, + const wxExecuteEnv *env) { ArgsArray argv(wxCmdLineParser::ConvertStringToArgs(command, wxCMD_LINE_SPLIT_UNIX)); - return wxExecute(argv, flags, process); + return wxExecute(argv, flags, process, env); } #if wxUSE_UNICODE -long wxExecute(wchar_t **wargv, int flags, wxProcess *process) +long wxExecute(wchar_t **wargv, int flags, wxProcess *process, + const wxExecuteEnv *env) { ArgsArray argv(wargv); - return wxExecute(argv, flags, process); + return wxExecute(argv, flags, process, env); } #endif // wxUSE_UNICODE // wxExecute: the real worker function -long wxExecute(char **argv, int flags, wxProcess *process) +long wxExecute(char **argv, int flags, wxProcess *process, + const wxExecuteEnv *env) { // for the sync execution, we return -1 to indicate failure, but for async // case we return 0 which is never a valid PID @@ -577,6 +580,40 @@ long wxExecute(char **argv, int flags, wxProcess *process) pipeErr.Close(); } + // Process additional options if we have any + if ( env ) + { + // Change working directory if it is specified + if ( !env->cwd.empty() ) + wxSetWorkingDirectory(env->cwd); + + // Change environment if needed. + // + // NB: We can't use execve() currently because we allow using + // non full paths to wxExecute(), i.e. we want to search for + // the program in PATH. However it just might be simpler/better + // to do the search manually and use execve() envp parameter to + // set up the environment of the child process explicitly + // instead of doing what we do below. + if ( !env->env.empty() ) + { + wxEnvVariableHashMap oldenv; + wxGetEnvMap(&oldenv); + + // Remove unwanted variables + wxEnvVariableHashMap::const_iterator it; + for ( it = oldenv.begin(); it != oldenv.end(); ++it ) + { + if ( env->env.find(it->first) == env->env.end() ) + wxUnsetEnv(it->first); + } + + // And add the new ones (possibly replacing the old values) + for ( it = env->env.begin(); it != env->env.end(); ++it ) + wxSetEnv(it->first, it->second); + } + } + execvp(*argv, argv); fprintf(stderr, "execvp(");