Add support for specifying child process cwd and env to wxExecute().
[wxWidgets.git] / samples / exec / exec.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: exec.cpp
3 // Purpose: exec sample demonstrates wxExecute and related functions
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 15.01.00
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 // for all others, include the necessary headers (this file is usually all you
28 // need because it includes almost all "standard" wxWidgets headers
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/log.h"
32 #include "wx/frame.h"
33 #include "wx/panel.h"
34
35 #include "wx/timer.h"
36
37 #include "wx/utils.h"
38 #include "wx/menu.h"
39
40 #include "wx/msgdlg.h"
41 #include "wx/textdlg.h"
42 #include "wx/filedlg.h"
43 #include "wx/choicdlg.h"
44
45 #include "wx/button.h"
46 #include "wx/checkbox.h"
47 #include "wx/stattext.h"
48 #include "wx/textctrl.h"
49 #include "wx/listbox.h"
50
51 #include "wx/sizer.h"
52 #endif
53
54 #include "wx/txtstrm.h"
55 #include "wx/numdlg.h"
56 #include "wx/textdlg.h"
57 #include "wx/ffile.h"
58 #include "wx/stopwatch.h"
59
60 #include "wx/process.h"
61
62 #include "wx/mimetype.h"
63
64 #ifdef __WINDOWS__
65 #include "wx/dde.h"
66 #endif // __WINDOWS__
67
68 // ----------------------------------------------------------------------------
69 // the usual application and main frame classes
70 // ----------------------------------------------------------------------------
71
72 // Define a new application type, each program should derive a class from wxApp
73 class MyApp : public wxApp
74 {
75 public:
76 // override base class virtuals
77 // ----------------------------
78
79 // this one is called on application startup and is a good place for the app
80 // initialization (doing it here and not in the ctor allows to have an error
81 // return: if OnInit() returns false, the application terminates)
82 virtual bool OnInit();
83 };
84
85 // Define an array of process pointers used by MyFrame
86 class MyPipedProcess;
87 WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyPipedProcessesArray);
88
89 class MyProcess;
90 WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
91
92 // Define a new frame type: this is going to be our main frame
93 class MyFrame : public wxFrame
94 {
95 public:
96 // ctor and dtor
97 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
98 virtual ~MyFrame();
99
100 // event handlers (these functions should _not_ be virtual)
101 void OnQuit(wxCommandEvent& event);
102
103 void OnKill(wxCommandEvent& event);
104
105 void OnClear(wxCommandEvent& event);
106
107 void OnBeginBusyCursor(wxCommandEvent& event);
108 void OnEndBusyCursor(wxCommandEvent& event);
109
110 void OnSyncExec(wxCommandEvent& event);
111 void OnSyncNoEventsExec(wxCommandEvent& event);
112 void OnAsyncExec(wxCommandEvent& event);
113 void OnShell(wxCommandEvent& event);
114 void OnExecWithRedirect(wxCommandEvent& event);
115 void OnExecWithPipe(wxCommandEvent& event);
116
117 void OnPOpen(wxCommandEvent& event);
118
119 void OnFileExec(wxCommandEvent& event);
120 void OnFileLaunch(wxCommandEvent& event);
121 void OnOpenURL(wxCommandEvent& event);
122
123 void OnAbout(wxCommandEvent& event);
124
125 // polling output of async processes
126 void OnIdleTimer(wxTimerEvent& event);
127 void OnIdle(wxIdleEvent& event);
128
129 // for MyPipedProcess
130 void OnProcessTerminated(MyPipedProcess *process);
131 wxListBox *GetLogListBox() const { return m_lbox; }
132
133 // for MyProcess
134 void OnAsyncTermination(MyProcess *process);
135
136 // timer updating a counter in the background
137 void OnBgTimer(wxTimerEvent& event);
138
139 private:
140 void ShowOutput(const wxString& cmd,
141 const wxArrayString& output,
142 const wxString& title);
143
144 void DoAsyncExec(const wxString& cmd);
145
146 void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
147
148 void AddPipedProcess(MyPipedProcess *process);
149 void RemovePipedProcess(MyPipedProcess *process);
150
151
152 // the PID of the last process we launched asynchronously
153 long m_pidLast;
154
155 // last command we executed
156 wxString m_cmdLast;
157
158 #ifdef __WINDOWS__
159 void OnDDEExec(wxCommandEvent& event);
160 void OnDDERequest(wxCommandEvent& event);
161
162 bool GetDDEServer();
163
164 // last params of a DDE transaction
165 wxString m_server,
166 m_topic,
167 m_cmdDde;
168 #endif // __WINDOWS__
169
170 wxListBox *m_lbox;
171
172 // array of running processes with redirected IO
173 MyPipedProcessesArray m_running;
174
175 // array of all asynchrously running processes
176 MyProcessesArray m_allAsync;
177
178 // the idle event wake up timer
179 wxTimer m_timerIdleWakeUp;
180
181 // a background timer allowing to easily check visually whether the
182 // messages are processed or not
183 wxTimer m_timerBg;
184
185 // any class wishing to process wxWidgets events must use this macro
186 DECLARE_EVENT_TABLE()
187 };
188
189 // ----------------------------------------------------------------------------
190 // MyPipeFrame: allows the user to communicate with the child process
191 // ----------------------------------------------------------------------------
192
193 class MyPipeFrame : public wxFrame
194 {
195 public:
196 MyPipeFrame(wxFrame *parent,
197 const wxString& cmd,
198 wxProcess *process);
199
200 protected:
201 void OnTextEnter(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
202 void OnBtnSend(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
203 void OnBtnSendFile(wxCommandEvent& WXUNUSED(event));
204 void OnBtnGet(wxCommandEvent& WXUNUSED(event)) { DoGet(); }
205 void OnBtnClose(wxCommandEvent& WXUNUSED(event)) { DoClose(); }
206
207 void OnClose(wxCloseEvent& event);
208
209 void OnProcessTerm(wxProcessEvent& event);
210
211 void DoSend()
212 {
213 wxString s(m_textOut->GetValue());
214 s += wxT('\n');
215 m_out.Write(s.c_str(), s.length());
216 m_textOut->Clear();
217
218 DoGet();
219 }
220
221 void DoGet();
222 void DoClose();
223
224 private:
225 void DoGetFromStream(wxTextCtrl *text, wxInputStream& in);
226 void DisableInput();
227 void DisableOutput();
228
229
230 wxProcess *m_process;
231
232 wxOutputStream &m_out;
233 wxInputStream &m_in,
234 &m_err;
235
236 wxTextCtrl *m_textOut,
237 *m_textIn,
238 *m_textErr;
239
240 DECLARE_EVENT_TABLE()
241 };
242
243 // ----------------------------------------------------------------------------
244 // wxProcess-derived classes
245 // ----------------------------------------------------------------------------
246
247 // This is the handler for process termination events
248 class MyProcess : public wxProcess
249 {
250 public:
251 MyProcess(MyFrame *parent, const wxString& cmd)
252 : wxProcess(parent), m_cmd(cmd)
253 {
254 m_parent = parent;
255 }
256
257 // instead of overriding this virtual function we might as well process the
258 // event from it in the frame class - this might be more convenient in some
259 // cases
260 virtual void OnTerminate(int pid, int status);
261
262 protected:
263 MyFrame *m_parent;
264 wxString m_cmd;
265 };
266
267 // A specialization of MyProcess for redirecting the output
268 class MyPipedProcess : public MyProcess
269 {
270 public:
271 MyPipedProcess(MyFrame *parent, const wxString& cmd)
272 : MyProcess(parent, cmd)
273 {
274 Redirect();
275 }
276
277 virtual void OnTerminate(int pid, int status);
278
279 virtual bool HasInput();
280 };
281
282 // A version of MyPipedProcess which also sends input to the stdin of the
283 // child process
284 class MyPipedProcess2 : public MyPipedProcess
285 {
286 public:
287 MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
288 : MyPipedProcess(parent, cmd), m_input(input)
289 {
290 }
291
292 virtual bool HasInput();
293
294 private:
295 wxString m_input;
296 };
297
298 // ----------------------------------------------------------------------------
299 // constants
300 // ----------------------------------------------------------------------------
301
302 // IDs for the controls and the menu commands
303 enum
304 {
305 // timer ids
306 Exec_TimerIdle = 10,
307 Exec_TimerBg,
308
309 // menu items
310 Exec_Quit = 100,
311 Exec_Kill,
312 Exec_ClearLog,
313 Exec_BeginBusyCursor,
314 Exec_EndBusyCursor,
315 Exec_SyncExec = 200,
316 Exec_SyncNoEventsExec,
317 Exec_AsyncExec,
318 Exec_Shell,
319 Exec_POpen,
320 Exec_OpenFile,
321 Exec_LaunchFile,
322 Exec_OpenURL,
323 Exec_DDEExec,
324 Exec_DDERequest,
325 Exec_Redirect,
326 Exec_Pipe,
327 Exec_About = wxID_ABOUT,
328
329 // control ids
330 Exec_Btn_Send = 1000,
331 Exec_Btn_SendFile,
332 Exec_Btn_Get,
333 Exec_Btn_Close
334 };
335
336 static const wxChar *DIALOG_TITLE = wxT("Exec sample");
337
338 // ----------------------------------------------------------------------------
339 // event tables and other macros for wxWidgets
340 // ----------------------------------------------------------------------------
341
342 // the event tables connect the wxWidgets events with the functions (event
343 // handlers) which process them. It can be also done at run-time, but for the
344 // simple menu events like this the static method is much simpler.
345 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
346 EVT_MENU(Exec_Quit, MyFrame::OnQuit)
347 EVT_MENU(Exec_Kill, MyFrame::OnKill)
348 EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
349 EVT_MENU(Exec_BeginBusyCursor, MyFrame::OnBeginBusyCursor)
350 EVT_MENU(Exec_EndBusyCursor, MyFrame::OnEndBusyCursor)
351
352 EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
353 EVT_MENU(Exec_SyncNoEventsExec, MyFrame::OnSyncNoEventsExec)
354 EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
355 EVT_MENU(Exec_Shell, MyFrame::OnShell)
356 EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
357 EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
358
359 EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
360
361 EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
362 EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch)
363 EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL)
364
365 #ifdef __WINDOWS__
366 EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
367 EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
368 #endif // __WINDOWS__
369
370 EVT_MENU(Exec_About, MyFrame::OnAbout)
371
372 EVT_IDLE(MyFrame::OnIdle)
373
374 EVT_TIMER(Exec_TimerIdle, MyFrame::OnIdleTimer)
375 EVT_TIMER(Exec_TimerBg, MyFrame::OnBgTimer)
376 END_EVENT_TABLE()
377
378 BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
379 EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend)
380 EVT_BUTTON(Exec_Btn_SendFile, MyPipeFrame::OnBtnSendFile)
381 EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet)
382 EVT_BUTTON(Exec_Btn_Close, MyPipeFrame::OnBtnClose)
383
384 EVT_TEXT_ENTER(wxID_ANY, MyPipeFrame::OnTextEnter)
385
386 EVT_CLOSE(MyPipeFrame::OnClose)
387
388 EVT_END_PROCESS(wxID_ANY, MyPipeFrame::OnProcessTerm)
389 END_EVENT_TABLE()
390
391 // Create a new application object: this macro will allow wxWidgets to create
392 // the application object during program execution (it's better than using a
393 // static object for many reasons) and also declares the accessor function
394 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
395 // not wxApp)
396 IMPLEMENT_APP(MyApp)
397
398 // ============================================================================
399 // implementation
400 // ============================================================================
401
402 // ----------------------------------------------------------------------------
403 // the application class
404 // ----------------------------------------------------------------------------
405
406 // `Main program' equivalent: the program execution "starts" here
407 bool MyApp::OnInit()
408 {
409 if ( !wxApp::OnInit() )
410 return false;
411
412 // Create the main application window
413 MyFrame *frame = new MyFrame(wxT("Exec wxWidgets sample"),
414 wxDefaultPosition, wxSize(500, 140));
415
416 // Show it and tell the application that it's our main window
417 frame->Show(true);
418 SetTopWindow(frame);
419
420 // success: wxApp::OnRun() will be called which will enter the main message
421 // loop and the application will run. If we returned false here, the
422 // application would exit immediately.
423 return true;
424 }
425
426 // ----------------------------------------------------------------------------
427 // main frame
428 // ----------------------------------------------------------------------------
429
430 #ifdef __VISUALC__
431 #pragma warning(disable: 4355) // this used in base member initializer list
432 #endif
433
434 // frame constructor
435 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
436 : wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
437 m_timerIdleWakeUp(this, Exec_TimerIdle),
438 m_timerBg(this, Exec_TimerBg)
439 {
440 m_pidLast = 0;
441
442 #ifdef __WXMAC__
443 // we need this in order to allow the about menu relocation, since ABOUT is
444 // not the default id of the about menu
445 wxApp::s_macAboutMenuItemId = Exec_About;
446 #endif
447
448 // create a menu bar
449 wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
450 menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
451 wxT("Kill a process by PID"));
452 menuFile->AppendSeparator();
453 menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
454 wxT("Clear the log window"));
455 menuFile->AppendSeparator();
456 menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
457 menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
458 menuFile->AppendSeparator();
459 menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
460
461 wxMenu *execMenu = new wxMenu;
462 execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
463 wxT("Launch a program and return when it terminates"));
464 execMenu->Append(Exec_SyncNoEventsExec, wxT("Sync execution and &block...\tCtrl-B"),
465 wxT("Launch a program and block until it terminates"));
466 execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
467 wxT("Launch a program and return immediately"));
468 execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
469 wxT("Launch a shell and execute a command in it"));
470 execMenu->AppendSeparator();
471 execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
472 wxT("Launch a program and capture its output"));
473 execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
474 wxT("Pipe a string through a filter"));
475 execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
476 wxT("Open a pipe to and from another program"));
477
478 execMenu->AppendSeparator();
479 execMenu->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
480 wxT("Launch the command to open this kind of files"));
481 execMenu->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
482 wxT("Launch the default application associated with the file"));
483 execMenu->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
484 wxT("Launch the default browser with the given URL"));
485 #ifdef __WINDOWS__
486 execMenu->AppendSeparator();
487 execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
488 execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
489 #endif
490
491 wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
492 helpMenu->Append(Exec_About, wxT("&About...\tF1"), wxT("Show about dialog"));
493
494 // now append the freshly created menu to the menu bar...
495 wxMenuBar *menuBar = new wxMenuBar();
496 menuBar->Append(menuFile, wxT("&File"));
497 menuBar->Append(execMenu, wxT("&Exec"));
498 menuBar->Append(helpMenu, wxT("&Help"));
499
500 // ... and attach this menu bar to the frame
501 SetMenuBar(menuBar);
502
503 // create the listbox in which we will show misc messages as they come
504 m_lbox = new wxListBox(this, wxID_ANY);
505 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
506 wxFONTWEIGHT_NORMAL);
507 if ( font.Ok() )
508 m_lbox->SetFont(font);
509
510 #if wxUSE_STATUSBAR
511 // create a status bar just for fun (by default with 1 pane only)
512 CreateStatusBar(2);
513 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
514 #endif // wxUSE_STATUSBAR
515
516 m_timerBg.Start(1000);
517 }
518
519 MyFrame::~MyFrame()
520 {
521 // any processes left until now must be deleted manually: normally this is
522 // done when the associated process terminates but it must be still running
523 // if this didn't happen until now
524 for ( size_t n = 0; n < m_allAsync.size(); n++ )
525 {
526 delete m_allAsync[n];
527 }
528 }
529
530 // ----------------------------------------------------------------------------
531 // event handlers: file and help menu
532 // ----------------------------------------------------------------------------
533
534 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
535 {
536 // true is to force the frame to close
537 Close(true);
538 }
539
540 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
541 {
542 m_lbox->Clear();
543 }
544
545 void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
546 {
547 wxBeginBusyCursor();
548 }
549
550 void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
551 {
552 wxEndBusyCursor();
553 }
554
555 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
556 {
557 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
558 wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
559 }
560
561 void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
562 {
563 long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
564 wxT("Enter PID:"),
565 wxT("Exec question"),
566 m_pidLast,
567 // we need the full unsigned int range
568 -INT_MAX, INT_MAX,
569 this);
570 if ( pid == -1 )
571 {
572 // cancelled
573 return;
574 }
575
576 m_pidLast = pid;
577
578 static const wxString signalNames[] =
579 {
580 wxT("Just test (SIGNONE)"),
581 wxT("Hangup (SIGHUP)"),
582 wxT("Interrupt (SIGINT)"),
583 wxT("Quit (SIGQUIT)"),
584 wxT("Illegal instruction (SIGILL)"),
585 wxT("Trap (SIGTRAP)"),
586 wxT("Abort (SIGABRT)"),
587 wxT("Emulated trap (SIGEMT)"),
588 wxT("FP exception (SIGFPE)"),
589 wxT("Kill (SIGKILL)"),
590 wxT("Bus (SIGBUS)"),
591 wxT("Segment violation (SIGSEGV)"),
592 wxT("System (SIGSYS)"),
593 wxT("Broken pipe (SIGPIPE)"),
594 wxT("Alarm (SIGALRM)"),
595 wxT("Terminate (SIGTERM)"),
596 };
597
598 static int s_sigLast = wxSIGNONE;
599 int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
600 wxT("Exec question"),
601 WXSIZEOF(signalNames), signalNames,
602 s_sigLast,
603 this);
604 switch ( sig )
605 {
606 default:
607 wxFAIL_MSG( wxT("unexpected return value") );
608 // fall through
609
610 case -1:
611 // cancelled
612 return;
613
614 case wxSIGNONE:
615 case wxSIGHUP:
616 case wxSIGINT:
617 case wxSIGQUIT:
618 case wxSIGILL:
619 case wxSIGTRAP:
620 case wxSIGABRT:
621 case wxSIGEMT:
622 case wxSIGFPE:
623 case wxSIGKILL:
624 case wxSIGBUS:
625 case wxSIGSEGV:
626 case wxSIGSYS:
627 case wxSIGPIPE:
628 case wxSIGALRM:
629 case wxSIGTERM:
630 break;
631 }
632
633 s_sigLast = sig;
634
635 if ( sig == wxSIGNONE )
636 {
637 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
638 if ( wxProcess::Exists(pid) )
639 {
640 wxLogStatus(wxT("Process %ld is running."), pid);
641 }
642 else
643 {
644 wxLogStatus(wxT("No process with pid = %ld."), pid);
645 }
646 }
647 else // not SIGNONE
648 {
649 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
650 if ( rc == wxKILL_OK )
651 {
652 wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
653 }
654 else
655 {
656 static const wxChar *errorText[] =
657 {
658 wxT(""), // no error
659 wxT("signal not supported"),
660 wxT("permission denied"),
661 wxT("no such process"),
662 wxT("unspecified error"),
663 };
664
665 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
666 pid, sig, errorText[rc]);
667 }
668 }
669 }
670
671 // ----------------------------------------------------------------------------
672 // execution options dialog
673 // ----------------------------------------------------------------------------
674
675 enum ExecQueryDialogID
676 {
677 TEXT_EXECUTABLE,
678 TEXT_CWD,
679 TEXT_ENVIRONMENT
680 };
681
682 class ExecQueryDialog : public wxDialog
683 {
684 public:
685 ExecQueryDialog(const wxString& cmd);
686
687 wxString GetExecutable() const
688 {
689 return m_executable->GetValue();
690 }
691
692 wxString GetWorkDir() const
693 {
694 return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString();
695 }
696
697 void GetEnvironment(wxEnvVariableHashMap& env);
698
699 private:
700 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event)
701 {
702 event.Enable(m_useCWD->GetValue());
703 }
704
705 void OnUpdateEnvironmentUI(wxUpdateUIEvent& event)
706 {
707 event.Enable(m_useEnv->GetValue());
708 }
709
710 wxTextCtrl* m_executable;
711 wxTextCtrl* m_cwdtext;
712 wxTextCtrl* m_envtext;
713 wxCheckBox* m_useCWD;
714 wxCheckBox* m_useEnv;
715
716 DECLARE_EVENT_TABLE()
717 };
718
719 BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog)
720 EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI)
721 EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI)
722 END_EVENT_TABLE()
723
724 ExecQueryDialog::ExecQueryDialog(const wxString& cmd)
725 : wxDialog(NULL, wxID_ANY, DIALOG_TITLE,
726 wxDefaultPosition, wxDefaultSize,
727 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
728 {
729 wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL);
730
731 m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString());
732 m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString());
733 m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(),
734 wxDefaultPosition, wxSize(300, 200),
735 wxTE_MULTILINE|wxHSCROLL);
736
737 const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border();
738 globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "),
739 flagsExpand);
740 globalSizer->Add(m_executable, flagsExpand);
741
742 m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: ");
743 globalSizer->Add(m_useCWD, flagsExpand);
744 globalSizer->Add(m_cwdtext, flagsExpand);
745
746 m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: ");
747 globalSizer->Add(m_useEnv, flagsExpand);
748 globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1));
749
750 globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand);
751 SetSizerAndFit(globalSizer);
752
753
754 m_executable->SetValue(cmd);
755 m_cwdtext->SetValue(wxGetCwd());
756 wxEnvVariableHashMap env;
757 if ( wxGetEnvMap(&env) )
758 {
759 for ( wxEnvVariableHashMap::iterator it = env.begin();
760 it != env.end();
761 ++it )
762 {
763 m_envtext->AppendText(it->first + '=' + it->second + '\n');
764 }
765 }
766 m_useCWD->SetValue(false);
767 m_useEnv->SetValue(false);
768 }
769
770 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env)
771 {
772 env.clear();
773 if ( m_useEnv->GetValue() )
774 {
775 wxString name,
776 value;
777
778 const int nb = m_envtext->GetNumberOfLines();
779 for ( int l = 0; l < nb; l++ )
780 {
781 const wxString line = m_envtext->GetLineText(l).Trim();
782
783 if ( !line.empty() )
784 {
785 name = line.BeforeFirst('=', &value);
786 if ( name.empty() )
787 {
788 wxLogWarning("Skipping invalid environment line \"%s\".", line);
789 continue;
790 }
791
792 env[name] = value;
793 }
794 }
795 }
796 }
797
798 static bool QueryExec(wxString& cmd, wxExecuteEnv& env)
799 {
800 ExecQueryDialog dialog(cmd);
801
802 if ( dialog.ShowModal() != wxID_OK )
803 return false;
804
805 cmd = dialog.GetExecutable();
806 env.cwd = dialog.GetWorkDir();
807 dialog.GetEnvironment(env.env);
808
809 return true;
810 }
811
812 // ----------------------------------------------------------------------------
813 // event handlers: exec menu
814 // ----------------------------------------------------------------------------
815
816 void MyFrame::DoAsyncExec(const wxString& cmd)
817 {
818 MyProcess * const process = new MyProcess(this, cmd);
819 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
820 if ( !m_pidLast )
821 {
822 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
823
824 delete process;
825 }
826 else
827 {
828 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
829
830 m_cmdLast = cmd;
831
832 // the parent frame keeps track of all async processes as it needs to
833 // free them if we exit before the child process terminates
834 AddAsyncProcess(process);
835 }
836 }
837
838 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
839 {
840 wxString cmd;
841 wxExecuteEnv env;
842 if ( !QueryExec(cmd, env) )
843 return;
844
845 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
846
847 int code = wxExecute(cmd, wxEXEC_SYNC, NULL, &env);
848
849 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
850 cmd.c_str(), code);
851
852 m_cmdLast = cmd;
853 }
854
855 void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event))
856 {
857 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
858 DIALOG_TITLE,
859 m_cmdLast);
860
861 if ( !cmd )
862 return;
863
864 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
865
866 int code = wxExecute(cmd, wxEXEC_BLOCK);
867
868 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
869 cmd.c_str(), code);
870
871 m_cmdLast = cmd;
872 }
873
874 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
875 {
876 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
877 DIALOG_TITLE,
878 m_cmdLast);
879
880 if ( !cmd )
881 return;
882
883 DoAsyncExec(cmd);
884 }
885
886 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
887 {
888 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
889 DIALOG_TITLE,
890 m_cmdLast);
891
892 if ( !cmd )
893 return;
894
895 int code = wxShell(cmd);
896 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
897 cmd.c_str(), code);
898 m_cmdLast = cmd;
899 }
900
901 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
902 {
903 if ( !m_cmdLast )
904 {
905 #ifdef __WXMSW__
906 m_cmdLast = "type Makefile.in";
907 #else
908 m_cmdLast = "cat -n Makefile";
909 #endif
910 }
911
912 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
913 DIALOG_TITLE,
914 m_cmdLast);
915
916 if ( !cmd )
917 return;
918
919 bool sync;
920 switch ( wxMessageBox(wxT("Execute it synchronously?"),
921 wxT("Exec question"),
922 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
923 {
924 case wxYES:
925 sync = true;
926 break;
927
928 case wxNO:
929 sync = false;
930 break;
931
932 default:
933 return;
934 }
935
936 if ( sync )
937 {
938 wxLogStatus("\"%s\" is running please wait...", cmd);
939
940 wxStopWatch sw;
941
942 wxArrayString output, errors;
943 int code = wxExecute(cmd, output, errors);
944
945 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
946 cmd, sw.Time(), code);
947
948 if ( code != -1 )
949 {
950 ShowOutput(cmd, output, wxT("Output"));
951 ShowOutput(cmd, errors, wxT("Errors"));
952 }
953 }
954 else // async exec
955 {
956 MyPipedProcess *process = new MyPipedProcess(this, cmd);
957 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
958 {
959 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
960
961 delete process;
962 }
963 else
964 {
965 AddPipedProcess(process);
966 }
967 }
968
969 m_cmdLast = cmd;
970 }
971
972 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
973 {
974 if ( !m_cmdLast )
975 m_cmdLast = wxT("tr [a-z] [A-Z]");
976
977 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
978 DIALOG_TITLE,
979 m_cmdLast);
980
981 if ( !cmd )
982 return;
983
984 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
985 DIALOG_TITLE);
986 if ( !input )
987 return;
988
989 // always execute the filter asynchronously
990 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
991 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
992 if ( pid )
993 {
994 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
995
996 AddPipedProcess(process);
997 }
998 else
999 {
1000 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
1001
1002 delete process;
1003 }
1004
1005 m_cmdLast = cmd;
1006 }
1007
1008 void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
1009 {
1010 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
1011 DIALOG_TITLE,
1012 m_cmdLast);
1013 if ( cmd.empty() )
1014 return;
1015
1016 wxProcess *process = wxProcess::Open(cmd);
1017 if ( !process )
1018 {
1019 wxLogError(wxT("Failed to launch the command."));
1020 return;
1021 }
1022
1023 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
1024
1025 wxOutputStream *out = process->GetOutputStream();
1026 if ( !out )
1027 {
1028 wxLogError(wxT("Failed to connect to child stdin"));
1029 return;
1030 }
1031
1032 wxInputStream *in = process->GetInputStream();
1033 if ( !in )
1034 {
1035 wxLogError(wxT("Failed to connect to child stdout"));
1036 return;
1037 }
1038
1039 new MyPipeFrame(this, cmd, process);
1040 }
1041
1042 static wxString gs_lastFile;
1043
1044 static bool AskUserForFileName()
1045 {
1046 wxString filename;
1047
1048 #if wxUSE_FILEDLG
1049 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
1050 #else // !wxUSE_FILEDLG
1051 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1052 gs_lastFile);
1053 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1054
1055 if ( filename.empty() )
1056 return false;
1057
1058 gs_lastFile = filename;
1059
1060 return true;
1061 }
1062
1063 void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
1064 {
1065 if ( !AskUserForFileName() )
1066 return;
1067
1068 wxString ext = gs_lastFile.AfterLast(wxT('.'));
1069 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
1070 if ( !ft )
1071 {
1072 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1073 ext.c_str());
1074 return;
1075 }
1076
1077 wxString cmd;
1078 bool ok = ft->GetOpenCommand(&cmd,
1079 wxFileType::MessageParameters(gs_lastFile));
1080 delete ft;
1081 if ( !ok )
1082 {
1083 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1084 ext.c_str());
1085 return;
1086 }
1087
1088 DoAsyncExec(cmd);
1089 }
1090
1091 void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
1092 {
1093 if ( !AskUserForFileName() )
1094 return;
1095
1096 if ( !wxLaunchDefaultApplication(gs_lastFile) )
1097 {
1098 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
1099 }
1100 }
1101
1102 void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
1103 {
1104 static wxString s_url(wxT("http://www.wxwidgets.org/"));
1105
1106 wxString filename = wxGetTextFromUser
1107 (
1108 wxT("Enter the URL"),
1109 wxT("exec sample"),
1110 s_url,
1111 this
1112 );
1113
1114 if ( filename.empty() )
1115 return;
1116
1117 s_url = filename;
1118
1119 if ( !wxLaunchDefaultBrowser(s_url) )
1120 {
1121 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
1122 }
1123 }
1124
1125 // ----------------------------------------------------------------------------
1126 // DDE stuff
1127 // ----------------------------------------------------------------------------
1128
1129 #ifdef __WINDOWS__
1130
1131 bool MyFrame::GetDDEServer()
1132 {
1133 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
1134 DIALOG_TITLE, m_server);
1135 if ( !server )
1136 return false;
1137
1138 m_server = server;
1139
1140 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
1141 if ( !topic )
1142 return false;
1143
1144 m_topic = topic;
1145
1146 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
1147 if ( !cmd )
1148 return false;
1149
1150 m_cmdDde = cmd;
1151
1152 return true;
1153 }
1154
1155 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1156 {
1157 if ( !GetDDEServer() )
1158 return;
1159
1160 wxDDEClient client;
1161 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1162 if ( !conn )
1163 {
1164 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1165 m_server.c_str());
1166 }
1167 else
1168 {
1169 if ( !conn->Execute(m_cmdDde) )
1170 {
1171 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1172 m_cmdDde.c_str());
1173 }
1174 else
1175 {
1176 wxLogStatus(wxT("Successfully executed DDE command"));
1177 }
1178 }
1179 }
1180
1181 void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1182 {
1183 if ( !GetDDEServer() )
1184 return;
1185
1186 wxDDEClient client;
1187 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1188 if ( !conn )
1189 {
1190 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1191 m_server.c_str());
1192 }
1193 else
1194 {
1195 if ( !conn->Request(m_cmdDde) )
1196 {
1197 wxLogError(wxT("Failed to send request '%s' via DDE."),
1198 m_cmdDde.c_str());
1199 }
1200 else
1201 {
1202 wxLogStatus(wxT("Successfully sent DDE request."));
1203 }
1204 }
1205 }
1206
1207 #endif // __WINDOWS__
1208
1209 // ----------------------------------------------------------------------------
1210 // various helpers
1211 // ----------------------------------------------------------------------------
1212
1213 // input polling
1214 void MyFrame::OnIdle(wxIdleEvent& event)
1215 {
1216 size_t count = m_running.GetCount();
1217 for ( size_t n = 0; n < count; n++ )
1218 {
1219 if ( m_running[n]->HasInput() )
1220 {
1221 event.RequestMore();
1222 }
1223 }
1224 }
1225
1226 void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
1227 {
1228 wxWakeUpIdle();
1229 }
1230
1231 void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1232 {
1233 static unsigned long s_ticks = 0;
1234 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1235 }
1236
1237 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1238 {
1239 RemovePipedProcess(process);
1240 }
1241
1242 void MyFrame::OnAsyncTermination(MyProcess *process)
1243 {
1244 m_allAsync.Remove(process);
1245
1246 delete process;
1247 }
1248
1249 void MyFrame::AddPipedProcess(MyPipedProcess *process)
1250 {
1251 if ( m_running.IsEmpty() )
1252 {
1253 // we want to start getting the timer events to ensure that a
1254 // steady stream of idle events comes in -- otherwise we
1255 // wouldn't be able to poll the child process input
1256 m_timerIdleWakeUp.Start(100);
1257 }
1258 //else: the timer is already running
1259
1260 m_running.Add(process);
1261 m_allAsync.Add(process);
1262 }
1263
1264 void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1265 {
1266 m_running.Remove(process);
1267
1268 if ( m_running.IsEmpty() )
1269 {
1270 // we don't need to get idle events all the time any more
1271 m_timerIdleWakeUp.Stop();
1272 }
1273 }
1274
1275 void MyFrame::ShowOutput(const wxString& cmd,
1276 const wxArrayString& output,
1277 const wxString& title)
1278 {
1279 size_t count = output.GetCount();
1280 if ( !count )
1281 return;
1282
1283 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1284 title.c_str(), cmd.c_str()));
1285
1286 for ( size_t n = 0; n < count; n++ )
1287 {
1288 m_lbox->Append(output[n]);
1289 }
1290
1291 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
1292 title.Lower().c_str()));
1293 }
1294
1295 // ----------------------------------------------------------------------------
1296 // MyProcess
1297 // ----------------------------------------------------------------------------
1298
1299 void MyProcess::OnTerminate(int pid, int status)
1300 {
1301 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
1302 pid, m_cmd.c_str(), status);
1303
1304 m_parent->OnAsyncTermination(this);
1305 }
1306
1307 // ----------------------------------------------------------------------------
1308 // MyPipedProcess
1309 // ----------------------------------------------------------------------------
1310
1311 bool MyPipedProcess::HasInput()
1312 {
1313 bool hasInput = false;
1314
1315 if ( IsInputAvailable() )
1316 {
1317 wxTextInputStream tis(*GetInputStream());
1318
1319 // this assumes that the output is always line buffered
1320 wxString msg;
1321 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
1322
1323 m_parent->GetLogListBox()->Append(msg);
1324
1325 hasInput = true;
1326 }
1327
1328 if ( IsErrorAvailable() )
1329 {
1330 wxTextInputStream tis(*GetErrorStream());
1331
1332 // this assumes that the output is always line buffered
1333 wxString msg;
1334 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
1335
1336 m_parent->GetLogListBox()->Append(msg);
1337
1338 hasInput = true;
1339 }
1340
1341 return hasInput;
1342 }
1343
1344 void MyPipedProcess::OnTerminate(int pid, int status)
1345 {
1346 // show the rest of the output
1347 while ( HasInput() )
1348 ;
1349
1350 m_parent->OnProcessTerminated(this);
1351
1352 MyProcess::OnTerminate(pid, status);
1353 }
1354
1355 // ----------------------------------------------------------------------------
1356 // MyPipedProcess2
1357 // ----------------------------------------------------------------------------
1358
1359 bool MyPipedProcess2::HasInput()
1360 {
1361 if ( !m_input.empty() )
1362 {
1363 wxTextOutputStream os(*GetOutputStream());
1364 os.WriteString(m_input);
1365
1366 CloseOutput();
1367 m_input.clear();
1368
1369 // call us once again - may be we'll have output
1370 return true;
1371 }
1372
1373 return MyPipedProcess::HasInput();
1374 }
1375
1376 // ============================================================================
1377 // MyPipeFrame implementation
1378 // ============================================================================
1379
1380 MyPipeFrame::MyPipeFrame(wxFrame *parent,
1381 const wxString& cmd,
1382 wxProcess *process)
1383 : wxFrame(parent, wxID_ANY, cmd),
1384 m_process(process),
1385 // in a real program we'd check that the streams are !NULL here
1386 m_out(*process->GetOutputStream()),
1387 m_in(*process->GetInputStream()),
1388 m_err(*process->GetErrorStream())
1389 {
1390 m_process->SetNextHandler(this);
1391
1392 wxPanel *panel = new wxPanel(this, wxID_ANY);
1393
1394 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1395 wxDefaultPosition, wxDefaultSize,
1396 wxTE_PROCESS_ENTER);
1397 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1398 wxDefaultPosition, wxDefaultSize,
1399 wxTE_MULTILINE | wxTE_RICH);
1400 m_textIn->SetEditable(false);
1401 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1402 wxDefaultPosition, wxDefaultSize,
1403 wxTE_MULTILINE | wxTE_RICH);
1404 m_textErr->SetEditable(false);
1405
1406 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1407 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1408
1409 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1410 sizerBtns->
1411 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1412 sizerBtns->
1413 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1414 sizerBtns->
1415 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1416 sizerBtns->
1417 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
1418
1419 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1420 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1421 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
1422
1423 panel->SetSizer(sizerTop);
1424 sizerTop->Fit(this);
1425
1426 Show();
1427 }
1428
1429 void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1430 {
1431 #if wxUSE_FILEDLG
1432 wxFileDialog filedlg(this, wxT("Select file to send"));
1433 if ( filedlg.ShowModal() != wxID_OK )
1434 return;
1435
1436 wxFFile file(filedlg.GetFilename(), wxT("r"));
1437 wxString data;
1438 if ( !file.IsOpened() || !file.ReadAll(&data) )
1439 return;
1440
1441 // can't write the entire string at once, this risk overflowing the pipe
1442 // and we would dead lock
1443 size_t len = data.length();
1444 const wxChar *pc = data.c_str();
1445 while ( len )
1446 {
1447 const size_t CHUNK_SIZE = 4096;
1448 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1449
1450 // note that not all data could have been written as we don't block on
1451 // the write end of the pipe
1452 const size_t lenChunk = m_out.LastWrite();
1453
1454 pc += lenChunk;
1455 len -= lenChunk;
1456
1457 DoGet();
1458 }
1459 #endif // wxUSE_FILEDLG
1460 }
1461
1462 void MyPipeFrame::DoGet()
1463 {
1464 // we don't have any way to be notified when any input appears on the
1465 // stream so we have to poll it :-(
1466 DoGetFromStream(m_textIn, m_in);
1467 DoGetFromStream(m_textErr, m_err);
1468 }
1469
1470 void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1471 {
1472 while ( in.CanRead() )
1473 {
1474 wxChar buffer[4096];
1475 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = wxT('\0');
1476
1477 text->AppendText(buffer);
1478 }
1479 }
1480
1481 void MyPipeFrame::DoClose()
1482 {
1483 m_process->CloseOutput();
1484
1485 DisableInput();
1486 }
1487
1488 void MyPipeFrame::DisableInput()
1489 {
1490 m_textOut->SetEditable(false);
1491 FindWindow(Exec_Btn_Send)->Disable();
1492 FindWindow(Exec_Btn_SendFile)->Disable();
1493 FindWindow(Exec_Btn_Close)->Disable();
1494 }
1495
1496 void MyPipeFrame::DisableOutput()
1497 {
1498 FindWindow(Exec_Btn_Get)->Disable();
1499 }
1500
1501 void MyPipeFrame::OnClose(wxCloseEvent& event)
1502 {
1503 if ( m_process )
1504 {
1505 // we're not interested in getting the process termination notification
1506 // if we are closing it ourselves
1507 wxProcess *process = m_process;
1508 m_process = NULL;
1509 process->SetNextHandler(NULL);
1510
1511 process->CloseOutput();
1512 }
1513
1514 event.Skip();
1515 }
1516
1517 void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
1518 {
1519 DoGet();
1520
1521 wxDELETE(m_process);
1522
1523 wxLogWarning(wxT("The other process has terminated, closing"));
1524
1525 DisableInput();
1526 DisableOutput();
1527 }