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(");