+ wxMenuBar* const mbar = GetMenuBar();
+
+ int flags = 0;
+
+ if ( mbar->IsChecked(Exec_Flags_HideConsole) )
+ flags |= wxEXEC_HIDE_CONSOLE;
+ if ( mbar->IsChecked(Exec_Flags_ShowConsole) )
+ flags |= wxEXEC_SHOW_CONSOLE;
+ if ( mbar->IsChecked(Exec_Flags_NoEvents) )
+ flags |= wxEXEC_NOEVENTS;
+
+ return flags;
+}
+
+void MyFrame::DoAsyncExec(const wxString& cmd)
+{
+ MyProcess * const process = new MyProcess(this, cmd);
+ m_pidLast = wxExecute(cmd, wxEXEC_ASYNC | GetExecFlags(), process);
+ if ( !m_pidLast )
+ {
+ wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
+
+ delete process;
+ }
+ else
+ {
+ wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
+
+ m_cmdLast = cmd;
+
+ // the parent frame keeps track of all async processes as it needs to
+ // free them if we exit before the child process terminates
+ AddAsyncProcess(process);
+ }
+}
+
+void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
+{
+ 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 | GetExecFlags(), NULL, &env);
+
+ wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
+ cmd.c_str(), code);
+
+ m_cmdLast = cmd;
+}
+
+void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
+{
+ wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
+ DIALOG_TITLE,
+ m_cmdLast);
+
+ if ( !cmd )
+ return;
+
+ DoAsyncExec(cmd);
+}
+
+void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
+{
+ wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
+ DIALOG_TITLE,
+ m_cmdLast);
+
+ if ( !cmd )
+ return;
+
+ int code = wxShell(cmd);
+ wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
+ cmd.c_str(), code);
+ m_cmdLast = cmd;
+}
+
+void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !m_cmdLast )
+ {
+#ifdef __WXMSW__
+ m_cmdLast = "type Makefile.in";
+#else
+ m_cmdLast = "cat -n Makefile";
+#endif
+ }
+
+ wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
+ DIALOG_TITLE,
+ m_cmdLast);
+
+ if ( !cmd )
+ return;
+
+ bool sync;
+ switch ( wxMessageBox(wxT("Execute it synchronously?"),
+ wxT("Exec question"),
+ wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
+ {
+ case wxYES:
+ sync = true;
+ break;
+
+ case wxNO:
+ sync = false;
+ break;
+
+ default:
+ return;
+ }
+
+ if ( sync )
+ {
+ wxLogStatus("\"%s\" is running please wait...", cmd);
+
+ wxStopWatch sw;
+
+ wxArrayString output, errors;
+ int code = wxExecute(cmd, output, errors);
+
+ wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
+ cmd, sw.Time(), code);
+
+ if ( code != -1 )
+ {
+ ShowOutput(cmd, output, wxT("Output"));
+ ShowOutput(cmd, errors, wxT("Errors"));
+ }
+ }
+ else // async exec
+ {
+ MyPipedProcess *process = new MyPipedProcess(this, cmd);
+ if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
+ {
+ wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
+
+ delete process;
+ }
+ else
+ {
+ AddPipedProcess(process);
+ }
+ }
+
+ m_cmdLast = cmd;
+}
+
+void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !m_cmdLast )
+ m_cmdLast = wxT("tr [a-z] [A-Z]");
+
+ wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
+ DIALOG_TITLE,
+ m_cmdLast);
+
+ if ( !cmd )
+ return;
+
+ wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
+ DIALOG_TITLE);
+ if ( !input )
+ return;
+
+ // always execute the filter asynchronously
+ MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
+ long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
+ if ( pid )
+ {
+ wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
+
+ AddPipedProcess(process);
+ }
+ else
+ {
+ wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
+
+ delete process;
+ }
+
+ m_cmdLast = cmd;
+}
+
+void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
+{
+ wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
+ DIALOG_TITLE,
+ m_cmdLast);
+ if ( cmd.empty() )
+ return;
+
+ wxProcess *process = wxProcess::Open(cmd);
+ if ( !process )
+ {
+ wxLogError(wxT("Failed to launch the command."));
+ return;
+ }
+
+ wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
+
+ wxOutputStream *out = process->GetOutputStream();
+ if ( !out )
+ {
+ wxLogError(wxT("Failed to connect to child stdin"));
+ return;
+ }
+
+ wxInputStream *in = process->GetInputStream();
+ if ( !in )
+ {
+ wxLogError(wxT("Failed to connect to child stdout"));
+ return;
+ }
+
+ new MyPipeFrame(this, cmd, process);
+}
+
+static wxString gs_lastFile;
+
+static bool AskUserForFileName()
+{
+ wxString filename;
+
+#if wxUSE_FILEDLG
+ filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
+#else // !wxUSE_FILEDLG
+ filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
+ gs_lastFile);
+#endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
+
+ if ( filename.empty() )
+ return false;
+
+ gs_lastFile = filename;
+
+ return true;
+}
+
+void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !AskUserForFileName() )
+ return;
+
+ wxString ext = gs_lastFile.AfterLast(wxT('.'));
+ wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
+ if ( !ft )
+ {
+ wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
+ ext.c_str());
+ return;
+ }
+
+ wxString cmd;
+ bool ok = ft->GetOpenCommand(&cmd,
+ wxFileType::MessageParameters(gs_lastFile));
+ delete ft;
+ if ( !ok )
+ {
+ wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
+ ext.c_str());
+ return;
+ }
+
+ DoAsyncExec(cmd);
+}
+
+void MyFrame::OnShowCommandForExt(wxCommandEvent& WXUNUSED(event))
+{
+ static wxString s_ext;
+
+ wxString ext = wxGetTextFromUser
+ (
+ "Enter the extension without leading dot",
+ "Exec sample",
+ s_ext,
+ this
+ );
+ if ( ext.empty() )
+ return;
+
+ s_ext = ext;
+
+ wxScopedPtr<wxFileType>
+ ft(wxTheMimeTypesManager->GetFileTypeFromExtension(ext));
+ if ( !ft )
+ {
+ wxLogError("Information for extension \"%s\" not found", ext);
+ return;
+ }
+
+ const wxString cmd = ft->GetOpenCommand("file." + ext);
+ if ( cmd.empty() )
+ {
+ wxLogWarning("Open command for extension \"%s\" not defined.", ext);
+ return;
+ }
+
+ wxLogMessage("Open command for files of extension \"%s\" is\n%s",
+ ext, cmd);
+}
+
+void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !AskUserForFileName() )
+ return;
+
+ if ( !wxLaunchDefaultApplication(gs_lastFile) )
+ {
+ wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
+ }
+}
+
+void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
+{
+ static wxString s_url(wxT("http://www.wxwidgets.org/"));
+
+ wxString filename = wxGetTextFromUser
+ (
+ wxT("Enter the URL"),
+ wxT("exec sample"),
+ s_url,
+ this
+ );
+
+ if ( filename.empty() )
+ return;
+
+ s_url = filename;
+
+ if ( !wxLaunchDefaultBrowser(s_url) )
+ {
+ wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
+ }
+}
+
+// ----------------------------------------------------------------------------
+// DDE stuff
+// ----------------------------------------------------------------------------
+
+#ifdef __WINDOWS__
+
+bool MyFrame::GetDDEServer()
+{
+ wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
+ DIALOG_TITLE, m_server);
+ if ( !server )
+ return false;
+
+ m_server = server;
+
+ wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
+ if ( !topic )
+ return false;
+
+ m_topic = topic;
+
+ wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
+ if ( !cmd )
+ return false;
+
+ m_cmdDde = cmd;
+
+ return true;
+}
+
+void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !GetDDEServer() )
+ return;
+
+ wxDDEClient client;
+ wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
+ if ( !conn )
+ {
+ wxLogError(wxT("Failed to connect to the DDE server '%s'."),
+ m_server.c_str());
+ }
+ else
+ {
+ if ( !conn->Execute(m_cmdDde) )
+ {
+ wxLogError(wxT("Failed to execute command '%s' via DDE."),
+ m_cmdDde.c_str());
+ }
+ else
+ {
+ wxLogStatus(wxT("Successfully executed DDE command"));
+ }
+ }
+}
+
+void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !GetDDEServer() )
+ return;
+
+ wxDDEClient client;
+ wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
+ if ( !conn )
+ {
+ wxLogError(wxT("Failed to connect to the DDE server '%s'."),
+ m_server.c_str());
+ }
+ else
+ {
+ if ( !conn->Request(m_cmdDde) )
+ {
+ wxLogError(wxT("Failed to send request '%s' via DDE."),
+ m_cmdDde.c_str());
+ }
+ else
+ {
+ wxLogStatus(wxT("Successfully sent DDE request."));
+ }
+ }
+}
+
+#endif // __WINDOWS__
+
+// ----------------------------------------------------------------------------
+// various helpers
+// ----------------------------------------------------------------------------
+
+// input polling
+void MyFrame::OnIdle(wxIdleEvent& event)
+{
+ size_t count = m_running.GetCount();
+ for ( size_t n = 0; n < count; n++ )
+ {
+ if ( m_running[n]->HasInput() )
+ {
+ event.RequestMore();
+ }
+ }
+}
+
+void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
+{
+ wxWakeUpIdle();
+}
+
+void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
+{
+ static unsigned long s_ticks = 0;
+ SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
+}
+
+void MyFrame::OnProcessTerminated(MyPipedProcess *process)
+{
+ RemovePipedProcess(process);
+}
+
+void MyFrame::OnAsyncTermination(MyProcess *process)
+{
+ m_allAsync.Remove(process);
+
+ delete process;
+}
+
+void MyFrame::AddPipedProcess(MyPipedProcess *process)
+{
+ if ( m_running.IsEmpty() )
+ {
+ // we want to start getting the timer events to ensure that a
+ // steady stream of idle events comes in -- otherwise we
+ // wouldn't be able to poll the child process input
+ m_timerIdleWakeUp.Start(100);
+ }
+ //else: the timer is already running
+
+ m_running.Add(process);
+ m_allAsync.Add(process);
+}
+
+void MyFrame::RemovePipedProcess(MyPipedProcess *process)
+{
+ m_running.Remove(process);
+
+ if ( m_running.IsEmpty() )
+ {
+ // we don't need to get idle events all the time any more
+ m_timerIdleWakeUp.Stop();
+ }
+}
+
+void MyFrame::ShowOutput(const wxString& cmd,
+ const wxArrayString& output,
+ const wxString& title)
+{
+ size_t count = output.GetCount();
+ if ( !count )
+ return;
+
+ m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
+ title.c_str(), cmd.c_str()));
+
+ for ( size_t n = 0; n < count; n++ )
+ {
+ m_lbox->Append(output[n]);
+ }
+
+ m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
+ title.Lower().c_str()));
+}
+
+// ----------------------------------------------------------------------------
+// MyProcess
+// ----------------------------------------------------------------------------
+
+void MyProcess::OnTerminate(int pid, int status)
+{
+ wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
+ pid, m_cmd.c_str(), status);
+
+ m_parent->OnAsyncTermination(this);
+}
+
+// ----------------------------------------------------------------------------
+// MyPipedProcess
+// ----------------------------------------------------------------------------
+
+bool MyPipedProcess::HasInput()
+{
+ bool hasInput = false;
+
+ if ( IsInputAvailable() )
+ {
+ wxTextInputStream tis(*GetInputStream());
+
+ // this assumes that the output is always line buffered
+ wxString msg;
+ msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
+
+ m_parent->GetLogListBox()->Append(msg);
+
+ hasInput = true;
+ }
+
+ if ( IsErrorAvailable() )
+ {
+ wxTextInputStream tis(*GetErrorStream());
+
+ // this assumes that the output is always line buffered
+ wxString msg;
+ msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
+
+ m_parent->GetLogListBox()->Append(msg);
+
+ hasInput = true;
+ }
+
+ return hasInput;
+}
+
+void MyPipedProcess::OnTerminate(int pid, int status)
+{
+ // show the rest of the output
+ while ( HasInput() )
+ ;
+
+ m_parent->OnProcessTerminated(this);
+
+ MyProcess::OnTerminate(pid, status);
+}
+
+// ----------------------------------------------------------------------------
+// MyPipedProcess2
+// ----------------------------------------------------------------------------
+
+bool MyPipedProcess2::HasInput()
+{
+ if ( !m_input.empty() )
+ {
+ wxTextOutputStream os(*GetOutputStream());
+ os.WriteString(m_input);
+
+ CloseOutput();
+ m_input.clear();
+
+ // call us once again - may be we'll have output
+ return true;
+ }
+
+ return MyPipedProcess::HasInput();
+}
+
+// ============================================================================
+// MyPipeFrame implementation
+// ============================================================================
+
+MyPipeFrame::MyPipeFrame(wxFrame *parent,
+ const wxString& cmd,
+ wxProcess *process)
+ : wxFrame(parent, wxID_ANY, cmd),
+ m_process(process),
+ // in a real program we'd check that the streams are !NULL here
+ m_out(*process->GetOutputStream()),
+ m_in(*process->GetInputStream()),
+ m_err(*process->GetErrorStream())
+{
+ m_process->SetNextHandler(this);
+
+ wxPanel *panel = new wxPanel(this, wxID_ANY);
+
+ m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxTE_PROCESS_ENTER);
+ m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxTE_MULTILINE | wxTE_RICH);
+ m_textIn->SetEditable(false);
+ m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxTE_MULTILINE | wxTE_RICH);
+ m_textErr->SetEditable(false);
+
+ wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
+ sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
+
+ wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
+ sizerBtns->
+ Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
+ sizerBtns->
+ Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
+ sizerBtns->
+ Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
+ sizerBtns->
+ Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
+
+ sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
+ sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
+ sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
+
+ panel->SetSizer(sizerTop);
+ sizerTop->Fit(this);
+
+ Show();
+}
+
+void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
+{
+#if wxUSE_FILEDLG
+ wxFileDialog filedlg(this, wxT("Select file to send"));
+ if ( filedlg.ShowModal() != wxID_OK )
+ return;
+
+ wxFFile file(filedlg.GetFilename(), wxT("r"));
+ wxString data;
+ if ( !file.IsOpened() || !file.ReadAll(&data) )
+ return;
+
+ // can't write the entire string at once, this risk overflowing the pipe
+ // and we would dead lock
+ size_t len = data.length();
+ const wxChar *pc = data.c_str();
+ while ( len )
+ {
+ const size_t CHUNK_SIZE = 4096;
+ m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
+
+ // note that not all data could have been written as we don't block on
+ // the write end of the pipe
+ const size_t lenChunk = m_out.LastWrite();
+
+ pc += lenChunk;
+ len -= lenChunk;
+
+ DoGet();
+ }
+#endif // wxUSE_FILEDLG
+}
+
+void MyPipeFrame::DoGet()
+{
+ // we don't have any way to be notified when any input appears on the
+ // stream so we have to poll it :-(
+ DoGetFromStream(m_textIn, m_in);
+ DoGetFromStream(m_textErr, m_err);
+}
+
+void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
+{
+ while ( in.CanRead() )
+ {
+ char buffer[4096];
+ buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
+
+ text->AppendText(buffer);
+ }
+}
+
+void MyPipeFrame::DoClose()
+{
+ m_process->CloseOutput();
+
+ DisableInput();
+}
+
+void MyPipeFrame::DisableInput()
+{
+ m_textOut->SetEditable(false);
+ FindWindow(Exec_Btn_Send)->Disable();
+ FindWindow(Exec_Btn_SendFile)->Disable();
+ FindWindow(Exec_Btn_Close)->Disable();
+}
+
+void MyPipeFrame::DisableOutput()
+{
+ FindWindow(Exec_Btn_Get)->Disable();
+}
+
+void MyPipeFrame::OnClose(wxCloseEvent& event)
+{
+ if ( m_process )
+ {
+ // we're not interested in getting the process termination notification
+ // if we are closing it ourselves
+ wxProcess *process = m_process;
+ m_process = NULL;
+ process->SetNextHandler(NULL);
+
+ process->CloseOutput();
+ }
+
+ event.Skip();
+}
+
+void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
+{
+ DoGet();
+
+ wxDELETE(m_process);
+
+ wxLogWarning(wxT("The other process has terminated, closing"));